Compare commits
144 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6cc432bbc4 | ||
|
dafae9818d | ||
|
9fe1619db9 | ||
|
da70aca28e | ||
|
6f531d140b | ||
|
f66a786029 | ||
|
d97903a358 | ||
|
93897134ca | ||
|
a6b59cd1a3 | ||
|
f64af6473f | ||
|
ac49c7932d | ||
|
61dcb686a8 | ||
|
9381a7d9d5 | ||
|
a5c6c748e0 | ||
|
36b5703796 | ||
|
ff6b8a6443 | ||
|
6b76a6212d | ||
|
33ee347c99 | ||
|
0306e3e9be | ||
|
357f9298bd | ||
|
f7c46fc113 | ||
|
74c39fada0 | ||
|
f02411da40 | ||
|
a568e3ca8e | ||
|
3b440bd5af | ||
|
39f27b6cf3 | ||
|
721234230c | ||
|
566ace2d64 | ||
|
092f1df9d0 | ||
|
844616e950 | ||
|
40871caa4b | ||
|
cdf8b92fea | ||
|
0074cad44f | ||
|
4a0a19fe54 | ||
|
338bff8b93 | ||
|
b88fcd53f7 | ||
|
ca7e6a6d2e | ||
|
f0cd957c7a | ||
|
64fc8d2b07 | ||
|
fd385e256d | ||
|
03119c857b | ||
|
2ef1ce1182 | ||
|
eb2425b53b | ||
|
79d3a8553f | ||
|
7709556673 | ||
|
f0ae6b4cc5 | ||
|
9e3a6d6784 | ||
|
8bf3e750ab | ||
|
18241ccbe1 | ||
|
0dccb398bd | ||
|
386365090c | ||
|
d9500c8a3b | ||
|
f7c1668bf6 | ||
|
051b649628 | ||
|
f5f165a5eb | ||
|
f89ff65ec7 | ||
|
48b940d5c6 | ||
|
6ae70a92c9 | ||
|
fa5c867e0e | ||
|
641abe2db7 | ||
|
4f7f6b3922 | ||
|
8b14726f5b | ||
|
9090b63831 | ||
|
ab27dccba5 | ||
|
56eb5c3f34 | ||
|
56333cca88 | ||
|
1aaec701bb | ||
|
cd252b794e | ||
|
b6003afcdb | ||
|
f5ee2d469b | ||
|
37b043d447 | ||
|
36579bac88 | ||
|
4476a45444 | ||
|
58a4633707 | ||
|
494969d394 | ||
|
c1a41181c5 | ||
|
6e309f5e45 | ||
|
e5f18ace2a | ||
|
11697d6894 | ||
|
675b8fea53 | ||
|
4c16ddf588 | ||
|
5ba4c36f95 | ||
|
ccd53e983c | ||
|
ff44b2e92d | ||
|
188aa3ea50 | ||
|
bd077ad7d9 | ||
|
a29d409e20 | ||
|
5acd5315f2 | ||
|
b79ab15859 | ||
|
77406d3a09 | ||
|
510c9049c7 | ||
|
ed902581d3 | ||
|
64db9ed5f6 | ||
|
1085ef3836 | ||
|
86a9de6753 | ||
|
83c982b458 | ||
|
9aba44ea79 | ||
|
6dcf96271e | ||
|
4ca60c665e | ||
|
b170627ceb | ||
|
a1b065700a | ||
|
8de048fcdb | ||
|
cfa9b6e13a | ||
|
e26d5ca923 | ||
|
dd9d00d293 | ||
|
89a52d6280 | ||
|
e6520c0270 | ||
|
913a38111f | ||
|
4847149b6e | ||
|
d7573fe584 | ||
|
cb74c0cfe4 | ||
|
b725924f0a | ||
|
81cefc1913 | ||
|
a07cfee644 | ||
|
13cf92df27 | ||
|
61e6275781 | ||
|
78d772af86 | ||
|
e9e475a29d | ||
|
20d1be18af | ||
|
b0664a5e6c | ||
|
a38dbd9c8a | ||
|
f6a8d835d3 | ||
|
4746feaa1c | ||
|
3d4e788ea9 | ||
|
bd40574476 | ||
|
1674e2f34c | ||
|
3b2e783c1f | ||
|
46a9a23749 | ||
|
4e929b2d17 | ||
|
ef44c62d17 | ||
|
219aac7800 | ||
|
c110fa62ac | ||
|
7a6eaad445 | ||
|
460e380d38 | ||
|
778b37790b | ||
|
b66ec3bf95 | ||
|
51d760960c | ||
|
9110db41c5 | ||
|
45feb439bd | ||
|
44829d8216 | ||
|
49092945ab | ||
|
c82a2358bd | ||
|
ecf06d7e82 | ||
|
42fe05dea1 |
1
.babelrc
1
.babelrc
@@ -4,6 +4,7 @@
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"exclude": ["transform-async-to-generator", "transform-regenerator"],
|
||||
"loose": true,
|
||||
"modules": false,
|
||||
"targets": {
|
||||
|
95
.env.nanobox
95
.env.nanobox
@@ -13,11 +13,29 @@ DB_PORT=5432
|
||||
|
||||
DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||
|
||||
# Optional ElasticSearch configuration
|
||||
# ES_ENABLED=true
|
||||
# ES_HOST=localhost
|
||||
# ES_PORT=9200
|
||||
|
||||
# Optimizations
|
||||
LD_PRELOAD=/data/lib/libjemalloc.so
|
||||
|
||||
# ImageMagick optimizations
|
||||
MAGICK_TEMPORARY_PATH=/app/tmp
|
||||
MAGICK_MEMORY_LIMIT=128MiB
|
||||
MAGICK_MAP_LIMIT=64MiB
|
||||
MAGICK_TIME_LIMIT=15
|
||||
MAGICK_AREA_LIMIT=16MP
|
||||
MAGICK_WIDTH_LIMIT=8KP
|
||||
MAGICK_HEIGHT_LIMIT=8KP
|
||||
|
||||
# Federation
|
||||
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||
LOCAL_HTTPS=false
|
||||
|
||||
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||
|
||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||
@@ -31,7 +49,6 @@ LOCAL_HTTPS=false
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
|
||||
PAPERCLIP_SECRET=$PAPERCLIP_SECRET
|
||||
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||
OTP_SECRET=$OTP_SECRET
|
||||
|
||||
@@ -131,9 +148,79 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
|
||||
# Cluster number setting for streaming API server.
|
||||
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||
STREAMING_CLUSTER_NUM=1
|
||||
# STREAMING_CLUSTER_NUM=1
|
||||
|
||||
# Docker mastodon user
|
||||
# If you use Docker, you may want to assign UID/GID manually.
|
||||
# UID=1000
|
||||
# GID=1000
|
||||
|
||||
# LDAP authentication (optional)
|
||||
# LDAP_ENABLED=true
|
||||
# LDAP_HOST=localhost
|
||||
# LDAP_PORT=389
|
||||
# LDAP_METHOD=simple_tls
|
||||
# LDAP_BASE=
|
||||
# LDAP_BIND_DN=
|
||||
# LDAP_PASSWORD=
|
||||
# LDAP_UID=cn
|
||||
|
||||
# PAM authentication (optional)
|
||||
# PAM authentication uses for the email generation the "email" pam variable
|
||||
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||
# The pam environment variable "email" is provided by:
|
||||
# https://github.com/devkral/pam_email_extractor
|
||||
# PAM_ENABLED=true
|
||||
# Fallback Suffix for email address generation (nil by default)
|
||||
# PAM_DEFAULT_SUFFIX=pam
|
||||
# Name of the pam service (pam "auth" section is evaluated)
|
||||
# PAM_DEFAULT_SERVICE=rpam
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||
# PAM_CONTROLLED_SERVICE=rpam
|
||||
|
||||
# Global OAuth settings (optional) :
|
||||
# If you have only one strategy, you may want to enable this
|
||||
# OAUTH_REDIRECT_AT_SIGN_IN=true
|
||||
|
||||
# Optional CAS authentication (cf. omniauth-cas) :
|
||||
# CAS_ENABLED=true
|
||||
# CAS_URL=https://sso.myserver.com/
|
||||
# CAS_HOST=sso.myserver.com/
|
||||
# CAS_PORT=443
|
||||
# CAS_SSL=true
|
||||
# CAS_VALIDATE_URL=
|
||||
# CAS_CALLBACK_URL=
|
||||
# CAS_LOGOUT_URL=
|
||||
# CAS_LOGIN_URL=
|
||||
# CAS_UID_FIELD='user'
|
||||
# CAS_CA_PATH=
|
||||
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||
# CAS_UID_KEY='user'
|
||||
# CAS_NAME_KEY='name'
|
||||
# CAS_EMAIL_KEY='email'
|
||||
# CAS_NICKNAME_KEY='nickname'
|
||||
# CAS_FIRST_NAME_KEY='firstname'
|
||||
# CAS_LAST_NAME_KEY='lastname'
|
||||
# CAS_LOCATION_KEY='location'
|
||||
# CAS_IMAGE_KEY='image'
|
||||
# CAS_PHONE_KEY='phone'
|
||||
|
||||
# Optional SAML authentication (cf. omniauth-saml)
|
||||
# SAML_ENABLED=true
|
||||
# SAML_ACS_URL=
|
||||
# SAML_ISSUER=http://localhost:3000/auth/auth/saml/callback
|
||||
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||
# SAML_IDP_CERT=
|
||||
# SAML_IDP_CERT_FINGERPRINT=
|
||||
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||
# SAML_CERT=
|
||||
# SAML_PRIVATE_KEY=
|
||||
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||
|
@@ -11,7 +11,7 @@ DB_PASS=
|
||||
DB_PORT=5432
|
||||
# Optional ElasticSearch configuration
|
||||
# ES_ENABLED=true
|
||||
# ES_HOST=localhost
|
||||
# ES_HOST=es
|
||||
# ES_PORT=9200
|
||||
|
||||
# Federation
|
||||
@@ -155,11 +155,11 @@ STREAMING_CLUSTER_NUM=1
|
||||
# The pam environment variable "email" is provided by:
|
||||
# https://github.com/devkral/pam_email_extractor
|
||||
# PAM_ENABLED=true
|
||||
# Fallback Suffix for email address generation (nil by default)
|
||||
# PAM_DEFAULT_SUFFIX=pam
|
||||
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
|
||||
# PAM_EMAIL_DOMAIN=example.com
|
||||
# Name of the pam service (pam "auth" section is evaluated)
|
||||
# PAM_DEFAULT_SERVICE=rpam
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated)
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||
# PAM_CONTROLLED_SERVICE=rpam
|
||||
|
||||
# Global OAuth settings (optional) :
|
||||
@@ -204,7 +204,9 @@ STREAMING_CLUSTER_NUM=1
|
||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4"
|
||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||
|
@@ -1,4 +1,3 @@
|
||||
# Federation
|
||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
LOCAL_HTTPS=true
|
||||
OTP_SECRET=100c7faeef00caa29242f6b04156742bf76065771fd4117990c4282b8748ff3d99f8fdae97c982ab5bd2e6756a159121377cce4421f4a8ecd2d67bd7749a3fb4
|
||||
|
@@ -55,5 +55,5 @@ before_script:
|
||||
|
||||
script:
|
||||
- travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||
- yarn test
|
||||
- yarn run test:jest
|
||||
- bundle exec i18n-tasks check-normalized && bundle exec i18n-tasks unused
|
||||
|
450
AUTHORS.md
Normal file
450
AUTHORS.md
Normal file
@@ -0,0 +1,450 @@
|
||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
||||
and provided thanks to the work of the following contributors:
|
||||
|
||||
* [Gargron](https://github.com/Gargron)
|
||||
* [ykzts](https://github.com/ykzts)
|
||||
* [mjankowski](https://github.com/mjankowski)
|
||||
* [akihikodaki](https://github.com/akihikodaki)
|
||||
* [unarist](https://github.com/unarist)
|
||||
* [yiskah](https://github.com/yiskah)
|
||||
* [m4sk1n](https://github.com/m4sk1n)
|
||||
* [nolanlawson](https://github.com/nolanlawson)
|
||||
* [sorin-davidoi](https://github.com/sorin-davidoi)
|
||||
* [abcang](https://github.com/abcang)
|
||||
* [ThibG](https://github.com/ThibG)
|
||||
* [lynlynlynx](https://github.com/lynlynlynx)
|
||||
* [alpaca-tc](https://github.com/alpaca-tc)
|
||||
* [nclm](https://github.com/nclm)
|
||||
* [ineffyble](https://github.com/ineffyble)
|
||||
* [jeroenpraat](https://github.com/jeroenpraat)
|
||||
* [blackle](https://github.com/blackle)
|
||||
* [Quent-in](https://github.com/Quent-in)
|
||||
* [JantsoP](https://github.com/JantsoP)
|
||||
* [nullkal](https://github.com/nullkal)
|
||||
* [yookoala](https://github.com/yookoala)
|
||||
* [ysksn](https://github.com/ysksn)
|
||||
* [ashfurrow](https://github.com/ashfurrow)
|
||||
* [eramdam](https://github.com/eramdam)
|
||||
* [mayaeh](https://github.com/mayaeh)
|
||||
* [zunda](https://github.com/zunda)
|
||||
* [ticky](https://github.com/ticky)
|
||||
* [masarakki](https://github.com/masarakki)
|
||||
* [Wonderfall](https://github.com/Wonderfall)
|
||||
* [matteoaquila](https://github.com/matteoaquila)
|
||||
* [rkarabut](https://github.com/rkarabut)
|
||||
* [stephenburgess8](https://github.com/stephenburgess8)
|
||||
* [Kjwon15](https://github.com/Kjwon15)
|
||||
* [Artoria2e5](https://github.com/Artoria2e5)
|
||||
* [yukimochi](https://github.com/yukimochi)
|
||||
* [marrus-sh](https://github.com/marrus-sh)
|
||||
* [krainboltgreene](https://github.com/krainboltgreene)
|
||||
* [renatolond](https://github.com/renatolond)
|
||||
* [BoFFire](https://github.com/BoFFire)
|
||||
* [clworld](https://github.com/clworld)
|
||||
* [danhunsaker](https://github.com/danhunsaker)
|
||||
* [patf](https://github.com/patf)
|
||||
* [Quenty31](https://github.com/Quenty31)
|
||||
* [MitarashiDango](https://github.com/MitarashiDango)
|
||||
* [Aldarone](https://github.com/Aldarone)
|
||||
* [JeanGauthier](https://github.com/JeanGauthier)
|
||||
* [kschaper](https://github.com/kschaper)
|
||||
* [takayamaki](https://github.com/takayamaki)
|
||||
* [adbelle](https://github.com/adbelle)
|
||||
* [evanminto](https://github.com/evanminto)
|
||||
* [mabkenar](https://github.com/mabkenar)
|
||||
* [MightyPork](https://github.com/MightyPork)
|
||||
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
|
||||
* [yhirano55](https://github.com/yhirano55)
|
||||
* [camponez](https://github.com/camponez)
|
||||
* [aschmitz](https://github.com/aschmitz)
|
||||
* [fpiesche](https://github.com/fpiesche)
|
||||
* [gandaro](https://github.com/gandaro)
|
||||
* [johnsudaar](https://github.com/johnsudaar)
|
||||
* [trebmuh](https://github.com/trebmuh)
|
||||
* [Sylvhem](https://github.com/Sylvhem)
|
||||
* [lindwurm](https://github.com/lindwurm)
|
||||
* [voidsatisfaction](https://github.com/voidsatisfaction)
|
||||
* [neetshin](https://github.com/neetshin)
|
||||
* [valentin2105](https://github.com/valentin2105)
|
||||
* [hikari-no-yume](https://github.com/hikari-no-yume)
|
||||
* [Angristan](https://github.com/Angristan)
|
||||
* [seefood](https://github.com/seefood)
|
||||
* [jackjennings](https://github.com/jackjennings)
|
||||
* [hcmiya](https://github.com/hcmiya)
|
||||
* [nightpool](https://github.com/nightpool)
|
||||
* [salvadorpla](https://github.com/salvadorpla)
|
||||
* [expenses](https://github.com/expenses)
|
||||
* [walf443](https://github.com/walf443)
|
||||
* [JoelQ](https://github.com/JoelQ)
|
||||
* [mistydemeo](https://github.com/mistydemeo)
|
||||
* [dunn](https://github.com/dunn)
|
||||
* [xqus](https://github.com/xqus)
|
||||
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
|
||||
* [fakenine](https://github.com/fakenine)
|
||||
* [tsuwatch](https://github.com/tsuwatch)
|
||||
* [victorhck](https://github.com/victorhck)
|
||||
* [puckipedia](https://github.com/puckipedia)
|
||||
* [contraexemplo](https://github.com/contraexemplo)
|
||||
* [kazu9su](https://github.com/kazu9su)
|
||||
* [Komic](https://github.com/Komic)
|
||||
* [diomed](https://github.com/diomed)
|
||||
* [rainyday](https://github.com/rainyday)
|
||||
* [kadiix](https://github.com/kadiix)
|
||||
* [kodacs](https://github.com/kodacs)
|
||||
* [ProgVal](https://github.com/ProgVal)
|
||||
* [sterdev](https://github.com/sterdev)
|
||||
* [TheKinrar](https://github.com/TheKinrar)
|
||||
* [AA4ch1](https://github.com/AA4ch1)
|
||||
* [alexgleason](https://github.com/alexgleason)
|
||||
* [cpytel](https://github.com/cpytel)
|
||||
* [northerner](https://github.com/northerner)
|
||||
* [hnrysmth](https://github.com/hnrysmth)
|
||||
* [hugogameiro](https://github.com/hugogameiro)
|
||||
* [JohnD28](https://github.com/JohnD28)
|
||||
* [znz](https://github.com/znz)
|
||||
* [Naouak](https://github.com/Naouak)
|
||||
* [rtucker](https://github.com/rtucker)
|
||||
* [reneklacan](https://github.com/reneklacan)
|
||||
* [KScl](https://github.com/KScl)
|
||||
* [SerCom-KC](https://github.com/SerCom-KC)
|
||||
* [tcitworld](https://github.com/tcitworld)
|
||||
* [geta6](https://github.com/geta6)
|
||||
* [goofy-bz](https://github.com/goofy-bz)
|
||||
* [happycoloredbanana](https://github.com/happycoloredbanana)
|
||||
* [leopku](https://github.com/leopku)
|
||||
* [SansPseudoFix](https://github.com/SansPseudoFix)
|
||||
* [tomfhowe](https://github.com/tomfhowe)
|
||||
* [noraworld](https://github.com/noraworld)
|
||||
* [fvh-P](https://github.com/fvh-P)
|
||||
* [178inaba](https://github.com/178inaba)
|
||||
* [devkral](https://github.com/devkral)
|
||||
* [alyssais](https://github.com/alyssais)
|
||||
* [kodnaplakal](https://github.com/kodnaplakal)
|
||||
* [stalker314314](https://github.com/stalker314314)
|
||||
* [huertanix](https://github.com/huertanix)
|
||||
* [genesixx](https://github.com/genesixx)
|
||||
* [fhemberger](https://github.com/fhemberger)
|
||||
* [halkeye](https://github.com/halkeye)
|
||||
* [treby](https://github.com/treby)
|
||||
* [d6rkaiz](https://github.com/d6rkaiz)
|
||||
* [jpdevries](https://github.com/jpdevries)
|
||||
* [rndm-stranger](https://github.com/rndm-stranger)
|
||||
* [saper](https://github.com/saper)
|
||||
* [nevillepark](https://github.com/nevillepark)
|
||||
* [ornithocoder](https://github.com/ornithocoder)
|
||||
* [pierreozoux](https://github.com/pierreozoux)
|
||||
* [ramlmn](https://github.com/ramlmn)
|
||||
* [harukasan](https://github.com/harukasan)
|
||||
* [stamak](https://github.com/stamak)
|
||||
* [Eychics](https://github.com/Eychics)
|
||||
* [thor-the-norseman](https://github.com/thor-the-norseman)
|
||||
* [0x70b1a5](https://github.com/0x70b1a5)
|
||||
* [gled-rs](https://github.com/gled-rs)
|
||||
* [R0ckweb](https://github.com/R0ckweb)
|
||||
* [esetomo](https://github.com/esetomo)
|
||||
* [foxiehkins](https://github.com/foxiehkins)
|
||||
* [sdukhovni](https://github.com/sdukhovni)
|
||||
* [unsmell](https://github.com/unsmell)
|
||||
* [chriswmartin](https://github.com/chriswmartin)
|
||||
* [vahnj](https://github.com/vahnj)
|
||||
* [ikuradon](https://github.com/ikuradon)
|
||||
* [AndreLewin](https://github.com/AndreLewin)
|
||||
* [redtachyons](https://github.com/redtachyons)
|
||||
* [thurloat](https://github.com/thurloat)
|
||||
* [aaribaud](https://github.com/aaribaud)
|
||||
* [estuans](https://github.com/estuans)
|
||||
* [dissolve](https://github.com/dissolve)
|
||||
* [PurpleBooth](https://github.com/PurpleBooth)
|
||||
* [bradurani](https://github.com/bradurani)
|
||||
* [wavebeem](https://github.com/wavebeem)
|
||||
* [bruwalfas](https://github.com/bruwalfas)
|
||||
* [foxsan48](https://github.com/foxsan48)
|
||||
* [wchristian](https://github.com/wchristian)
|
||||
* [muffinista](https://github.com/muffinista)
|
||||
* [cdutson](https://github.com/cdutson)
|
||||
* [farlistener](https://github.com/farlistener)
|
||||
* [DavidLibeau](https://github.com/DavidLibeau)
|
||||
* [SirCmpwn](https://github.com/SirCmpwn)
|
||||
* [MasterGroosha](https://github.com/MasterGroosha)
|
||||
* [Fjoerfoks](https://github.com/Fjoerfoks)
|
||||
* [fmauNeko](https://github.com/fmauNeko)
|
||||
* [gloaec](https://github.com/gloaec)
|
||||
* [greysteil](https://github.com/greysteil)
|
||||
* [unstabler](https://github.com/unstabler)
|
||||
* [potato4d](https://github.com/potato4d)
|
||||
* [h-izumi](https://github.com/h-izumi)
|
||||
* [ErikXXon](https://github.com/ErikXXon)
|
||||
* [ian-kelling](https://github.com/ian-kelling)
|
||||
* [foozmeat](https://github.com/foozmeat)
|
||||
* [jasonrhodes](https://github.com/jasonrhodes)
|
||||
* [asm](https://github.com/asm)
|
||||
* [jviide](https://github.com/jviide)
|
||||
* [crakaC](https://github.com/crakaC)
|
||||
* [tkbky](https://github.com/tkbky)
|
||||
* [Kazhnuz](https://github.com/Kazhnuz)
|
||||
* [alimony](https://github.com/alimony)
|
||||
* [mig5](https://github.com/mig5)
|
||||
* [ndarville](https://github.com/ndarville)
|
||||
* [Abzol](https://github.com/Abzol)
|
||||
* [xPaw](https://github.com/xPaw)
|
||||
* [raymestalez](https://github.com/raymestalez)
|
||||
* [sim6](https://github.com/sim6)
|
||||
* [ekiru](https://github.com/ekiru)
|
||||
* [Technowix](https://github.com/Technowix)
|
||||
* [ThomasLeister](https://github.com/ThomasLeister)
|
||||
* [mcat-ee](https://github.com/mcat-ee)
|
||||
* [tototoshi](https://github.com/tototoshi)
|
||||
* [VirtuBox](https://github.com/VirtuBox)
|
||||
* [kaniini](https://github.com/kaniini)
|
||||
* [vayan](https://github.com/vayan)
|
||||
* [yannicka](https://github.com/yannicka)
|
||||
* [ikasoumen](https://github.com/ikasoumen)
|
||||
* [zacanger](https://github.com/zacanger)
|
||||
* [amazedkoumei](https://github.com/amazedkoumei)
|
||||
* [anon5r](https://github.com/anon5r)
|
||||
* [codl](https://github.com/codl)
|
||||
* [barzamin](https://github.com/barzamin)
|
||||
* [fhalna](https://github.com/fhalna)
|
||||
* [haoyayoi](https://github.com/haoyayoi)
|
||||
* [ik11235](https://github.com/ik11235)
|
||||
* [kawax](https://github.com/kawax)
|
||||
* [007lva](https://github.com/007lva)
|
||||
* [matsurai25](https://github.com/matsurai25)
|
||||
* [mecab](https://github.com/mecab)
|
||||
* [nicobz25](https://github.com/nicobz25)
|
||||
* [oliverkeeble](https://github.com/oliverkeeble)
|
||||
* [pinfort](https://github.com/pinfort)
|
||||
* [rbaumert](https://github.com/rbaumert)
|
||||
* [usagi-f](https://github.com/usagi-f)
|
||||
* [vidarlee](https://github.com/vidarlee)
|
||||
* [vjackson725](https://github.com/vjackson725)
|
||||
* [wxcafe](https://github.com/wxcafe)
|
||||
* [rinsuki](https://github.com/rinsuki)
|
||||
* [cygnan](https://github.com/cygnan)
|
||||
* [Awea](https://github.com/Awea)
|
||||
* [halcy](https://github.com/halcy)
|
||||
* [bounshi](https://github.com/bounshi)
|
||||
* [8398a7](https://github.com/8398a7)
|
||||
* [857b](https://github.com/857b)
|
||||
* [unascribed](https://github.com/unascribed)
|
||||
* [Aguay-val](https://github.com/Aguay-val)
|
||||
* [knu](https://github.com/knu)
|
||||
* [alxrcs](https://github.com/alxrcs)
|
||||
* [console-cowboy](https://github.com/console-cowboy)
|
||||
* [pointlessone](https://github.com/pointlessone)
|
||||
* [a2](https://github.com/a2)
|
||||
* [0xa](https://github.com/0xa)
|
||||
* [virtualpain](https://github.com/virtualpain)
|
||||
* [sapphirus](https://github.com/sapphirus)
|
||||
* [amandavisconti](https://github.com/amandavisconti)
|
||||
* [ameliavoncat](https://github.com/ameliavoncat)
|
||||
* [ilpianista](https://github.com/ilpianista)
|
||||
* [andydrop](https://github.com/andydrop)
|
||||
* [schas002](https://github.com/schas002)
|
||||
* [jumbosushi](https://github.com/jumbosushi)
|
||||
* [ayumin](https://github.com/ayumin)
|
||||
* [BaptisteGelez](https://github.com/BaptisteGelez)
|
||||
* [bzg](https://github.com/bzg)
|
||||
* [benediktg](https://github.com/benediktg)
|
||||
* [blakebarnett](https://github.com/blakebarnett)
|
||||
* [bradj](https://github.com/bradj)
|
||||
* [brycied00d](https://github.com/brycied00d)
|
||||
* [carlosjs23](https://github.com/carlosjs23)
|
||||
* [cgxxx](https://github.com/cgxxx)
|
||||
* [chrisheninger](https://github.com/chrisheninger)
|
||||
* [chris-martin](https://github.com/chris-martin)
|
||||
* [DoubleMalt](https://github.com/DoubleMalt)
|
||||
* [Moosh-be](https://github.com/Moosh-be)
|
||||
* [Motoma](https://github.com/Motoma)
|
||||
* [chriswk](https://github.com/chriswk)
|
||||
* [csu](https://github.com/csu)
|
||||
* [kklleemm](https://github.com/kklleemm)
|
||||
* [monsterpit-daggertooth](https://github.com/monsterpit-daggertooth)
|
||||
* [watilde](https://github.com/watilde)
|
||||
* [daprice](https://github.com/daprice)
|
||||
* [dar5hak](https://github.com/dar5hak)
|
||||
* [kant](https://github.com/kant)
|
||||
* [singingwolfboy](https://github.com/singingwolfboy)
|
||||
* [davidcelis](https://github.com/davidcelis)
|
||||
* [yipdw](https://github.com/yipdw)
|
||||
* [debanshuk](https://github.com/debanshuk)
|
||||
* [dblandin](https://github.com/dblandin)
|
||||
* [aranaur](https://github.com/aranaur)
|
||||
* [d3vgru](https://github.com/d3vgru)
|
||||
* [Elizafox](https://github.com/Elizafox)
|
||||
* [ericblade](https://github.com/ericblade)
|
||||
* [mikoim](https://github.com/mikoim)
|
||||
* [siuying](https://github.com/siuying)
|
||||
* [hattori6789](https://github.com/hattori6789)
|
||||
* [algernon](https://github.com/algernon)
|
||||
* [Fastbyte01](https://github.com/Fastbyte01)
|
||||
* [myfreeweb](https://github.com/myfreeweb)
|
||||
* [gfaivre](https://github.com/gfaivre)
|
||||
* [Fiaxhs](https://github.com/Fiaxhs)
|
||||
* [reedcourty](https://github.com/reedcourty)
|
||||
* [anneau](https://github.com/anneau)
|
||||
* [HellPie](https://github.com/HellPie)
|
||||
* [Habu-Kagumba](https://github.com/Habu-Kagumba)
|
||||
* [hinaloe](https://github.com/hinaloe)
|
||||
* [suzukaze](https://github.com/suzukaze)
|
||||
* [Hiromi-Kai](https://github.com/Hiromi-Kai)
|
||||
* [musashino205](https://github.com/musashino205)
|
||||
* [iwaim](https://github.com/iwaim)
|
||||
* [valrus](https://github.com/valrus)
|
||||
* [IMcD23](https://github.com/IMcD23)
|
||||
* [yi0713](https://github.com/yi0713)
|
||||
* [immae](https://github.com/immae)
|
||||
* [iblech](https://github.com/iblech)
|
||||
* [jack-michaud](https://github.com/jack-michaud)
|
||||
* [Floppy](https://github.com/Floppy)
|
||||
* [loomchild](https://github.com/loomchild)
|
||||
* [docjkl](https://github.com/docjkl)
|
||||
* [TrollDecker](https://github.com/TrollDecker)
|
||||
* [jmontane](https://github.com/jmontane)
|
||||
* [jonathanklee](https://github.com/jonathanklee)
|
||||
* [jguerder](https://github.com/jguerder)
|
||||
* [Jehops](https://github.com/Jehops)
|
||||
* [joshuap](https://github.com/joshuap)
|
||||
* [Tiwy57](https://github.com/Tiwy57)
|
||||
* [xuv](https://github.com/xuv)
|
||||
* [Jnsll](https://github.com/Jnsll)
|
||||
* [j0k3r](https://github.com/j0k3r)
|
||||
* [KEINOS](https://github.com/KEINOS)
|
||||
* [futoase](https://github.com/futoase)
|
||||
* [abjectio](https://github.com/abjectio)
|
||||
* [mkody](https://github.com/mkody)
|
||||
* [connyduck](https://github.com/connyduck)
|
||||
* [k0ta0uchi](https://github.com/k0ta0uchi)
|
||||
* [KrzysiekJ](https://github.com/KrzysiekJ)
|
||||
* [leowzukw](https://github.com/leowzukw)
|
||||
* [lmorchard](https://github.com/lmorchard)
|
||||
* [cacheflow](https://github.com/cacheflow)
|
||||
* [ldidry](https://github.com/ldidry)
|
||||
* [jemus42](https://github.com/jemus42)
|
||||
* [lfuelling](https://github.com/lfuelling)
|
||||
* [Grabacr07](https://github.com/Grabacr07)
|
||||
* [mistermantas](https://github.com/mistermantas)
|
||||
* [wirehack7](https://github.com/wirehack7)
|
||||
* [marvinkopf](https://github.com/marvinkopf)
|
||||
* [otsune](https://github.com/otsune)
|
||||
* [m-blc](https://github.com/m-blc)
|
||||
* [matt-auckland](https://github.com/matt-auckland)
|
||||
* [mattjmattj](https://github.com/mattjmattj)
|
||||
* [mtparet](https://github.com/mtparet)
|
||||
* [maximeborges](https://github.com/maximeborges)
|
||||
* [minacle](https://github.com/minacle)
|
||||
* [michaeljdeeb](https://github.com/michaeljdeeb)
|
||||
* [Themimitoof](https://github.com/Themimitoof)
|
||||
* [cyweo](https://github.com/cyweo)
|
||||
* [M1dgard](https://github.com/M1dgard)
|
||||
* [mike-burns](https://github.com/mike-burns)
|
||||
* [verymilan](https://github.com/verymilan)
|
||||
* [milmazz](https://github.com/milmazz)
|
||||
* [Mnkai](https://github.com/Mnkai)
|
||||
* [mitchhentges](https://github.com/mitchhentges)
|
||||
* [moritzheiber](https://github.com/moritzheiber)
|
||||
* [mouse-reeve](https://github.com/mouse-reeve)
|
||||
* [lae](https://github.com/lae)
|
||||
* [Nanamachi](https://github.com/Nanamachi)
|
||||
* [ngerakines](https://github.com/ngerakines)
|
||||
* [vonneudeck](https://github.com/vonneudeck)
|
||||
* [Ninetailed](https://github.com/Ninetailed)
|
||||
* [k24](https://github.com/k24)
|
||||
* [noiob](https://github.com/noiob)
|
||||
* [kwaio](https://github.com/kwaio)
|
||||
* [norayr](https://github.com/norayr)
|
||||
* [joyeusenoelle](https://github.com/joyeusenoelle)
|
||||
* [OlivierNicole](https://github.com/OlivierNicole)
|
||||
* [Otakan951](https://github.com/Otakan951)
|
||||
* [fahy](https://github.com/fahy)
|
||||
* [Pangoraw](https://github.com/Pangoraw)
|
||||
* [pwoolcoc](https://github.com/pwoolcoc)
|
||||
* [peterkeen](https://github.com/peterkeen)
|
||||
* [petzah](https://github.com/petzah)
|
||||
* [ignisf](https://github.com/ignisf)
|
||||
* [rfwatson](https://github.com/rfwatson)
|
||||
* [rfreebern](https://github.com/rfreebern)
|
||||
* [sylph01](https://github.com/sylph01)
|
||||
* [staticsafe](https://github.com/staticsafe)
|
||||
* [snwh](https://github.com/snwh)
|
||||
* [skoji](https://github.com/skoji)
|
||||
* [ScienJus](https://github.com/ScienJus)
|
||||
* [larkinscott](https://github.com/larkinscott)
|
||||
* [imolein](https://github.com/imolein)
|
||||
* [blinry](https://github.com/blinry)
|
||||
* [Noiwex](https://github.com/Noiwex)
|
||||
* [yuki764](https://github.com/yuki764)
|
||||
* [shnjp](https://github.com/shnjp)
|
||||
* [ernix](https://github.com/ernix)
|
||||
* [rosylilly](https://github.com/rosylilly)
|
||||
* [shouko](https://github.com/shouko)
|
||||
* [sossii](https://github.com/sossii)
|
||||
* [StefOfficiel](https://github.com/StefOfficiel)
|
||||
* [svetlik](https://github.com/svetlik)
|
||||
* [dereckson](https://github.com/dereckson)
|
||||
* [theboss](https://github.com/theboss)
|
||||
* [takp](https://github.com/takp)
|
||||
* [tkusano](https://github.com/tkusano)
|
||||
* [TheInventrix](https://github.com/TheInventrix)
|
||||
* [shug0](https://github.com/shug0)
|
||||
* [Fortyseven](https://github.com/Fortyseven)
|
||||
* [tobypinder](https://github.com/tobypinder)
|
||||
* [tomosm](https://github.com/tomosm)
|
||||
* [TomoyaShibata](https://github.com/TomoyaShibata)
|
||||
* [TrashMacNugget](https://github.com/TrashMacNugget)
|
||||
* [treyssatvincent](https://github.com/treyssatvincent)
|
||||
* [optikfluffel](https://github.com/optikfluffel)
|
||||
* [vmincev](https://github.com/vmincev)
|
||||
* [waldyrious](https://github.com/waldyrious)
|
||||
* [tahnok](https://github.com/tahnok)
|
||||
* [YDrogen](https://github.com/YDrogen)
|
||||
* [YOSHIOKAEiichiro](https://github.com/YOSHIOKAEiichiro)
|
||||
* [S-YOU](https://github.com/S-YOU)
|
||||
* [YaQ00](https://github.com/YaQ00)
|
||||
* [yanakend](https://github.com/yanakend)
|
||||
* [orzFly](https://github.com/orzFly)
|
||||
* [chansuke](https://github.com/chansuke)
|
||||
* [yuntan](https://github.com/yuntan)
|
||||
* [LogicalDash](https://github.com/LogicalDash)
|
||||
* [ZiiX](https://github.com/ZiiX)
|
||||
* [benklop](https://github.com/benklop)
|
||||
* [caasi](https://github.com/caasi)
|
||||
* [caesarologia](https://github.com/caesarologia)
|
||||
* [chrolis](https://github.com/chrolis)
|
||||
* [cormojs](https://github.com/cormojs)
|
||||
* [cpsdqs](https://github.com/cpsdqs)
|
||||
* [d0p1s4m4](https://github.com/d0p1s4m4)
|
||||
* [evilny0](https://github.com/evilny0)
|
||||
* [febrezo](https://github.com/febrezo)
|
||||
* [fsubal](https://github.com/fsubal)
|
||||
* [dikky1218](https://github.com/dikky1218)
|
||||
* [gentarok](https://github.com/gentarok)
|
||||
* [hakoai](https://github.com/hakoai)
|
||||
* [chaosbunker](https://github.com/chaosbunker)
|
||||
* [isati](https://github.com/isati)
|
||||
* [jkap](https://github.com/jkap)
|
||||
* [jirayudech](https://github.com/jirayudech)
|
||||
* [jukper](https://github.com/jukper)
|
||||
* [karlyeurl](https://github.com/karlyeurl)
|
||||
* [kedamaDQ](https://github.com/kedamaDQ)
|
||||
* [kuro5hin](https://github.com/kuro5hin)
|
||||
* [maxypy](https://github.com/maxypy)
|
||||
* [marcus-herrmann](https://github.com/marcus-herrmann)
|
||||
* [mshrtkch](https://github.com/mshrtkch)
|
||||
* [muan](https://github.com/muan)
|
||||
* [rch850](https://github.com/rch850)
|
||||
* [roikale](https://github.com/roikale)
|
||||
* [rysiekpl](https://github.com/rysiekpl)
|
||||
* [saturday06](https://github.com/saturday06)
|
||||
* [scriptjunkie](https://github.com/scriptjunkie)
|
||||
* [seekr](https://github.com/seekr)
|
||||
* [syui](https://github.com/syui)
|
||||
* [tackeyy](https://github.com/tackeyy)
|
||||
* [tmyt](https://github.com/tmyt)
|
||||
* [utam0k](https://github.com/utam0k)
|
||||
* [vpzomtrrfrt](https://github.com/vpzomtrrfrt)
|
||||
* [walfie](https://github.com/walfie)
|
||||
* [y-temp4](https://github.com/y-temp4)
|
||||
* [ymmtmdk](https://github.com/ymmtmdk)
|
||||
|
||||
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.
|
13
Dockerfile
13
Dockerfile
@@ -1,7 +1,7 @@
|
||||
FROM ruby:2.5.0-alpine3.7
|
||||
FROM ruby:2.4.3-alpine3.6
|
||||
|
||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||
description="A GNU Social-compatible microblogging server"
|
||||
description="Your self-hosted, globally interconnected microblogging community"
|
||||
|
||||
ARG UID=991
|
||||
ARG GID=991
|
||||
@@ -40,7 +40,6 @@ RUN apk -U upgrade \
|
||||
nodejs \
|
||||
nodejs-npm \
|
||||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
tzdata \
|
||||
&& update-ca-certificates \
|
||||
@@ -70,9 +69,13 @@ RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-in
|
||||
&& yarn --pure-lockfile \
|
||||
&& yarn cache clean
|
||||
|
||||
RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon
|
||||
RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \
|
||||
&& mkdir -p /mastodon/public/system /mastodon/public/assets /mastodon/public/packs \
|
||||
&& chown -R mastodon:mastodon /mastodon/public
|
||||
|
||||
COPY --chown=mastodon:mastodon . /mastodon
|
||||
COPY . /mastodon
|
||||
|
||||
RUN chown -R mastodon:mastodon /mastodon
|
||||
|
||||
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||
|
||||
|
14
Gemfile
14
Gemfile
@@ -27,15 +27,17 @@ gem 'bootsnap'
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.5'
|
||||
gem 'iso-639'
|
||||
gem 'chewy', '~> 0.10', git: 'https://github.com/toptal/chewy.git'
|
||||
gem 'chewy', '~> 5.0'
|
||||
gem 'cld3', '~> 3.2.0'
|
||||
gem 'devise', '~> 4.4'
|
||||
gem 'devise-two-factor', '~> 3.0'
|
||||
|
||||
gem 'devise_pam_authenticatable2', '~> 8.0', install_if: -> { ENV['PAM_ENABLED'] == 'true' }
|
||||
gem 'net-ldap', '~> 0.10', install_if: -> { ENV['LDAP_ENABLED'] == 'true' }
|
||||
gem 'omniauth-cas', '~> 1.1', install_if: -> { ENV['CAS_ENABLED'] == 'true' }
|
||||
gem 'omniauth-saml', '~> 1.10', install_if: -> { ENV['SAML_ENABLED'] == 'true' }
|
||||
group :pam_authentication, optional: true do
|
||||
gem 'devise_pam_authenticatable2', '~> 9.0'
|
||||
end
|
||||
gem 'net-ldap', '~> 0.10'
|
||||
gem 'omniauth-cas', '~> 1.1'
|
||||
gem 'omniauth-saml', '~> 1.10'
|
||||
gem 'omniauth', '~> 1.2'
|
||||
|
||||
gem 'doorkeeper', '~> 4.2'
|
||||
@@ -69,7 +71,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||
gem 'rqrcode', '~> 0.10'
|
||||
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
|
||||
gem 'ruby-progressbar', '~> 1.4'
|
||||
gem 'sanitize', '~> 4.4'
|
||||
gem 'sanitize', '~> 4.6.4'
|
||||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-scheduler', '~> 2.1'
|
||||
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||
|
31
Gemfile.lock
31
Gemfile.lock
@@ -1,12 +1,3 @@
|
||||
GIT
|
||||
remote: https://github.com/toptal/chewy.git
|
||||
revision: a7d21eb4b0bd7415533ef134bb6d31b2df309701
|
||||
specs:
|
||||
chewy (0.10.1)
|
||||
activesupport (>= 4.0)
|
||||
elasticsearch (>= 2.0.0)
|
||||
elasticsearch-dsl
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
@@ -118,6 +109,10 @@ GEM
|
||||
case_transform (0.2)
|
||||
activesupport
|
||||
charlock_holmes (0.7.5)
|
||||
chewy (5.0.0)
|
||||
activesupport (>= 4.0)
|
||||
elasticsearch (>= 2.0.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.3.8)
|
||||
cld3 (3.2.2)
|
||||
ffi (>= 1.1.0, < 1.10.0)
|
||||
@@ -146,7 +141,7 @@ GEM
|
||||
devise (~> 4.0)
|
||||
railties (< 5.2)
|
||||
rotp (~> 2.0)
|
||||
devise_pam_authenticatable2 (8.0.1)
|
||||
devise_pam_authenticatable2 (9.0.0)
|
||||
devise (>= 4.0.0)
|
||||
rpam2 (~> 3.0)
|
||||
diff-lcs (1.3)
|
||||
@@ -293,7 +288,7 @@ GEM
|
||||
activesupport (>= 4, < 5.2)
|
||||
railties (>= 4, < 5.2)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.1.1)
|
||||
loofah (2.2.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.0)
|
||||
@@ -321,9 +316,9 @@ GEM
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (4.2.0)
|
||||
nio4r (2.1.0)
|
||||
nokogiri (1.8.1)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.4.13)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
nsa (0.2.4)
|
||||
activesupport (>= 4.2, < 6)
|
||||
@@ -501,10 +496,10 @@ GEM
|
||||
rufus-scheduler (3.4.2)
|
||||
et-orbi (~> 1.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.5.0)
|
||||
sanitize (4.6.4)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4.1)
|
||||
nokogumbo (~> 1.4)
|
||||
sass (3.5.3)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
@@ -631,12 +626,12 @@ DEPENDENCIES
|
||||
capistrano-yarn (~> 2.0)
|
||||
capybara (~> 2.15)
|
||||
charlock_holmes (~> 0.7.5)
|
||||
chewy (~> 0.10)!
|
||||
chewy (~> 5.0)
|
||||
cld3 (~> 3.2.0)
|
||||
climate_control (~> 0.2)
|
||||
devise (~> 4.4)
|
||||
devise-two-factor (~> 3.0)
|
||||
devise_pam_authenticatable2 (~> 8.0)
|
||||
devise_pam_authenticatable2 (~> 9.0)
|
||||
doorkeeper (~> 4.2)
|
||||
dotenv-rails (~> 2.2)
|
||||
fabrication (~> 2.18)
|
||||
@@ -704,7 +699,7 @@ DEPENDENCIES
|
||||
rubocop
|
||||
ruby-oembed (~> 0.12)
|
||||
ruby-progressbar (~> 1.4)
|
||||
sanitize (~> 4.4)
|
||||
sanitize (~> 4.6.4)
|
||||
scss_lint (~> 0.55)
|
||||
sidekiq (~> 5.0)
|
||||
sidekiq-bulk (~> 0.1.1)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
web: PORT=3000 bundle exec puma -C config/puma.rb
|
||||
sidekiq: PORT=3000 bundle exec sidekiq
|
||||
stream: PORT=4000 yarn run start
|
||||
web: env PORT=3000 bundle exec puma -C config/puma.rb
|
||||
sidekiq: env PORT=3000 bundle exec sidekiq
|
||||
stream: env PORT=4000 yarn run start
|
||||
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
|
||||
|
10
README.md
10
README.md
@@ -79,6 +79,16 @@ You can open issues for bugs you've found or features you think are missing. You
|
||||
|
||||
**IRC channel**: #mastodon on irc.freenode.net
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2016-2018 Eugen Rochko & other Mastodon contributors (see AUTHORS.md)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
## Extra credits
|
||||
|
57
app/controllers/activitypub/collections_controller.rb
Normal file
57
app/controllers/activitypub/collections_controller.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::CollectionsController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
before_action :set_account
|
||||
before_action :set_size
|
||||
before_action :set_statuses
|
||||
|
||||
def show
|
||||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json',
|
||||
skip_activities: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = scope_for_collection.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
def set_size
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@account.pinned_statuses.count
|
||||
else
|
||||
raise ActiveRecord::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def scope_for_collection
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@account.statuses.permitted_for(@account, signed_request_account).tap do |scope|
|
||||
scope.merge!(@account.pinned_statuses)
|
||||
end
|
||||
else
|
||||
raise ActiveRecord::NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_collection_url(@account, params[:id]),
|
||||
type: :ordered,
|
||||
size: @size,
|
||||
items: @statuses
|
||||
)
|
||||
end
|
||||
end
|
@@ -9,7 +9,7 @@ class ActivityPub::OutboxesController < Api::BaseController
|
||||
@statuses = @account.statuses.permitted_for(@account, signed_request_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
|
||||
render json: outbox_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
@@ -51,7 +51,13 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
||||
end
|
||||
|
||||
def account_media_status_ids
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
|
||||
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
||||
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
||||
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
.reorder(id: :desc).distinct(:id).pluck(:id)
|
||||
end
|
||||
|
||||
def pinned_scope
|
||||
|
@@ -11,12 +11,18 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
|
||||
def create
|
||||
StatusPin.create!(account: current_account, status: @status)
|
||||
distribute_add_activity!
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
pin = StatusPin.find_by(account: current_account, status: @status)
|
||||
pin&.destroy!
|
||||
|
||||
if pin
|
||||
pin.destroy!
|
||||
distribute_remove_activity!
|
||||
end
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
@@ -25,4 +31,24 @@ class Api::V1::Statuses::PinsController < Api::BaseController
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
end
|
||||
|
||||
def distribute_add_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::AddSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account)
|
||||
end
|
||||
|
||||
def distribute_remove_activity!
|
||||
json = ActiveModelSerializers::SerializableResource.new(
|
||||
@status,
|
||||
serializer: ActivityPub::RemoveSerializer,
|
||||
adapter: ActivityPub::Adapter
|
||||
).as_json
|
||||
|
||||
ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account)
|
||||
end
|
||||
end
|
||||
|
@@ -12,10 +12,9 @@ class Auth::SessionsController < Devise::SessionsController
|
||||
|
||||
def new
|
||||
Devise.omniauth_configs.each do |provider, config|
|
||||
if config.strategy.redirect_at_sign_in
|
||||
return redirect_to(omniauth_authorize_path(resource_name, provider))
|
||||
end
|
||||
return redirect_to(omniauth_authorize_path(resource_name, provider)) if config.strategy.redirect_at_sign_in
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
@@ -59,6 +58,14 @@ class Auth::SessionsController < Devise::SessionsController
|
||||
end
|
||||
end
|
||||
|
||||
def after_sign_out_path_for(_resource_or_scope)
|
||||
Devise.omniauth_configs.each_value do |config|
|
||||
return root_path if config.strategy.redirect_at_sign_in
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def two_factor_enabled?
|
||||
find_user.try(:otp_required_for_login?)
|
||||
end
|
||||
|
@@ -17,11 +17,11 @@ module Localized
|
||||
end
|
||||
|
||||
def default_locale
|
||||
request_locale || env_locale || I18n.default_locale
|
||||
end
|
||||
|
||||
def env_locale
|
||||
ENV['DEFAULT_LOCALE']
|
||||
if ENV['DEFAULT_LOCALE'].present?
|
||||
I18n.default_locale
|
||||
else
|
||||
request_locale || I18n.default_locale
|
||||
end
|
||||
end
|
||||
|
||||
def request_locale
|
||||
@@ -29,12 +29,10 @@ module Localized
|
||||
end
|
||||
|
||||
def preferred_locale
|
||||
http_accept_language.preferred_language_from([env_locale]) ||
|
||||
http_accept_language.preferred_language_from(I18n.available_locales)
|
||||
http_accept_language.preferred_language_from(I18n.available_locales)
|
||||
end
|
||||
|
||||
def compatible_locale
|
||||
http_accept_language.compatible_language_from([env_locale]) ||
|
||||
http_accept_language.compatible_language_from(I18n.available_locales)
|
||||
http_accept_language.compatible_language_from(I18n.available_locales)
|
||||
end
|
||||
end
|
||||
|
@@ -34,7 +34,8 @@ class HomeController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
redirect_to(default_redirect_path)
|
||||
matches = request.path.match(%r{\A/web/timelines/tag/(?<tag>.+)\z})
|
||||
redirect_to(matches ? tag_path(CGI.unescape(matches[:tag])) : default_redirect_path)
|
||||
end
|
||||
|
||||
def set_initial_state_json
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
module InstanceHelper
|
||||
def site_title
|
||||
Setting.site_title.presence || site_hostname
|
||||
Setting.site_title
|
||||
end
|
||||
|
||||
def site_hostname
|
||||
|
@@ -8,6 +8,56 @@ module StreamEntriesHelper
|
||||
account.display_name.presence || account.username
|
||||
end
|
||||
|
||||
def account_description(account)
|
||||
prepend_str = [
|
||||
[
|
||||
number_to_human(account.statuses_count, strip_insignificant_zeros: true),
|
||||
t('accounts.posts'),
|
||||
].join(' '),
|
||||
|
||||
[
|
||||
number_to_human(account.following_count, strip_insignificant_zeros: true),
|
||||
t('accounts.following'),
|
||||
].join(' '),
|
||||
|
||||
[
|
||||
number_to_human(account.followers_count, strip_insignificant_zeros: true),
|
||||
t('accounts.followers'),
|
||||
].join(' '),
|
||||
].join(', ')
|
||||
|
||||
[prepend_str, account.note].join(' · ')
|
||||
end
|
||||
|
||||
def media_summary(status)
|
||||
attachments = { image: 0, video: 0 }
|
||||
|
||||
status.media_attachments.each do |media|
|
||||
if media.video?
|
||||
attachments[:video] += 1
|
||||
else
|
||||
attachments[:image] += 1
|
||||
end
|
||||
end
|
||||
|
||||
text = attachments.to_a.reject { |_, value| value.zero? }.map { |key, value| t("statuses.attached.#{key}", count: value) }.join(' · ')
|
||||
|
||||
return if text.blank?
|
||||
|
||||
t('statuses.attached.description', attached: text)
|
||||
end
|
||||
|
||||
def status_text_summary(status)
|
||||
return if status.spoiler_text.blank?
|
||||
t('statuses.content_warning', warning: status.spoiler_text)
|
||||
end
|
||||
|
||||
def status_description(status)
|
||||
components = [[media_summary(status), status_text_summary(status)].reject(&:blank?).join(' · ')]
|
||||
components << status.text if status.spoiler_text.blank?
|
||||
components.reject(&:blank?).join("\n\n")
|
||||
end
|
||||
|
||||
def stream_link_target
|
||||
embedded_view? ? '_blank' : nil
|
||||
end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import api from '../api';
|
||||
import { CancelToken } from 'axios';
|
||||
import { throttle } from 'lodash';
|
||||
import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light';
|
||||
import { tagHistory } from '../settings';
|
||||
import { useEmoji } from './emojis';
|
||||
|
||||
import {
|
||||
@@ -10,6 +12,8 @@ import {
|
||||
refreshPublicTimeline,
|
||||
} from './timelines';
|
||||
|
||||
let cancelFetchComposeSuggestionsAccounts;
|
||||
|
||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
||||
@@ -27,6 +31,9 @@ export const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
||||
export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
|
||||
export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
|
||||
export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
|
||||
export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
||||
|
||||
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
||||
|
||||
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
||||
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
|
||||
@@ -92,8 +99,9 @@ export function mentionCompose(account, router) {
|
||||
export function submitCompose() {
|
||||
return function (dispatch, getState) {
|
||||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
|
||||
if (!status || !status.length) {
|
||||
if ((!status || !status.length) && media.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,7 +110,7 @@ export function submitCompose() {
|
||||
api(getState).post('/api/v1/statuses', {
|
||||
status,
|
||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||
media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
||||
media_ids: media.map(item => item.get('id')),
|
||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
||||
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
|
||||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
@@ -111,6 +119,7 @@ export function submitCompose() {
|
||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||
},
|
||||
}).then(function (response) {
|
||||
dispatch(insertIntoTagHistory(response.data.tags));
|
||||
dispatch(submitComposeSuccess({ ...response.data }));
|
||||
|
||||
// To make the app more responsive, immediately get the status into the columns
|
||||
@@ -251,13 +260,22 @@ export function undoUploadCompose(media_id) {
|
||||
};
|
||||
|
||||
export function clearComposeSuggestions() {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
cancelFetchComposeSuggestionsAccounts();
|
||||
}
|
||||
return {
|
||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||
};
|
||||
};
|
||||
|
||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
cancelFetchComposeSuggestionsAccounts();
|
||||
}
|
||||
api(getState).get('/api/v1/accounts/search', {
|
||||
cancelToken: new CancelToken(cancel => {
|
||||
cancelFetchComposeSuggestionsAccounts = cancel;
|
||||
}),
|
||||
params: {
|
||||
q: token.slice(1),
|
||||
resolve: false,
|
||||
@@ -273,12 +291,22 @@ const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
|
||||
dispatch(readyComposeSuggestionsEmojis(token, results));
|
||||
};
|
||||
|
||||
const fetchComposeSuggestionsTags = (dispatch, getState, token) => {
|
||||
dispatch(updateSuggestionTags(token));
|
||||
};
|
||||
|
||||
export function fetchComposeSuggestions(token) {
|
||||
return (dispatch, getState) => {
|
||||
if (token[0] === ':') {
|
||||
switch (token[0]) {
|
||||
case ':':
|
||||
fetchComposeSuggestionsEmojis(dispatch, getState, token);
|
||||
} else {
|
||||
break;
|
||||
case '#':
|
||||
fetchComposeSuggestionsTags(dispatch, getState, token);
|
||||
break;
|
||||
default:
|
||||
fetchComposeSuggestionsAccounts(dispatch, getState, token);
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -308,6 +336,9 @@ export function selectComposeSuggestion(position, token, suggestion) {
|
||||
startPosition = position - 1;
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion[0] === '#') {
|
||||
completion = suggestion;
|
||||
startPosition = position - 1;
|
||||
} else {
|
||||
completion = getState().getIn(['accounts', suggestion, 'acct']);
|
||||
startPosition = position;
|
||||
@@ -322,6 +353,48 @@ export function selectComposeSuggestion(position, token, suggestion) {
|
||||
};
|
||||
};
|
||||
|
||||
export function updateSuggestionTags(token) {
|
||||
return {
|
||||
type: COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateTagHistory(tags) {
|
||||
return {
|
||||
type: COMPOSE_TAG_HISTORY_UPDATE,
|
||||
tags,
|
||||
};
|
||||
}
|
||||
|
||||
export function hydrateCompose() {
|
||||
return (dispatch, getState) => {
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const history = tagHistory.get(me);
|
||||
|
||||
if (history !== null) {
|
||||
dispatch(updateTagHistory(history));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function insertIntoTagHistory(tags) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const oldHistory = state.getIn(['compose', 'tagHistory']);
|
||||
const me = state.getIn(['meta', 'me']);
|
||||
const names = tags.map(({ name }) => name);
|
||||
const intersectedOldHistory = oldHistory.filter(name => !names.includes(name));
|
||||
|
||||
names.push(...intersectedOldHistory.toJS());
|
||||
|
||||
const newHistory = names.slice(0, 1000);
|
||||
|
||||
tagHistory.set(me, newHistory);
|
||||
dispatch(updateTagHistory(newHistory));
|
||||
};
|
||||
}
|
||||
|
||||
export function mountCompose() {
|
||||
return {
|
||||
type: COMPOSE_MOUNT,
|
||||
|
10
app/javascript/mastodon/actions/dropdown_menu.js
Normal file
10
app/javascript/mastodon/actions/dropdown_menu.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
||||
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
||||
|
||||
export function openDropdownMenu(id, placement) {
|
||||
return { type: DROPDOWN_MENU_OPEN, id, placement };
|
||||
}
|
||||
|
||||
export function closeDropdownMenu(id) {
|
||||
return { type: DROPDOWN_MENU_CLOSE, id };
|
||||
}
|
@@ -62,6 +62,7 @@ export function reblogRequest(status) {
|
||||
return {
|
||||
type: REBLOG_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -70,6 +71,7 @@ export function reblogSuccess(status, response) {
|
||||
type: REBLOG_SUCCESS,
|
||||
status: status,
|
||||
response: response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -78,6 +80,7 @@ export function reblogFail(status, error) {
|
||||
type: REBLOG_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -85,6 +88,7 @@ export function unreblogRequest(status) {
|
||||
return {
|
||||
type: UNREBLOG_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -93,6 +97,7 @@ export function unreblogSuccess(status, response) {
|
||||
type: UNREBLOG_SUCCESS,
|
||||
status: status,
|
||||
response: response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -101,6 +106,7 @@ export function unreblogFail(status, error) {
|
||||
type: UNREBLOG_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -132,6 +138,7 @@ export function favouriteRequest(status) {
|
||||
return {
|
||||
type: FAVOURITE_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -140,6 +147,7 @@ export function favouriteSuccess(status, response) {
|
||||
type: FAVOURITE_SUCCESS,
|
||||
status: status,
|
||||
response: response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -148,6 +156,7 @@ export function favouriteFail(status, error) {
|
||||
type: FAVOURITE_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -155,6 +164,7 @@ export function unfavouriteRequest(status) {
|
||||
return {
|
||||
type: UNFAVOURITE_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -163,6 +173,7 @@ export function unfavouriteSuccess(status, response) {
|
||||
type: UNFAVOURITE_SUCCESS,
|
||||
status: status,
|
||||
response: response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -171,6 +182,7 @@ export function unfavouriteFail(status, error) {
|
||||
type: UNFAVOURITE_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -258,6 +270,7 @@ export function pinRequest(status) {
|
||||
return {
|
||||
type: PIN_REQUEST,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -266,6 +279,7 @@ export function pinSuccess(status, response) {
|
||||
type: PIN_SUCCESS,
|
||||
status,
|
||||
response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -274,6 +288,7 @@ export function pinFail(status, error) {
|
||||
type: PIN_FAIL,
|
||||
status,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -293,6 +308,7 @@ export function unpinRequest(status) {
|
||||
return {
|
||||
type: UNPIN_REQUEST,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -301,6 +317,7 @@ export function unpinSuccess(status, response) {
|
||||
type: UNPIN_SUCCESS,
|
||||
status,
|
||||
response,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -309,5 +326,6 @@ export function unpinFail(status, error) {
|
||||
type: UNPIN_FAIL,
|
||||
status,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
@@ -24,7 +24,7 @@ defineMessages({
|
||||
const fetchRelatedRelationships = (dispatch, notifications) => {
|
||||
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
|
||||
|
||||
if (accountIds > 0) {
|
||||
if (accountIds.length > 0) {
|
||||
dispatch(fetchRelationships(accountIds));
|
||||
}
|
||||
};
|
||||
|
@@ -23,6 +23,9 @@ export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
|
||||
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
|
||||
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
|
||||
|
||||
export const STATUS_REVEAL = 'STATUS_REVEAL';
|
||||
export const STATUS_HIDE = 'STATUS_HIDE';
|
||||
|
||||
export function fetchStatusRequest(id, skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_REQUEST,
|
||||
@@ -215,3 +218,25 @@ export function unmuteStatusFail(id, error) {
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function hideStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
|
||||
return {
|
||||
type: STATUS_HIDE,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
|
||||
export function revealStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
|
||||
return {
|
||||
type: STATUS_REVEAL,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Iterable, fromJS } from 'immutable';
|
||||
import { hydrateCompose } from './compose';
|
||||
|
||||
export const STORE_HYDRATE = 'STORE_HYDRATE';
|
||||
export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
|
||||
@@ -8,10 +9,14 @@ const convertState = rawState =>
|
||||
Iterable.isIndexed(v) ? v.toList() : v.toMap());
|
||||
|
||||
export function hydrateStore(rawState) {
|
||||
const state = convertState(rawState);
|
||||
return dispatch => {
|
||||
const state = convertState(rawState);
|
||||
|
||||
return {
|
||||
type: STORE_HYDRATE,
|
||||
state,
|
||||
dispatch({
|
||||
type: STORE_HYDRATE,
|
||||
state,
|
||||
});
|
||||
|
||||
dispatch(hydrateCompose());
|
||||
};
|
||||
};
|
||||
|
@@ -117,13 +117,14 @@ export function refreshTimeline(timelineId, path, params = {}) {
|
||||
};
|
||||
};
|
||||
|
||||
export const refreshHomeTimeline = () => refreshTimeline('home', '/api/v1/timelines/home');
|
||||
export const refreshPublicTimeline = () => refreshTimeline('public', '/api/v1/timelines/public');
|
||||
export const refreshCommunityTimeline = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
|
||||
export const refreshAccountTimeline = (accountId, withReplies) => refreshTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies });
|
||||
export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
|
||||
export const refreshHashtagTimeline = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
|
||||
export const refreshListTimeline = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
|
||||
export const refreshHomeTimeline = () => refreshTimeline('home', '/api/v1/timelines/home');
|
||||
export const refreshPublicTimeline = () => refreshTimeline('public', '/api/v1/timelines/public');
|
||||
export const refreshCommunityTimeline = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
|
||||
export const refreshAccountTimeline = (accountId, withReplies) => refreshTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies });
|
||||
export const refreshAccountFeaturedTimeline = accountId => refreshTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
||||
export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
|
||||
export const refreshHashtagTimeline = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
|
||||
export const refreshListTimeline = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
|
||||
|
||||
export function refreshTimelineFail(timeline, error, skipLoading) {
|
||||
return {
|
||||
|
@@ -3,6 +3,7 @@ import 'intl/locale-data/jsonp/en';
|
||||
import 'es6-symbol/implement';
|
||||
import includes from 'array-includes';
|
||||
import assign from 'object-assign';
|
||||
import values from 'object.values';
|
||||
import isNaN from 'is-nan';
|
||||
|
||||
if (!Array.prototype.includes) {
|
||||
@@ -13,6 +14,10 @@ if (!Object.assign) {
|
||||
Object.assign = assign;
|
||||
}
|
||||
|
||||
if (!Object.values) {
|
||||
values.shim();
|
||||
}
|
||||
|
||||
if (!Number.isNaN) {
|
||||
Number.isNaN = isNaN;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||
@@ -8,10 +9,29 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
media: ImmutablePropTypes.list.isRequired,
|
||||
compact: PropTypes.bool,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { media } = this.props;
|
||||
const { media, compact } = this.props;
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<div className='attachment-list compact'>
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'><i className='fa fa-link' /> {filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='attachment-list'>
|
||||
@@ -20,11 +40,15 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
|
||||
</li>
|
||||
))}
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener'>{filename(displayUrl)}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
@@ -20,7 +20,7 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||
word = str.slice(left, right + caretPosition);
|
||||
}
|
||||
|
||||
if (!word || word.trim().length < 3 || ['@', ':'].indexOf(word[0]) === -1) {
|
||||
if (!word || word.trim().length < 3 || ['@', ':', '#'].indexOf(word[0]) === -1) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
@@ -170,6 +170,9 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
if (typeof suggestion === 'object') {
|
||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||
key = suggestion.id;
|
||||
} else if (suggestion[0] === '#') {
|
||||
inner = suggestion;
|
||||
key = suggestion;
|
||||
} else {
|
||||
inner = <AutosuggestAccountContainer id={suggestion} />;
|
||||
key = suggestion;
|
||||
|
@@ -19,10 +19,11 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
active: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
extraButton: PropTypes.node,
|
||||
showBackButton: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
pinned: PropTypes.bool,
|
||||
@@ -63,7 +64,7 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { title, icon, active, children, pinned, onPin, multiColumn, showBackButton, intl: { formatMessage } } = this.props;
|
||||
const { title, icon, active, children, pinned, onPin, multiColumn, extraButton, showBackButton, intl: { formatMessage } } = this.props;
|
||||
const { collapsed, animating } = this.state;
|
||||
|
||||
const wrapperClassName = classNames('column-header__wrapper', {
|
||||
@@ -125,19 +126,26 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
}
|
||||
|
||||
if (children || multiColumn) {
|
||||
collapseButton = <button className={collapsibleButtonClassName} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><i className='fa fa-sliders' /></button>;
|
||||
collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><i className='fa fa-sliders' /></button>;
|
||||
}
|
||||
|
||||
const hasTitle = icon && title;
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 className={buttonClassName}>
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<i className={`fa fa-fw fa-${icon} column-header__icon`} />
|
||||
{title}
|
||||
</button>
|
||||
{hasTitle && (
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<i className={`fa fa-fw fa-${icon} column-header__icon`} />
|
||||
{title}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!hasTitle && backButton}
|
||||
|
||||
<div className='column-header__buttons'>
|
||||
{backButton}
|
||||
{hasTitle && backButton}
|
||||
{extraButton}
|
||||
{collapseButton}
|
||||
</div>
|
||||
</h1>
|
||||
|
@@ -8,6 +8,7 @@ import spring from 'react-motion/lib/spring';
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
|
||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
|
||||
let id = 0;
|
||||
|
||||
class DropdownMenu extends React.PureComponent {
|
||||
|
||||
@@ -29,6 +30,10 @@ class DropdownMenu extends React.PureComponent {
|
||||
placement: 'bottom',
|
||||
};
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
};
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
@@ -38,6 +43,7 @@ class DropdownMenu extends React.PureComponent {
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
this.setState({ mounted: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
@@ -82,11 +88,15 @@ class DropdownMenu extends React.PureComponent {
|
||||
|
||||
render () {
|
||||
const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props;
|
||||
const { mounted } = this.state;
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}>
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||
|
||||
<ul>
|
||||
@@ -115,8 +125,10 @@ export default class Dropdown extends React.PureComponent {
|
||||
status: ImmutablePropTypes.map,
|
||||
isUserTouching: PropTypes.func,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
onModalOpen: PropTypes.func,
|
||||
onModalClose: PropTypes.func,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
dropdownPlacement: PropTypes.string,
|
||||
openDropdownId: PropTypes.number,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -124,37 +136,28 @@ export default class Dropdown extends React.PureComponent {
|
||||
};
|
||||
|
||||
state = {
|
||||
expanded: false,
|
||||
id: id++,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.state.expanded && this.props.isUserTouching() && this.props.onModalOpen) {
|
||||
const { status, items } = this.props;
|
||||
handleClick = ({ target }) => {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect();
|
||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top';
|
||||
|
||||
this.props.onModalOpen({
|
||||
status,
|
||||
actions: items,
|
||||
onClick: this.handleItemClick,
|
||||
});
|
||||
|
||||
return;
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, placement);
|
||||
}
|
||||
|
||||
this.setState({ expanded: !this.state.expanded });
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
if (this.props.onModalClose) {
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
this.setState({ expanded: false });
|
||||
this.props.onClose(this.state.id);
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch(e.key) {
|
||||
case 'Enter':
|
||||
this.handleClick();
|
||||
this.handleClick(e);
|
||||
break;
|
||||
case 'Escape':
|
||||
this.handleClose();
|
||||
@@ -186,22 +189,22 @@ export default class Dropdown extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { icon, items, size, title, disabled } = this.props;
|
||||
const { expanded } = this.state;
|
||||
const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId } = this.props;
|
||||
const open = this.state.id === openDropdownId;
|
||||
|
||||
return (
|
||||
<div onKeyDown={this.handleKeyDown}>
|
||||
<IconButton
|
||||
icon={icon}
|
||||
title={title}
|
||||
active={expanded}
|
||||
active={open}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
ref={this.setTargetRef}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
|
||||
<Overlay show={expanded} placement='bottom' target={this.findTarget}>
|
||||
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
||||
<DropdownMenu items={items} onClose={this.handleClose} />
|
||||
</Overlay>
|
||||
</div>
|
||||
|
@@ -11,6 +11,7 @@ export default class ExtendedVideoPlayer extends React.PureComponent {
|
||||
time: PropTypes.number,
|
||||
controls: PropTypes.bool.isRequired,
|
||||
muted: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
handleLoadedData = () => {
|
||||
@@ -31,6 +32,12 @@ export default class ExtendedVideoPlayer extends React.PureComponent {
|
||||
this.video = c;
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
e.stopPropagation();
|
||||
const handler = this.props.onClick;
|
||||
if (handler) handler();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { src, muted, controls, alt } = this.props;
|
||||
|
||||
@@ -46,6 +53,7 @@ export default class ExtendedVideoPlayer extends React.PureComponent {
|
||||
muted={muted}
|
||||
controls={controls}
|
||||
loop={!controls}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@@ -12,26 +12,6 @@ const messages = defineMessages({
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||
});
|
||||
|
||||
const shiftToPoint = (containerToImageRatio, containerSize, imageSize, focusSize, toMinus) => {
|
||||
const containerCenter = Math.floor(containerSize / 2);
|
||||
const focusFactor = (focusSize + 1) / 2;
|
||||
const scaledImage = Math.floor(imageSize / containerToImageRatio);
|
||||
|
||||
let focus = Math.floor(focusFactor * scaledImage);
|
||||
|
||||
if (toMinus) focus = scaledImage - focus;
|
||||
|
||||
let focusOffset = focus - containerCenter;
|
||||
|
||||
const remainder = scaledImage - focus;
|
||||
const containerRemainder = containerSize - containerCenter;
|
||||
|
||||
if (remainder < containerRemainder) focusOffset -= containerRemainder - remainder;
|
||||
if (focusOffset < 0) focusOffset = 0;
|
||||
|
||||
return (focusOffset * -100 / containerSize) + '%';
|
||||
};
|
||||
|
||||
class Item extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
@@ -44,8 +24,6 @@ class Item extends React.PureComponent {
|
||||
index: PropTypes.number.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
containerWidth: PropTypes.number,
|
||||
containerHeight: PropTypes.number,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -84,7 +62,7 @@ class Item extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { attachment, index, size, standalone, containerWidth, containerHeight } = this.props;
|
||||
const { attachment, index, size, standalone } = this.props;
|
||||
|
||||
let width = 50;
|
||||
let height = 100;
|
||||
@@ -143,35 +121,16 @@ class Item extends React.PureComponent {
|
||||
|
||||
const originalUrl = attachment.get('url');
|
||||
const originalWidth = attachment.getIn(['meta', 'original', 'width']);
|
||||
const originalHeight = attachment.getIn(['meta', 'original', 'height']);
|
||||
|
||||
const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
|
||||
|
||||
const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
|
||||
const sizes = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
|
||||
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']);
|
||||
const focusY = attachment.getIn(['meta', 'focus', 'y']);
|
||||
const imageStyle = {};
|
||||
|
||||
if (originalWidth && originalHeight && containerWidth && containerHeight && focusX && focusY) {
|
||||
const widthRatio = originalWidth / (containerWidth * (width / 100));
|
||||
const heightRatio = originalHeight / (containerHeight * (height / 100));
|
||||
|
||||
let hShift = 0;
|
||||
let vShift = 0;
|
||||
|
||||
if (widthRatio > heightRatio) {
|
||||
hShift = shiftToPoint(heightRatio, (containerWidth * (width / 100)), originalWidth, focusX);
|
||||
} else if(widthRatio < heightRatio) {
|
||||
vShift = shiftToPoint(widthRatio, (containerHeight * (height / 100)), originalHeight, focusY, true);
|
||||
}
|
||||
|
||||
imageStyle.top = vShift;
|
||||
imageStyle.left = hShift;
|
||||
} else {
|
||||
imageStyle.height = '100%';
|
||||
}
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
|
||||
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
|
||||
const x = ((focusX / 2) + .5) * 100;
|
||||
const y = ((focusY / -2) + .5) * 100;
|
||||
|
||||
thumbnail = (
|
||||
<a
|
||||
@@ -186,7 +145,7 @@ class Item extends React.PureComponent {
|
||||
sizes={sizes}
|
||||
alt={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
style={imageStyle}
|
||||
style={{ objectPosition: `${x}% ${y}%` }}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
@@ -310,7 +269,7 @@ export default class MediaGallery extends React.PureComponent {
|
||||
if (this.isStandaloneEligible()) {
|
||||
children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />;
|
||||
} else {
|
||||
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} containerWidth={width} containerHeight={style.height} />);
|
||||
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,9 +12,15 @@ export default class Permalink extends React.PureComponent {
|
||||
href: PropTypes.string.isRequired,
|
||||
to: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
onInterceptClick: PropTypes.func,
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
handleClick = e => {
|
||||
if (this.props.onInterceptClick && this.props.onInterceptClick()) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(this.props.to);
|
||||
@@ -22,7 +28,7 @@ export default class Permalink extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { href, children, className, ...other } = this.props;
|
||||
const { href, children, className, onInterceptClick, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
|
@@ -17,7 +17,7 @@ export default class ScrollableList extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
onScrollToBottom: PropTypes.func,
|
||||
onLoadMore: PropTypes.func.isRequired,
|
||||
onScrollToTop: PropTypes.func,
|
||||
onScroll: PropTypes.func,
|
||||
trackScroll: PropTypes.bool,
|
||||
@@ -45,9 +45,11 @@ export default class ScrollableList extends PureComponent {
|
||||
const offset = scrollHeight - scrollTop - clientHeight;
|
||||
this._oldScrollPosition = scrollHeight - scrollTop;
|
||||
|
||||
if (400 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
|
||||
this.props.onScrollToBottom();
|
||||
} else if (scrollTop < 100 && this.props.onScrollToTop) {
|
||||
if (400 > offset && this.props.onLoadMore && !this.props.isLoading) {
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
|
||||
if (scrollTop < 100 && this.props.onScrollToTop) {
|
||||
this.props.onScrollToTop();
|
||||
} else if (this.props.onScroll) {
|
||||
this.props.onScroll();
|
||||
@@ -138,7 +140,7 @@ export default class ScrollableList extends PureComponent {
|
||||
|
||||
handleLoadMore = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.onScrollToBottom();
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
|
||||
_recentlyMoved () {
|
||||
|
@@ -7,6 +7,7 @@ import RelativeTimestamp from './relative_timestamp';
|
||||
import DisplayName from './display_name';
|
||||
import StatusContent from './status_content';
|
||||
import StatusActionBar from './status_action_bar';
|
||||
import AttachmentList from './attachment_list';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { MediaGallery, Video } from '../features/ui/util/async-components';
|
||||
@@ -36,16 +37,13 @@ export default class Status extends ImmutablePureComponent {
|
||||
onBlock: PropTypes.func,
|
||||
onEmbed: PropTypes.func,
|
||||
onHeightChange: PropTypes.func,
|
||||
onToggleHidden: PropTypes.func,
|
||||
muted: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
isExpanded: false,
|
||||
}
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
// evaluate to false. See react-immutable-pure-component for usage.
|
||||
updateOnProps = [
|
||||
@@ -55,8 +53,6 @@ export default class Status extends ImmutablePureComponent {
|
||||
'hidden',
|
||||
]
|
||||
|
||||
updateOnStates = ['isExpanded']
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.context.router) {
|
||||
return;
|
||||
@@ -75,7 +71,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.setState({ isExpanded: !this.state.isExpanded });
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery () {
|
||||
@@ -138,8 +134,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
let media = null;
|
||||
let statusAvatar, prepend;
|
||||
|
||||
const { hidden } = this.props;
|
||||
const { isExpanded } = this.state;
|
||||
const { hidden, featured } = this.props;
|
||||
|
||||
let { status, account, ...other } = this.props;
|
||||
|
||||
@@ -156,7 +151,14 @@ export default class Status extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
if (featured) {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-thumb-tack status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned toot' />
|
||||
</div>
|
||||
);
|
||||
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
|
||||
|
||||
prepend = (
|
||||
@@ -170,9 +172,14 @@ export default class Status extends ImmutablePureComponent {
|
||||
status = status.get('reblog');
|
||||
}
|
||||
|
||||
if (status.get('media_attachments').size > 0 && !this.props.muted) {
|
||||
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
||||
|
||||
if (status.get('media_attachments').size > 0) {
|
||||
if (this.props.muted || status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
||||
media = (
|
||||
<AttachmentList
|
||||
compact
|
||||
media={status.get('media_attachments')}
|
||||
/>
|
||||
);
|
||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||
const video = status.getIn(['media_attachments', 0]);
|
||||
|
||||
@@ -235,7 +242,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<StatusContent status={status} onClick={this.handleClick} expanded={isExpanded} onExpandedToggle={this.handleExpandedToggle} />
|
||||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||
|
||||
{media}
|
||||
|
||||
|
@@ -24,7 +24,12 @@ export default class StatusContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
_updateStatusLinks () {
|
||||
const node = this.node;
|
||||
const node = this.node;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = node.querySelectorAll('a');
|
||||
|
||||
for (var i = 0; i < links.length; ++i) {
|
||||
@@ -115,6 +120,10 @@ export default class StatusContent extends React.PureComponent {
|
||||
render () {
|
||||
const { status } = this.props;
|
||||
|
||||
if (status.get('content').length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||
|
||||
const content = { __html: status.get('contentHtml') };
|
||||
@@ -145,7 +154,7 @@ export default class StatusContent extends React.PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
|
||||
<span dangerouslySetInnerHTML={spoilerContent} />
|
||||
{' '}
|
||||
|
@@ -11,7 +11,8 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
statusIds: ImmutablePropTypes.list.isRequired,
|
||||
onScrollToBottom: PropTypes.func,
|
||||
featuredStatusIds: ImmutablePropTypes.list,
|
||||
onLoadMore: PropTypes.func,
|
||||
onScrollToTop: PropTypes.func,
|
||||
onScroll: PropTypes.func,
|
||||
trackScroll: PropTypes.bool,
|
||||
@@ -50,7 +51,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusIds, ...other } = this.props;
|
||||
const { statusIds, featuredStatusIds, ...other } = this.props;
|
||||
const { isLoading, isPartial } = other;
|
||||
|
||||
if (isPartial) {
|
||||
@@ -68,8 +69,8 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
const scrollableContent = (isLoading || statusIds.size > 0) ? (
|
||||
statusIds.map((statusId) => (
|
||||
let scrollableContent = (isLoading || statusIds.size > 0) ? (
|
||||
statusIds.map(statusId => (
|
||||
<StatusContainer
|
||||
key={statusId}
|
||||
id={statusId}
|
||||
@@ -79,6 +80,18 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
))
|
||||
) : null;
|
||||
|
||||
if (scrollableContent && featuredStatusIds) {
|
||||
scrollableContent = featuredStatusIds.map(statusId => (
|
||||
<StatusContainer
|
||||
key={`f-${statusId}`}
|
||||
id={statusId}
|
||||
featured
|
||||
onMoveUp={this.handleMoveUp}
|
||||
onMoveDown={this.handleMoveDown}
|
||||
/>
|
||||
)).concat(scrollableContent);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollableList {...other} ref={this.setRef}>
|
||||
{scrollableContent}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { openDropdownMenu, closeDropdownMenu } from '../actions/dropdown_menu';
|
||||
import { openModal, closeModal } from '../actions/modal';
|
||||
import { connect } from 'react-redux';
|
||||
import DropdownMenu from '../components/dropdown_menu';
|
||||
@@ -5,12 +6,22 @@ import { isUserTouching } from '../is_mobile';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
isUserTouching,
|
||||
onModalOpen: props => dispatch(openModal('ACTIONS', props)),
|
||||
onModalClose: () => dispatch(closeModal()),
|
||||
const mapDispatchToProps = (dispatch, { status, items }) => ({
|
||||
onOpen(id, onItemClick, dropdownPlacement) {
|
||||
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
||||
status,
|
||||
actions: items,
|
||||
onClick: onItemClick,
|
||||
}) : openDropdownMenu(id, dropdownPlacement));
|
||||
},
|
||||
onClose(id) {
|
||||
dispatch(closeModal());
|
||||
dispatch(closeDropdownMenu(id));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DropdownMenu);
|
||||
|
@@ -15,7 +15,13 @@ import {
|
||||
unpin,
|
||||
} from '../actions/interactions';
|
||||
import { blockAccount } from '../actions/accounts';
|
||||
import { muteStatus, unmuteStatus, deleteStatus } from '../actions/statuses';
|
||||
import {
|
||||
muteStatus,
|
||||
unmuteStatus,
|
||||
deleteStatus,
|
||||
hideStatus,
|
||||
revealStatus,
|
||||
} from '../actions/statuses';
|
||||
import { initMuteModal } from '../actions/mutes';
|
||||
import { initReport } from '../actions/reports';
|
||||
import { openModal } from '../actions/modal';
|
||||
@@ -128,6 +134,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
}
|
||||
},
|
||||
|
||||
onToggleHidden (status) {
|
||||
if (status.get('hidden')) {
|
||||
dispatch(revealStatus(status.get('id')));
|
||||
} else {
|
||||
dispatch(hideStatus(status.get('id')));
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
|
||||
|
@@ -13,6 +13,7 @@ const messages = defineMessages({
|
||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
|
||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||
});
|
||||
|
||||
class Avatar extends ImmutablePureComponent {
|
||||
@@ -69,6 +70,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
@@ -80,11 +82,20 @@ export default class Header extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
let info = '';
|
||||
let mutingInfo = '';
|
||||
let actionBtn = '';
|
||||
let lockedIcon = '';
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||
info = <span className='account--follows-info'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>;
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
|
||||
info = <span className='account--follows-info'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>;
|
||||
}
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
|
||||
mutingInfo = <span className='account--muting-info'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>;
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
|
||||
mutingInfo = <span className='account--muting-info'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>;
|
||||
}
|
||||
|
||||
if (me !== account.get('id')) {
|
||||
@@ -100,6 +111,12 @@ export default class Header extends ImmutablePureComponent {
|
||||
<IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
|
||||
</div>
|
||||
);
|
||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||
actionBtn = (
|
||||
<div className='account--action-button'>
|
||||
<IconButton size={26} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +141,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
<div className='account__header__content' dangerouslySetInnerHTML={content} />
|
||||
|
||||
{info}
|
||||
{mutingInfo}
|
||||
{actionBtn}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Permalink from '../../../components/permalink';
|
||||
import { displaySensitiveMedia } from '../../../initial_state';
|
||||
|
||||
export default class MediaItem extends ImmutablePureComponent {
|
||||
|
||||
@@ -9,8 +10,22 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||
media: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: !this.props.media.getIn(['status', 'sensitive']) || displaySensitiveMedia,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.state.visible) {
|
||||
this.setState({ visible: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { media } = this.props;
|
||||
const { visible } = this.state;
|
||||
const status = media.get('status');
|
||||
const focusX = media.getIn(['meta', 'focus', 'x']);
|
||||
const focusY = media.getIn(['meta', 'focus', 'y']);
|
||||
@@ -18,21 +33,28 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||
const y = ((focusY / -2) + .5) * 100;
|
||||
const style = {};
|
||||
|
||||
let content;
|
||||
let label, icon;
|
||||
|
||||
if (media.get('type') === 'gifv') {
|
||||
content = <span className='media-gallery__gifv__label'>GIF</span>;
|
||||
label = <span className='media-gallery__gifv__label'>GIF</span>;
|
||||
}
|
||||
|
||||
if (!status.get('sensitive')) {
|
||||
if (visible) {
|
||||
style.backgroundImage = `url(${media.get('preview_url')})`;
|
||||
style.backgroundPosition = `${x}% ${y}%`;
|
||||
} else {
|
||||
icon = (
|
||||
<span className='account-gallery__item__icons'>
|
||||
<i className='fa fa-eye-slash' />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account-gallery__item'>
|
||||
<Permalink to={`/statuses/${status.get('id')}`} href={status.get('url')} style={style}>
|
||||
{content}
|
||||
<Permalink to={`/statuses/${status.get('id')}`} href={status.get('url')} style={style} onInterceptClick={this.handleClick}>
|
||||
{icon}
|
||||
{label}
|
||||
</Permalink>
|
||||
</div>
|
||||
);
|
||||
|
@@ -21,6 +21,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
onMute: PropTypes.func.isRequired,
|
||||
onBlockDomain: PropTypes.func.isRequired,
|
||||
onUnblockDomain: PropTypes.func.isRequired,
|
||||
hideTabs: PropTypes.bool,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -68,7 +69,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account } = this.props;
|
||||
const { account, hideTabs } = this.props;
|
||||
|
||||
if (account === null) {
|
||||
return <MissingIndicator />;
|
||||
@@ -81,6 +82,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
<InnerHeader
|
||||
account={account}
|
||||
onFollow={this.handleFollow}
|
||||
onBlock={this.handleBlock}
|
||||
/>
|
||||
|
||||
<ActionBar
|
||||
@@ -94,11 +96,13 @@ export default class Header extends ImmutablePureComponent {
|
||||
onUnblockDomain={this.handleUnblockDomain}
|
||||
/>
|
||||
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
|
||||
</div>
|
||||
{!hideTabs && (
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>
|
||||
<NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { fetchAccount } from '../../actions/accounts';
|
||||
import { refreshAccountTimeline, expandAccountTimeline } from '../../actions/timelines';
|
||||
import { refreshAccountTimeline, refreshAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines';
|
||||
import StatusList from '../../components/status_list';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import Column from '../ui/components/column';
|
||||
@@ -17,6 +17,7 @@ const mapStateToProps = (state, { params: { accountId }, withReplies = false })
|
||||
|
||||
return {
|
||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
|
||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
|
||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||
hasMore: !!state.getIn(['timelines', `account:${path}`, 'next']),
|
||||
};
|
||||
@@ -29,31 +30,40 @@ export default class AccountTimeline extends ImmutablePureComponent {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
statusIds: ImmutablePropTypes.list,
|
||||
featuredStatusIds: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
withReplies: PropTypes.bool,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.props.dispatch(fetchAccount(this.props.params.accountId));
|
||||
this.props.dispatch(refreshAccountTimeline(this.props.params.accountId, this.props.withReplies));
|
||||
const { params: { accountId }, withReplies } = this.props;
|
||||
|
||||
this.props.dispatch(fetchAccount(accountId));
|
||||
if (!withReplies) {
|
||||
this.props.dispatch(refreshAccountFeaturedTimeline(accountId));
|
||||
}
|
||||
this.props.dispatch(refreshAccountTimeline(accountId, withReplies));
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
|
||||
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||
if (!nextProps.withReplies) {
|
||||
this.props.dispatch(refreshAccountFeaturedTimeline(nextProps.params.accountId));
|
||||
}
|
||||
this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId, nextProps.params.withReplies));
|
||||
}
|
||||
}
|
||||
|
||||
handleScrollToBottom = () => {
|
||||
handleLoadMore = () => {
|
||||
if (!this.props.isLoading && this.props.hasMore) {
|
||||
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, this.props.withReplies));
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusIds, isLoading, hasMore } = this.props;
|
||||
const { statusIds, featuredStatusIds, isLoading, hasMore } = this.props;
|
||||
|
||||
if (!statusIds && isLoading) {
|
||||
return (
|
||||
@@ -71,9 +81,10 @@ export default class AccountTimeline extends ImmutablePureComponent {
|
||||
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
|
||||
scrollKey='account_timeline'
|
||||
statusIds={statusIds}
|
||||
featuredStatusIds={featuredStatusIds}
|
||||
isLoading={isLoading}
|
||||
hasMore={hasMore}
|
||||
onScrollToBottom={this.handleScrollToBottom}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
@@ -50,6 +50,7 @@ export default class ComposeForm extends ImmutablePureComponent {
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
showSearch: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -142,10 +143,10 @@ export default class ComposeForm extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, onPaste, showSearch } = this.props;
|
||||
const { intl, onPaste, showSearch, anyMedia } = this.props;
|
||||
const disabled = this.props.is_submitting;
|
||||
const text = [this.props.spoiler_text, countableText(this.props.text)].join('');
|
||||
|
||||
const disabledButton = disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
let publishText = '';
|
||||
|
||||
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||
@@ -203,7 +204,7 @@ export default class ComposeForm extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<div className='compose-form__publish'>
|
||||
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0)} block /></div>
|
||||
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabledButton} block /></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -23,6 +23,7 @@ const mapStateToProps = state => ({
|
||||
is_submitting: state.getIn(['compose', 'is_submitting']),
|
||||
is_uploading: state.getIn(['compose', 'is_uploading']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
@@ -9,7 +9,8 @@ import spring from 'react-motion/lib/spring';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'compose_form.sensitive', defaultMessage: 'Mark media as sensitive' },
|
||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
||||
unmarked: { id: 'compose_form.sensitive.unmarked', defaultMessage: 'Media is not marked as sensitive' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
@@ -50,7 +51,7 @@ class SensitiveButton extends React.PureComponent {
|
||||
<div className={className} style={{ transform: `scale(${scale})` }}>
|
||||
<IconButton
|
||||
className='compose-form__sensitive-button__icon'
|
||||
title={intl.formatMessage(messages.title)}
|
||||
title={intl.formatMessage(active ? messages.marked : messages.unmarked)}
|
||||
icon={icon}
|
||||
onClick={onClick}
|
||||
size={18}
|
||||
|
@@ -4,12 +4,13 @@ import { changeComposeSpoilerness } from '../../../actions/compose';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'compose_form.spoiler', defaultMessage: 'Hide text behind warning' },
|
||||
marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' },
|
||||
unmarked: { id: 'compose_form.spoiler.unmarked', defaultMessage: 'Text is not hidden' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { intl }) => ({
|
||||
label: 'CW',
|
||||
title: intl.formatMessage(messages.title),
|
||||
title: intl.formatMessage(state.getIn(['compose', 'spoiler']) ? messages.marked : messages.unmarked),
|
||||
active: state.getIn(['compose', 'spoiler']),
|
||||
ariaControls: 'cw-spoiler-input',
|
||||
});
|
||||
|
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { me } from '../../../initial_state';
|
||||
|
||||
const APPROX_HASHTAG_RE = /(?:^|[^\/\)\w])#(\w*[a-zA-Z]\w*)/i;
|
||||
const APPROX_HASHTAG_RE = /(?:^|[^\/\)\w])#(\w*[a-zA-Z·]\w*)/i;
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
|
||||
|
@@ -97,7 +97,7 @@ export default class Compose extends React.PureComponent {
|
||||
<ComposeFormContainer />
|
||||
{multiColumn && (
|
||||
<div className='drawer__inner__mastodon'>
|
||||
<img alt='' src={elephantUIPlane} />
|
||||
<img alt='' draggable='false' src={elephantUIPlane} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -62,7 +62,7 @@ export default class Favourites extends ImmutablePureComponent {
|
||||
this.column = c;
|
||||
}
|
||||
|
||||
handleScrollToBottom = debounce(() => {
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandFavouritedStatuses());
|
||||
}, 300, { leading: true })
|
||||
|
||||
@@ -89,7 +89,7 @@ export default class Favourites extends ImmutablePureComponent {
|
||||
scrollKey={`favourited_statuses-${columnId}`}
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onScrollToBottom={this.handleScrollToBottom}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
|
@@ -80,7 +80,7 @@ export default class Followers extends ImmutablePureComponent {
|
||||
<ScrollContainer scrollKey='followers'>
|
||||
<div className='scrollable' onScroll={this.handleScroll}>
|
||||
<div className='followers'>
|
||||
<HeaderContainer accountId={this.props.params.accountId} />
|
||||
<HeaderContainer accountId={this.props.params.accountId} hideTabs />
|
||||
{accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
|
||||
{loadMore}
|
||||
</div>
|
||||
|
@@ -80,7 +80,7 @@ export default class Following extends ImmutablePureComponent {
|
||||
<ScrollContainer scrollKey='following'>
|
||||
<div className='scrollable' onScroll={this.handleScroll}>
|
||||
<div className='following'>
|
||||
<HeaderContainer accountId={this.props.params.accountId} />
|
||||
<HeaderContainer accountId={this.props.params.accountId} hideTabs />
|
||||
{accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
|
||||
{loadMore}
|
||||
</div>
|
||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default class ClearColumnButton extends React.Component {
|
||||
export default class ClearColumnButton extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
|
@@ -50,8 +50,14 @@ export default class Notifications extends React.PureComponent {
|
||||
trackScroll: true,
|
||||
};
|
||||
|
||||
handleScrollToBottom = debounce(() => {
|
||||
componentWillUnmount () {
|
||||
this.handleLoadMore.cancel();
|
||||
this.handleScrollToTop.cancel();
|
||||
this.handleScroll.cancel();
|
||||
this.props.dispatch(scrollTopNotifications(false));
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandNotifications());
|
||||
}, 300, { leading: true });
|
||||
|
||||
@@ -136,7 +142,7 @@ export default class Notifications extends React.PureComponent {
|
||||
isLoading={isLoading}
|
||||
hasMore={hasMore}
|
||||
emptyMessage={emptyMessage}
|
||||
onScrollToBottom={this.handleScrollToBottom}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
onScrollToTop={this.handleScrollToTop}
|
||||
onScroll={this.handleScroll}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
|
@@ -2,6 +2,10 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Toggle from 'react-toggle';
|
||||
import noop from 'lodash/noop';
|
||||
import StatusContent from '../../../components/status_content';
|
||||
import { MediaGallery, Video } from '../../ui/util/async-components';
|
||||
import Bundle from '../../ui/components/bundle';
|
||||
|
||||
export default class StatusCheckBox extends React.PureComponent {
|
||||
|
||||
@@ -14,18 +18,48 @@ export default class StatusCheckBox extends React.PureComponent {
|
||||
|
||||
render () {
|
||||
const { status, checked, onToggle, disabled } = this.props;
|
||||
const content = { __html: status.get('contentHtml') };
|
||||
let media = null;
|
||||
|
||||
if (status.get('reblog')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (status.get('media_attachments').size > 0) {
|
||||
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
|
||||
|
||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||
const video = status.getIn(['media_attachments', 0]);
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
{Component => (
|
||||
<Component
|
||||
preview={video.get('preview_url')}
|
||||
src={video.get('url')}
|
||||
width={239}
|
||||
height={110}
|
||||
inline
|
||||
sensitive={status.get('sensitive')}
|
||||
onOpenVideo={noop}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
|
||||
{Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={noop} />}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='status-check-box'>
|
||||
<div
|
||||
className='status__content'
|
||||
dangerouslySetInnerHTML={content}
|
||||
/>
|
||||
<div className='status-check-box__status'>
|
||||
<StatusContent status={status} />
|
||||
{media}
|
||||
</div>
|
||||
|
||||
<div className='status-check-box-toggle'>
|
||||
<Toggle checked={checked} onChange={onToggle} disabled={disabled} />
|
||||
|
@@ -22,6 +22,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
onOpenVideo: PropTypes.func.isRequired,
|
||||
onToggleHidden: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
@@ -37,6 +38,10 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime);
|
||||
}
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.props.onToggleHidden(this.props.status);
|
||||
}
|
||||
|
||||
render () {
|
||||
const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
|
||||
|
||||
@@ -105,7 +110,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
<DisplayName account={status.get('account')} />
|
||||
</a>
|
||||
|
||||
<StatusContent status={status} />
|
||||
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||
|
||||
{media}
|
||||
|
||||
|
@@ -21,12 +21,19 @@ import {
|
||||
mentionCompose,
|
||||
} from '../../actions/compose';
|
||||
import { blockAccount } from '../../actions/accounts';
|
||||
import { muteStatus, unmuteStatus, deleteStatus } from '../../actions/statuses';
|
||||
import {
|
||||
muteStatus,
|
||||
unmuteStatus,
|
||||
deleteStatus,
|
||||
hideStatus,
|
||||
revealStatus,
|
||||
} from '../../actions/statuses';
|
||||
import { initMuteModal } from '../../actions/mutes';
|
||||
import { initReport } from '../../actions/reports';
|
||||
import { makeGetStatus } from '../../selectors';
|
||||
import { ScrollContainer } from 'react-router-scroll-4';
|
||||
import ColumnBackButton from '../../components/column_back_button';
|
||||
import ColumnHeader from '../../components/column_header';
|
||||
import StatusContainer from '../../containers/status_container';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
@@ -39,6 +46,8 @@ const messages = defineMessages({
|
||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||
blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
|
||||
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
||||
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
@@ -163,6 +172,25 @@ export default class Status extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleHidden = (status) => {
|
||||
if (status.get('hidden')) {
|
||||
this.props.dispatch(revealStatus(status.get('id')));
|
||||
} else {
|
||||
this.props.dispatch(hideStatus(status.get('id')));
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleAll = () => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS());
|
||||
|
||||
if (status.get('hidden')) {
|
||||
this.props.dispatch(revealStatus(statusIds));
|
||||
} else {
|
||||
this.props.dispatch(hideStatus(statusIds));
|
||||
}
|
||||
}
|
||||
|
||||
handleBlockClick = (account) => {
|
||||
const { dispatch, intl } = this.props;
|
||||
|
||||
@@ -293,7 +321,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
|
||||
render () {
|
||||
let ancestors, descendants;
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
const { status, ancestorsIds, descendantsIds, intl } = this.props;
|
||||
const { fullscreen } = this.state;
|
||||
|
||||
if (status === null) {
|
||||
@@ -325,7 +353,12 @@ export default class Status extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<ColumnBackButton />
|
||||
<ColumnHeader
|
||||
showBackButton
|
||||
extraButton={(
|
||||
<button className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll} aria-pressed={status.get('hidden') ? 'false' : 'true'}><i className={`fa fa-${status.get('hidden') ? 'eye-slash' : 'eye'}`} /></button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<ScrollContainer scrollKey='thread'>
|
||||
<div className={classNames('scrollable', 'detailed-status__wrapper', { fullscreen })} ref={this.setRef}>
|
||||
@@ -337,6 +370,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
onToggleHidden={this.handleToggleHidden}
|
||||
/>
|
||||
|
||||
<ActionBar
|
||||
|
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
const emptyComponent = () => null;
|
||||
const noop = () => { };
|
||||
|
||||
class Bundle extends React.Component {
|
||||
class Bundle extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
fetchComponent: PropTypes.func.isRequired,
|
||||
|
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' },
|
||||
});
|
||||
|
||||
class BundleColumnError extends React.Component {
|
||||
class BundleColumnError extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
|
@@ -10,7 +10,7 @@ const messages = defineMessages({
|
||||
close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
|
||||
});
|
||||
|
||||
class BundleModalError extends React.Component {
|
||||
class BundleModalError extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
|
@@ -28,6 +28,8 @@ const componentMap = {
|
||||
'LIST': ListTimeline,
|
||||
};
|
||||
|
||||
const shouldHideFAB = path => path.match(/^\/statuses\//);
|
||||
|
||||
@component => injectIntl(component, { withRef: true })
|
||||
export default class ColumnsArea extends ImmutablePureComponent {
|
||||
|
||||
@@ -153,7 +155,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
this.pendingIndex = null;
|
||||
|
||||
if (singleColumn) {
|
||||
const floatingActionButton = this.context.router.history.location.pathname === '/statuses/new' ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>;
|
||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>;
|
||||
|
||||
return columnIndex !== -1 ? [
|
||||
<ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
|
||||
|
@@ -103,8 +103,8 @@ export default class FocalPointModal extends ImmutablePureComponent {
|
||||
const height = media.getIn(['meta', 'original', 'height']) || null;
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal media-modal'>
|
||||
<div className={classNames('media-modal__content focal-point', { dragging })} ref={this.setRef}>
|
||||
<div className='modal-root__modal video-modal focal-point-modal'>
|
||||
<div className={classNames('focal-point', { dragging })} ref={this.setRef}>
|
||||
<ImageLoader
|
||||
previewSrc={media.get('preview_url')}
|
||||
src={media.get('url')}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import ZoomableImage from './zoomable_image';
|
||||
|
||||
export default class ImageLoader extends React.PureComponent {
|
||||
|
||||
@@ -10,6 +11,7 @@ export default class ImageLoader extends React.PureComponent {
|
||||
previewSrc: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
@@ -24,6 +26,7 @@ export default class ImageLoader extends React.PureComponent {
|
||||
}
|
||||
|
||||
removers = [];
|
||||
canvas = null;
|
||||
|
||||
get canvasContext() {
|
||||
if (!this.canvas) {
|
||||
@@ -43,6 +46,10 @@ export default class ImageLoader extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.removeEventListeners();
|
||||
}
|
||||
|
||||
loadImage (props) {
|
||||
this.removeEventListeners();
|
||||
this.setState({ loading: true, error: false });
|
||||
@@ -118,7 +125,7 @@ export default class ImageLoader extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { alt, src, width, height } = this.props;
|
||||
const { alt, src, width, height, onClick } = this.props;
|
||||
const { loading } = this.state;
|
||||
|
||||
const className = classNames('image-loader', {
|
||||
@@ -128,22 +135,19 @@ export default class ImageLoader extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<canvas
|
||||
className='image-loader__preview-canvas'
|
||||
width={width}
|
||||
height={height}
|
||||
ref={this.setCanvasRef}
|
||||
style={{ opacity: loading ? 1 : 0 }}
|
||||
/>
|
||||
|
||||
{!loading && (
|
||||
<img
|
||||
alt={alt}
|
||||
className='image-loader__img'
|
||||
src={src}
|
||||
{loading ? (
|
||||
<canvas
|
||||
className='image-loader__preview-canvas'
|
||||
ref={this.setCanvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
) : (
|
||||
<ZoomableImage
|
||||
alt={alt}
|
||||
src={src}
|
||||
onClick={onClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@@ -3,6 +3,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import ExtendedVideoPlayer from '../../../components/extended_video_player';
|
||||
import classNames from 'classnames';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import IconButton from '../../../components/icon_button';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
@@ -26,6 +27,7 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
|
||||
state = {
|
||||
index: null,
|
||||
navigationHidden: false,
|
||||
};
|
||||
|
||||
handleSwipe = (index) => {
|
||||
@@ -68,14 +70,21 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
return this.state.index !== null ? this.state.index : this.props.index;
|
||||
}
|
||||
|
||||
toggleNavigation = () => {
|
||||
this.setState(prevState => ({
|
||||
navigationHidden: !prevState.navigationHidden,
|
||||
}));
|
||||
};
|
||||
|
||||
render () {
|
||||
const { media, intl, onClose } = this.props;
|
||||
const { navigationHidden } = this.state;
|
||||
|
||||
const index = this.getIndex();
|
||||
let pagination = [];
|
||||
|
||||
const leftNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><i className='fa fa-fw fa-chevron-left' /></button>;
|
||||
const rightNav = media.size > 1 && <button tabIndex='0' className='modal-container__nav modal-container__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><i className='fa fa-fw fa-chevron-right' /></button>;
|
||||
const leftNav = media.size > 1 && <button tabIndex='0' className='media-modal__nav media-modal__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><i className='fa fa-fw fa-chevron-left' /></button>;
|
||||
const rightNav = media.size > 1 && <button tabIndex='0' className='media-modal__nav media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><i className='fa fa-fw fa-chevron-right' /></button>;
|
||||
|
||||
if (media.size > 1) {
|
||||
pagination = media.map((item, i) => {
|
||||
@@ -92,33 +101,77 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
const height = image.getIn(['meta', 'original', 'height']) || null;
|
||||
|
||||
if (image.get('type') === 'image') {
|
||||
return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} alt={image.get('description')} key={image.get('url')} />;
|
||||
return (
|
||||
<ImageLoader
|
||||
previewSrc={image.get('preview_url')}
|
||||
src={image.get('url')}
|
||||
width={width}
|
||||
height={height}
|
||||
alt={image.get('description')}
|
||||
key={image.get('url')}
|
||||
onClick={this.toggleNavigation}
|
||||
/>
|
||||
);
|
||||
} else if (image.get('type') === 'gifv') {
|
||||
return <ExtendedVideoPlayer src={image.get('url')} muted controls={false} width={width} height={height} key={image.get('preview_url')} alt={image.get('description')} />;
|
||||
return (
|
||||
<ExtendedVideoPlayer
|
||||
src={image.get('url')}
|
||||
muted
|
||||
controls={false}
|
||||
width={width}
|
||||
height={height}
|
||||
key={image.get('preview_url')}
|
||||
alt={image.get('description')}
|
||||
onClick={this.toggleNavigation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toArray();
|
||||
|
||||
// you can't use 100vh, because the viewport height is taller
|
||||
// than the visible part of the document in some mobile
|
||||
// browsers when it's address bar is visible.
|
||||
// https://developers.google.com/web/updates/2016/12/url-bar-resizing
|
||||
const swipeableViewsStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
const containerStyle = {
|
||||
alignItems: 'center', // center vertically
|
||||
};
|
||||
|
||||
const navigationClassName = classNames('media-modal__navigation', {
|
||||
'media-modal__navigation--hidden': navigationHidden,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal media-modal'>
|
||||
{leftNav}
|
||||
|
||||
<div className='media-modal__content'>
|
||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} />
|
||||
<ReactSwipeableViews containerStyle={containerStyle} onChangeIndex={this.handleSwipe} index={index}>
|
||||
<div
|
||||
className='media-modal__closer'
|
||||
role='presentation'
|
||||
onClick={onClose}
|
||||
>
|
||||
<ReactSwipeableViews
|
||||
style={swipeableViewsStyle}
|
||||
containerStyle={containerStyle}
|
||||
onChangeIndex={this.handleSwipe}
|
||||
onSwitching={this.handleSwitching}
|
||||
index={index}
|
||||
>
|
||||
{content}
|
||||
</ReactSwipeableViews>
|
||||
</div>
|
||||
<ul className='media-modal__pagination'>
|
||||
{pagination}
|
||||
</ul>
|
||||
|
||||
{rightNav}
|
||||
<div className={navigationClassName}>
|
||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={40} />
|
||||
{leftNav}
|
||||
{rightNav}
|
||||
<ul className='media-modal__pagination'>
|
||||
{pagination}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { debounce } from 'lodash';
|
||||
import { isUserTouching } from '../../../is_mobile';
|
||||
@@ -24,14 +24,12 @@ export function getLink (index) {
|
||||
}
|
||||
|
||||
@injectIntl
|
||||
export default class TabsBar extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
}
|
||||
@withRouter
|
||||
export default class TabsBar extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
setRef = ref => {
|
||||
@@ -59,7 +57,7 @@ export default class TabsBar extends React.Component {
|
||||
|
||||
const listener = debounce(() => {
|
||||
nextTab.removeEventListener('transitionend', listener);
|
||||
this.context.router.history.push(to);
|
||||
this.props.history.push(to);
|
||||
}, 50);
|
||||
|
||||
nextTab.addEventListener('transitionend', listener);
|
||||
|
@@ -16,7 +16,7 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||
const { media, time, onClose } = this.props;
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal media-modal'>
|
||||
<div className='modal-root__modal video-modal'>
|
||||
<div>
|
||||
<Video
|
||||
preview={media.get('preview_url')}
|
||||
|
151
app/javascript/mastodon/features/ui/components/zoomable_image.js
Normal file
151
app/javascript/mastodon/features/ui/components/zoomable_image.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const MIN_SCALE = 1;
|
||||
const MAX_SCALE = 4;
|
||||
|
||||
const getMidpoint = (p1, p2) => ({
|
||||
x: (p1.clientX + p2.clientX) / 2,
|
||||
y: (p1.clientY + p2.clientY) / 2,
|
||||
});
|
||||
|
||||
const getDistance = (p1, p2) =>
|
||||
Math.sqrt(Math.pow(p1.clientX - p2.clientX, 2) + Math.pow(p1.clientY - p2.clientY, 2));
|
||||
|
||||
const clamp = (min, max, value) => Math.min(max, Math.max(min, value));
|
||||
|
||||
export default class ZoomableImage extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
alt: PropTypes.string,
|
||||
src: PropTypes.string.isRequired,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
alt: '',
|
||||
width: null,
|
||||
height: null,
|
||||
};
|
||||
|
||||
state = {
|
||||
scale: MIN_SCALE,
|
||||
}
|
||||
|
||||
removers = [];
|
||||
container = null;
|
||||
image = null;
|
||||
lastTouchEndTime = 0;
|
||||
lastDistance = 0;
|
||||
|
||||
componentDidMount () {
|
||||
let handler = this.handleTouchStart;
|
||||
this.container.addEventListener('touchstart', handler);
|
||||
this.removers.push(() => this.container.removeEventListener('touchstart', handler));
|
||||
handler = this.handleTouchMove;
|
||||
// on Chrome 56+, touch event listeners will default to passive
|
||||
// https://www.chromestatus.com/features/5093566007214080
|
||||
this.container.addEventListener('touchmove', handler, { passive: false });
|
||||
this.removers.push(() => this.container.removeEventListener('touchend', handler));
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.removeEventListeners();
|
||||
}
|
||||
|
||||
removeEventListeners () {
|
||||
this.removers.forEach(listeners => listeners());
|
||||
this.removers = [];
|
||||
}
|
||||
|
||||
handleTouchStart = e => {
|
||||
if (e.touches.length !== 2) return;
|
||||
|
||||
this.lastDistance = getDistance(...e.touches);
|
||||
}
|
||||
|
||||
handleTouchMove = e => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = this.container;
|
||||
if (e.touches.length === 1 && scrollTop !== scrollHeight - clientHeight) {
|
||||
// prevent propagating event to MediaModal
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (e.touches.length !== 2) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const distance = getDistance(...e.touches);
|
||||
const midpoint = getMidpoint(...e.touches);
|
||||
const scale = clamp(MIN_SCALE, MAX_SCALE, this.state.scale * distance / this.lastDistance);
|
||||
|
||||
this.zoom(scale, midpoint);
|
||||
|
||||
this.lastMidpoint = midpoint;
|
||||
this.lastDistance = distance;
|
||||
}
|
||||
|
||||
zoom(nextScale, midpoint) {
|
||||
const { scale } = this.state;
|
||||
const { scrollLeft, scrollTop } = this.container;
|
||||
|
||||
// math memo:
|
||||
// x = (scrollLeft + midpoint.x) / scrollWidth
|
||||
// x' = (nextScrollLeft + midpoint.x) / nextScrollWidth
|
||||
// scrollWidth = clientWidth * scale
|
||||
// scrollWidth' = clientWidth * nextScale
|
||||
// Solve x = x' for nextScrollLeft
|
||||
const nextScrollLeft = (scrollLeft + midpoint.x) * nextScale / scale - midpoint.x;
|
||||
const nextScrollTop = (scrollTop + midpoint.y) * nextScale / scale - midpoint.y;
|
||||
|
||||
this.setState({ scale: nextScale }, () => {
|
||||
this.container.scrollLeft = nextScrollLeft;
|
||||
this.container.scrollTop = nextScrollTop;
|
||||
});
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
// don't propagate event to MediaModal
|
||||
e.stopPropagation();
|
||||
const handler = this.props.onClick;
|
||||
if (handler) handler();
|
||||
}
|
||||
|
||||
setContainerRef = c => {
|
||||
this.container = c;
|
||||
}
|
||||
|
||||
setImageRef = c => {
|
||||
this.image = c;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { alt, src } = this.props;
|
||||
const { scale } = this.state;
|
||||
const overflow = scale === 1 ? 'hidden' : 'scroll';
|
||||
|
||||
return (
|
||||
<div
|
||||
className='zoomable-image'
|
||||
ref={this.setContainerRef}
|
||||
style={{ overflow }}
|
||||
>
|
||||
<img
|
||||
role='presentation'
|
||||
ref={this.setImageRef}
|
||||
alt={alt}
|
||||
src={src}
|
||||
style={{
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: '0 0',
|
||||
}}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -56,10 +56,7 @@ const makeMapStateToProps = () => {
|
||||
|
||||
const mapDispatchToProps = (dispatch, { timelineId, loadMore }) => ({
|
||||
|
||||
onScrollToBottom: debounce(() => {
|
||||
dispatch(scrollTopTimeline(timelineId, false));
|
||||
loadMore();
|
||||
}, 300, { leading: true }),
|
||||
onLoadMore: debounce(loadMore, 300, { leading: true }),
|
||||
|
||||
onScrollToTop: debounce(() => {
|
||||
dispatch(scrollTopTimeline(timelineId, true));
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import NotificationsContainer from './containers/notifications_container';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -55,6 +56,7 @@ const messages = defineMessages({
|
||||
const mapStateToProps = state => ({
|
||||
isComposing: state.getIn(['compose', 'is_composing']),
|
||||
hasComposingText: state.getIn(['compose', 'text']) !== '',
|
||||
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||
});
|
||||
|
||||
const keyMap = {
|
||||
@@ -84,10 +86,93 @@ const keyMap = {
|
||||
goToMuted: 'g m',
|
||||
};
|
||||
|
||||
class SwitchingColumnsArea extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
onLayoutChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mobile: isMobile(window.innerWidth),
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
|
||||
this.node.handleChildrenContentChange();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
handleResize = debounce(() => {
|
||||
// The cached heights are no longer accurate, invalidate
|
||||
this.props.onLayoutChange();
|
||||
|
||||
this.setState({ mobile: isMobile(window.innerWidth) });
|
||||
}, 500, {
|
||||
trailing: true,
|
||||
});
|
||||
|
||||
setRef = c => {
|
||||
this.node = c.getWrappedInstance().getWrappedInstance();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children } = this.props;
|
||||
const { mobile } = this.state;
|
||||
|
||||
return (
|
||||
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
|
||||
<WrappedSwitch>
|
||||
<Redirect from='/' to='/getting-started' exact />
|
||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
|
||||
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
|
||||
|
||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||
|
||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||
<WrappedRoute path='/lists' component={Lists} content={children} />
|
||||
|
||||
<WrappedRoute component={GenericNotFound} content={children} />
|
||||
</WrappedSwitch>
|
||||
</ColumnsAreaContainer>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@connect(mapStateToProps)
|
||||
@injectIntl
|
||||
@withRouter
|
||||
export default class UI extends React.Component {
|
||||
export default class UI extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
@@ -100,10 +185,10 @@ export default class UI extends React.Component {
|
||||
hasComposingText: PropTypes.bool,
|
||||
location: PropTypes.object,
|
||||
intl: PropTypes.object.isRequired,
|
||||
dropdownMenuIsOpen: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
width: window.innerWidth,
|
||||
draggingOver: false,
|
||||
};
|
||||
|
||||
@@ -118,14 +203,10 @@ export default class UI extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleResize = debounce(() => {
|
||||
handleLayoutChange = () => {
|
||||
// The cached heights are no longer accurate, invalidate
|
||||
this.props.dispatch(clearHeight());
|
||||
|
||||
this.setState({ width: window.innerWidth });
|
||||
}, 500, {
|
||||
trailing: true,
|
||||
});
|
||||
}
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
@@ -193,7 +274,6 @@ export default class UI extends React.Component {
|
||||
|
||||
componentWillMount () {
|
||||
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
document.addEventListener('dragenter', this.handleDragEnter, false);
|
||||
document.addEventListener('dragover', this.handleDragOver, false);
|
||||
document.addEventListener('drop', this.handleDrop, false);
|
||||
@@ -214,28 +294,8 @@ export default class UI extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps) {
|
||||
if (nextProps.isComposing !== this.props.isComposing) {
|
||||
// Avoid expensive update just to toggle a class
|
||||
this.node.classList.toggle('is-composing', nextProps.isComposing);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Why isn't this working?!?
|
||||
// return super.shouldComponentUpdate(nextProps, nextState);
|
||||
return true;
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
|
||||
this.columnsAreaNode.handleChildrenContentChange();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
document.removeEventListener('dragenter', this.handleDragEnter);
|
||||
document.removeEventListener('dragover', this.handleDragOver);
|
||||
document.removeEventListener('drop', this.handleDrop);
|
||||
@@ -247,10 +307,6 @@ export default class UI extends React.Component {
|
||||
this.node = c;
|
||||
}
|
||||
|
||||
setColumnsAreaRef = c => {
|
||||
this.columnsAreaNode = c.getWrappedInstance().getWrappedInstance();
|
||||
}
|
||||
|
||||
handleHotkeyNew = e => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -350,8 +406,8 @@ export default class UI extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { width, draggingOver } = this.state;
|
||||
const { children } = this.props;
|
||||
const { draggingOver } = this.state;
|
||||
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
||||
|
||||
const handlers = {
|
||||
help: this.handleHotkeyToggleHelp,
|
||||
@@ -374,43 +430,12 @@ export default class UI extends React.Component {
|
||||
|
||||
return (
|
||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
|
||||
<div className='ui' ref={this.setRef}>
|
||||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
|
||||
<TabsBar />
|
||||
|
||||
<ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width)}>
|
||||
<WrappedSwitch>
|
||||
<Redirect from='/' to='/getting-started' exact />
|
||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
|
||||
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
|
||||
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
|
||||
|
||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
|
||||
|
||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
|
||||
<WrappedRoute path='/blocks' component={Blocks} content={children} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} />
|
||||
<WrappedRoute path='/lists' component={Lists} content={children} />
|
||||
|
||||
<WrappedRoute component={GenericNotFound} content={children} />
|
||||
</WrappedSwitch>
|
||||
</ColumnsAreaContainer>
|
||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
||||
<NotificationsContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
|
@@ -14,6 +14,7 @@ function loadPolyfills() {
|
||||
const needsBasePolyfills = !(
|
||||
window.Intl &&
|
||||
Object.assign &&
|
||||
Object.values &&
|
||||
Number.isNaN &&
|
||||
window.Symbol &&
|
||||
Array.prototype.includes
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "حظر @{name}",
|
||||
"account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}",
|
||||
"account.blocked": "محظور",
|
||||
"account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.",
|
||||
"account.domain_blocked": "النطاق مخفي",
|
||||
"account.edit_profile": "تعديل الملف الشخصي",
|
||||
"account.follow": "تابِع",
|
||||
"account.followers": "المتابعون",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} إنتقل إلى :",
|
||||
"account.mute": "أكتم @{name}",
|
||||
"account.mute_notifications": "كتم إخطارات @{name}",
|
||||
"account.muted": "مكتوم",
|
||||
"account.posts": "التبويقات",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "تبويقات تحتوي على رُدود",
|
||||
"account.report": "أبلغ عن @{name}",
|
||||
"account.requested": "في انتظار الموافقة",
|
||||
"account.share": "مشاركة @{name}'s profile",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "فيمَ تفكّر؟",
|
||||
"compose_form.publish": "بوّق",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "ضع علامة على الوسيط باعتباره حسّاس",
|
||||
"compose_form.spoiler": "أخفِ النص واعرض تحذيرا",
|
||||
"compose_form.sensitive.marked": "لقد تم تحديد هذه الصورة كحساسة",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "إنّ النص مخفي وراء تحذير",
|
||||
"compose_form.spoiler.unmarked": "النص غير مخفي",
|
||||
"compose_form.spoiler_placeholder": "تنبيه عن المحتوى",
|
||||
"confirmation_modal.cancel": "إلغاء",
|
||||
"confirmations.block.confirm": "حجب",
|
||||
@@ -208,21 +213,22 @@
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "إلغاء",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.forward": "التحويل إلى {target}",
|
||||
"report.forward_hint": "هذا الحساب ينتمي إلى خادوم آخَر. هل تودّ إرسال نسخة مجهولة مِن التقرير إلى هنالك أيضًا ؟",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.placeholder": "تعليقات إضافية",
|
||||
"report.submit": "إرسال",
|
||||
"report.target": "إبلاغ",
|
||||
"search.placeholder": "ابحث",
|
||||
"search_popout.search_format": "نمط البحث المتقدم",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "وسم",
|
||||
"search_popout.tips.status": "حالة",
|
||||
"search_popout.tips.text": "جملة قصيرة تُمكّنُك من عرض أسماء و حسابات و كلمات رمزية",
|
||||
"search_popout.tips.user": "مستخدِم",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.accounts": "أشخاص",
|
||||
"search_results.hashtags": "الوُسوم",
|
||||
"search_results.statuses": "التبويقات",
|
||||
"search_results.total": "{count, number} {count, plural, one {result} و {results}}",
|
||||
"standalone.public_title": "نظرة على ...",
|
||||
"status.block": "Block @{name}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "كتم المحادثة",
|
||||
"status.open": "وسع هذه المشاركة",
|
||||
"status.pin": "تدبيس على الملف الشخصي",
|
||||
"status.pinned": "تبويق مثبَّت",
|
||||
"status.reblog": "رَقِّي",
|
||||
"status.reblogged_by": "{name} رقى",
|
||||
"status.reply": "ردّ",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "محتوى حساس",
|
||||
"status.share": "مشاركة",
|
||||
"status.show_less": "إعرض أقلّ",
|
||||
"status.show_less_all": "طي الكل",
|
||||
"status.show_more": "أظهر المزيد",
|
||||
"status.show_more_all": "توسيع الكل",
|
||||
"status.unmute_conversation": "فك الكتم عن المحادثة",
|
||||
"status.unpin": "فك التدبيس من الملف الشخصي",
|
||||
"tabs_bar.compose": "تحرير",
|
||||
"tabs_bar.federated_timeline": "الموحَّد",
|
||||
"tabs_bar.home": "الرئيسية",
|
||||
"tabs_bar.local_timeline": "المحلي",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "إسحب ثم أفلت للرفع",
|
||||
"upload_button.label": "إضافة وسائط",
|
||||
"upload_form.description": "وصف للمعاقين بصريا",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "قص",
|
||||
"upload_form.undo": "إلغاء",
|
||||
"upload_progress.label": "يرفع...",
|
||||
"video.close": "إغلاق الفيديو",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Блокирай",
|
||||
"account.block_domain": "Hide everything from {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Редактирай профила си",
|
||||
"account.follow": "Последвай",
|
||||
"account.followers": "Последователи",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.mute": "Mute @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Публикации",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Report @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Какво си мислиш?",
|
||||
"compose_form.publish": "Раздумай",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Отбележи съдържанието като деликатно",
|
||||
"compose_form.spoiler": "Скрий текста зад предупреждение",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Content warning",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.confirm": "Block",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Reporting",
|
||||
"search.placeholder": "Търсене",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Expand this status",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Споделяне",
|
||||
"status.reblogged_by": "{name} сподели",
|
||||
"status.reply": "Отговор",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Деликатно съдържание",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Show less",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Show more",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Unmute conversation",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Съставяне",
|
||||
"tabs_bar.federated_timeline": "Federated",
|
||||
"tabs_bar.home": "Начало",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloca @{name}",
|
||||
"account.block_domain": "Amaga-ho tot de {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Edita el perfil",
|
||||
"account.follow": "Segueix",
|
||||
"account.followers": "Seguidors",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} s'ha mogut a:",
|
||||
"account.mute": "Silencia @{name}",
|
||||
"account.mute_notifications": "Notificacions desactivades de @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "Toots amb respostes",
|
||||
"account.report": "Informe @{name}",
|
||||
"account.requested": "Esperant aprovació. Clic per a cancel·lar la petició de seguiment",
|
||||
"account.share": "Comparteix el perfil de @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "En què estàs pensant?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marca el contingut multimèdia com a sensible",
|
||||
"compose_form.spoiler": "Amaga el text darrera darrere un avís",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Escriu l'avís aquí",
|
||||
"confirmation_modal.cancel": "Cancel·la",
|
||||
"confirmations.block.confirm": "Bloca",
|
||||
@@ -208,20 +213,21 @@
|
||||
"relative_time.minutes": "fa {number} minuts",
|
||||
"relative_time.seconds": "fa {number} segons",
|
||||
"reply_indicator.cancel": "Cancel·lar",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Reenvia a {target}",
|
||||
"report.forward_hint": "Aquest compte és d'un altre servidor. Enviar-hi també una copia anònima del informe?",
|
||||
"report.hint": "El informe s'enviarà als moderadors de la teva instància. Pots explicar perquè vols informar d'aquest compte aquí:",
|
||||
"report.placeholder": "Comentaris addicionals",
|
||||
"report.submit": "Enviar",
|
||||
"report.target": "Informes",
|
||||
"search.placeholder": "Cercar",
|
||||
"search_popout.search_format": "Format de cerca avançada",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "etiqueta",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "El text simple retorna coincidències amb els noms de visualització, els noms d'usuari i els hashtags",
|
||||
"search_popout.tips.user": "usuari",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.accounts": "Gent",
|
||||
"search_results.hashtags": "Etiquetes",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.total": "{count, number} {count, plural, un {result} altres {results}}",
|
||||
"standalone.public_title": "Una mirada a l'interior ...",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Silenciar conversació",
|
||||
"status.open": "Ampliar aquest estat",
|
||||
"status.pin": "Fixat en el perfil",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Impuls",
|
||||
"status.reblogged_by": "{name} ha retootejat",
|
||||
"status.reply": "Respondre",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Contingut sensible",
|
||||
"status.share": "Compartir",
|
||||
"status.show_less": "Mostra menys",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Mostra més",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Activar conversació",
|
||||
"status.unpin": "Deslliga del perfil",
|
||||
"tabs_bar.compose": "Compondre",
|
||||
"tabs_bar.federated_timeline": "Federada",
|
||||
"tabs_bar.home": "Inici",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Arrossega i deixa anar per carregar",
|
||||
"upload_button.label": "Afegir multimèdia",
|
||||
"upload_form.description": "Descriure els problemes visuals",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Retallar",
|
||||
"upload_form.undo": "Desfer",
|
||||
"upload_progress.label": "Pujant...",
|
||||
"video.close": "Tancar el vídeo",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "@{name} blocken",
|
||||
"account.block_domain": "Alles von {domain} verstecken",
|
||||
"account.blocked": "Blockiert",
|
||||
"account.disclaimer_full": "Das Profil wird möglicherweise unvollständig wiedergegeben.",
|
||||
"account.domain_blocked": "Domain versteckt",
|
||||
"account.edit_profile": "Profil bearbeiten",
|
||||
"account.follow": "Folgen",
|
||||
"account.followers": "Folgende",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} ist umgezogen auf:",
|
||||
"account.mute": "@{name} stummschalten",
|
||||
"account.mute_notifications": "Benachrichtigungen von @{name} verbergen",
|
||||
"account.muted": "Stummgeschaltet",
|
||||
"account.posts": "Beiträge",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "Beiträge mit Antworten",
|
||||
"account.report": "@{name} melden",
|
||||
"account.requested": "Warte auf Erlaubnis. Klicke zum Abbrechen",
|
||||
"account.share": "Profil von @{name} teilen",
|
||||
@@ -37,7 +40,7 @@
|
||||
"column.favourites": "Favoriten",
|
||||
"column.follow_requests": "Folgeanfragen",
|
||||
"column.home": "Startseite",
|
||||
"column.lists": "Lists",
|
||||
"column.lists": "Listen",
|
||||
"column.mutes": "Stummgeschaltete Profile",
|
||||
"column.notifications": "Mitteilungen",
|
||||
"column.pins": "Angeheftete Beiträge",
|
||||
@@ -51,14 +54,16 @@
|
||||
"column_header.unpin": "Lösen",
|
||||
"column_subheading.navigation": "Navigation",
|
||||
"column_subheading.settings": "Einstellungen",
|
||||
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
|
||||
"compose_form.hashtag_warning": "Dieser Beitrag wird nicht unter einen dieser Hashtags sichtbar sein, solange er ungelistet ist. Bei einer Suche kann er nicht gefunden werden.",
|
||||
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.",
|
||||
"compose_form.lock_disclaimer.lock": "gesperrt",
|
||||
"compose_form.placeholder": "Worüber möchtest du schreiben?",
|
||||
"compose_form.publish": "Tröt",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Medien als heikel markieren",
|
||||
"compose_form.spoiler": "Text hinter Warnung verbergen",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Inhaltswarnung",
|
||||
"confirmation_modal.cancel": "Abbrechen",
|
||||
"confirmations.block.confirm": "Blockieren",
|
||||
@@ -66,7 +71,7 @@
|
||||
"confirmations.delete.confirm": "Löschen",
|
||||
"confirmations.delete.message": "Bist du dir sicher, dass du diesen Beitrag löschen möchtest?",
|
||||
"confirmations.delete_list.confirm": "Delete",
|
||||
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
|
||||
"confirmations.delete_list.message": "Bist du dir sicher, dass du diese Liste permanent löschen möchtest?",
|
||||
"confirmations.domain_block.confirm": "Die ganze Domain verbergen",
|
||||
"confirmations.domain_block.message": "Bist du dir wirklich sicher, dass du die ganze Domain {domain} verbergen willst? In den meisten Fällen reichen ein paar gezielte Blocks aus.",
|
||||
"confirmations.mute.confirm": "Stummschalten",
|
||||
@@ -112,35 +117,35 @@
|
||||
"keyboard_shortcuts.back": "zurück navigieren",
|
||||
"keyboard_shortcuts.boost": "boosten",
|
||||
"keyboard_shortcuts.column": "einen Status in einer der Spalten fokussieren",
|
||||
"keyboard_shortcuts.compose": "to focus the compose textarea",
|
||||
"keyboard_shortcuts.description": "Description",
|
||||
"keyboard_shortcuts.compose": "um das Textfeld zu fokussieren",
|
||||
"keyboard_shortcuts.description": "Beschreibung",
|
||||
"keyboard_shortcuts.down": "sich in der Liste hinunter bewegen",
|
||||
"keyboard_shortcuts.enter": "to open status",
|
||||
"keyboard_shortcuts.favourite": "favorisieren",
|
||||
"keyboard_shortcuts.heading": "Keyboard Shortcuts",
|
||||
"keyboard_shortcuts.enter": "um den Status zu öffnen",
|
||||
"keyboard_shortcuts.favourite": "um zu favorisieren",
|
||||
"keyboard_shortcuts.heading": "Tastenkombinationen",
|
||||
"keyboard_shortcuts.hotkey": "Hotkey",
|
||||
"keyboard_shortcuts.legend": "diese Übersicht anzeigen",
|
||||
"keyboard_shortcuts.mention": "Autor_in erwähnen",
|
||||
"keyboard_shortcuts.reply": "antworten",
|
||||
"keyboard_shortcuts.search": "die Suche fokussieren",
|
||||
"keyboard_shortcuts.toot": "einen neuen Toot beginnen",
|
||||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||
"keyboard_shortcuts.legend": "um diese Übersicht anzuzeigen",
|
||||
"keyboard_shortcuts.mention": "um Autor_in zu erwähnen",
|
||||
"keyboard_shortcuts.reply": "um zu antworten",
|
||||
"keyboard_shortcuts.search": "um die Suche zu fokussieren",
|
||||
"keyboard_shortcuts.toot": "um einen neuen Toot zu beginnen",
|
||||
"keyboard_shortcuts.unfocus": "um das Textfeld/die Suche nicht mehr zu fokussieren",
|
||||
"keyboard_shortcuts.up": "sich in der Liste hinauf bewegen",
|
||||
"lightbox.close": "Schließen",
|
||||
"lightbox.next": "Weiter",
|
||||
"lightbox.previous": "Zurück",
|
||||
"lists.account.add": "Add to list",
|
||||
"lists.account.remove": "Remove from list",
|
||||
"lists.account.add": "Zur Liste hinzufügen",
|
||||
"lists.account.remove": "Von der Liste entfernen",
|
||||
"lists.delete": "Delete list",
|
||||
"lists.edit": "Edit list",
|
||||
"lists.new.create": "Add list",
|
||||
"lists.new.title_placeholder": "New list title",
|
||||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"lists.edit": "Liste bearbeiten",
|
||||
"lists.new.create": "Liste hinzufügen",
|
||||
"lists.new.title_placeholder": "Neuer Titel der Liste",
|
||||
"lists.search": "Suche nach Leuten denen du folgst",
|
||||
"lists.subheading": "Deine Listen",
|
||||
"loading_indicator.label": "Wird geladen …",
|
||||
"media_gallery.toggle_visible": "Sichtbarkeit umschalten",
|
||||
"missing_indicator.label": "Nicht gefunden",
|
||||
"missing_indicator.sublabel": "This resource could not be found",
|
||||
"missing_indicator.sublabel": "Die Ressource konnte nicht gefunden werden",
|
||||
"mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
|
||||
"navigation_bar.blocks": "Blockierte Profile",
|
||||
"navigation_bar.community_timeline": "Lokale Zeitleiste",
|
||||
@@ -149,7 +154,7 @@
|
||||
"navigation_bar.follow_requests": "Folgeanfragen",
|
||||
"navigation_bar.info": "Über diese Instanz",
|
||||
"navigation_bar.keyboard_shortcuts": "Tastenkombinationen",
|
||||
"navigation_bar.lists": "Lists",
|
||||
"navigation_bar.lists": "Listen",
|
||||
"navigation_bar.logout": "Abmelden",
|
||||
"navigation_bar.mutes": "Stummgeschaltete Profile",
|
||||
"navigation_bar.pins": "Angeheftete Beiträge",
|
||||
@@ -172,7 +177,7 @@
|
||||
"notifications.column_settings.sound": "Ton abspielen",
|
||||
"onboarding.done": "Fertig",
|
||||
"onboarding.next": "Weiter",
|
||||
"onboarding.page_five.public_timelines": "Die lokale Zeitleiste zeigt alle Beiträge von Leuten, die auch auf {domain} sind. Das gesamte bekannte Netz zeigt Beiträge von allen, denen von Leuten auf {domain} gefolgt wird. Zusammen sind sie die öffentlichen Zeitleisten. In ihnen kannst du viel Neues entdecken!",
|
||||
"onboarding.page_five.public_timelines": "Die lokale Zeitleiste zeigt alle Beiträge von Leuten, die auch auf {domain} sind. Das gesamte bekannte Netz zeigt Beiträge von allen, denen von Leuten auf {domain} gefolgt wird. Zusammen sind sie die öffentlichen Zeitleisten, ein guter Weg, um neue Leute zu finden.",
|
||||
"onboarding.page_four.home": "Die Startseite zeigt dir Beiträge von Leuten, denen du folgst.",
|
||||
"onboarding.page_four.notifications": "Wenn jemand mit dir interagiert, bekommst du eine Mitteilung.",
|
||||
"onboarding.page_one.federation": "Mastodon ist ein soziales Netzwerk, das aus unabhängigen Servern besteht. Diese Server nennen wir auch Instanzen.",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "{target} melden",
|
||||
"search.placeholder": "Suche",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Thread stummschalten",
|
||||
"status.open": "Diesen Beitrag öffnen",
|
||||
"status.pin": "Im Profil anheften",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Teilen",
|
||||
"status.reblogged_by": "{name} teilte",
|
||||
"status.reply": "Antworten",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Heikle Inhalte",
|
||||
"status.share": "Teilen",
|
||||
"status.show_less": "Weniger anzeigen",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Mehr anzeigen",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Stummschaltung von Thread aufheben",
|
||||
"status.unpin": "Vom Profil lösen",
|
||||
"tabs_bar.compose": "Schreiben",
|
||||
"tabs_bar.federated_timeline": "Föderation",
|
||||
"tabs_bar.home": "Startseite",
|
||||
"tabs_bar.local_timeline": "Lokal",
|
||||
|
@@ -274,6 +274,10 @@
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Pinned toot",
|
||||
"id": "status.pinned"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "{name} boosted",
|
||||
"id": "status.reblogged_by"
|
||||
@@ -469,9 +473,25 @@
|
||||
"defaultMessage": "Awaiting approval. Click to cancel follow request",
|
||||
"id": "account.requested"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Unblock @{name}",
|
||||
"id": "account.unblock"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Follows you",
|
||||
"id": "account.follows_you"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Blocked",
|
||||
"id": "account.blocked"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Muted",
|
||||
"id": "account.muted"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Domain hidden",
|
||||
"id": "account.domain_blocked"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/account/components/header.json"
|
||||
@@ -683,6 +703,14 @@
|
||||
"defaultMessage": "Search",
|
||||
"id": "search.placeholder"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"id": "search_popout.tips.full_text"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Simple text returns matching display names, usernames and hashtags",
|
||||
"id": "search_popout.tips.text"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Advanced search format",
|
||||
"id": "search_popout.search_format"
|
||||
@@ -698,10 +726,6 @@
|
||||
{
|
||||
"defaultMessage": "status",
|
||||
"id": "search_popout.tips.status"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Simple text returns matching display names, usernames and hashtags",
|
||||
"id": "search_popout.tips.text"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/compose/components/search.json"
|
||||
@@ -744,8 +768,12 @@
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Mark media as sensitive",
|
||||
"id": "compose_form.sensitive"
|
||||
"defaultMessage": "Media is marked as sensitive",
|
||||
"id": "compose_form.sensitive.marked"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Media is not marked as sensitive",
|
||||
"id": "compose_form.sensitive.unmarked"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json"
|
||||
@@ -753,8 +781,12 @@
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Hide text behind warning",
|
||||
"id": "compose_form.spoiler"
|
||||
"defaultMessage": "Text is hidden behind warning",
|
||||
"id": "compose_form.spoiler.marked"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Text is not hidden",
|
||||
"id": "compose_form.spoiler.unmarked"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/compose/containers/spoiler_button_container.json"
|
||||
@@ -1351,6 +1383,14 @@
|
||||
"defaultMessage": "Block",
|
||||
"id": "confirmations.block.confirm"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Show more for all",
|
||||
"id": "status.show_more_all"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Show less for all",
|
||||
"id": "status.show_less_all"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Are you sure you want to block {name}?",
|
||||
"id": "confirmations.block.message"
|
||||
@@ -1576,6 +1616,10 @@
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Close",
|
||||
"id": "lightbox.close"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Additional comments",
|
||||
"id": "report.placeholder"
|
||||
@@ -1605,10 +1649,6 @@
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Compose",
|
||||
"id": "tabs_bar.compose"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Home",
|
||||
"id": "tabs_bar.home"
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Block @{name}",
|
||||
"account.block_domain": "Hide everything from {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Edit profile",
|
||||
"account.follow": "Follow",
|
||||
"account.followers": "Followers",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.mute": "Mute @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Report @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "What is on your mind?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Mark media as sensitive",
|
||||
"compose_form.spoiler": "Hide text behind warning",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Write your warning here",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.confirm": "Block",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Reporting {target}",
|
||||
"search.placeholder": "Search",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Expand this status",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblogged_by": "{name} boosted",
|
||||
"status.reply": "Reply",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Sensitive content",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Show less",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Show more",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Unmute conversation",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Compose",
|
||||
"tabs_bar.federated_timeline": "Federated",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloki @{name}",
|
||||
"account.block_domain": "Kaŝi ĉion de {domain}",
|
||||
"account.blocked": "Blokita",
|
||||
"account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.",
|
||||
"account.domain_blocked": "Domajno kaŝita",
|
||||
"account.edit_profile": "Redakti profilon",
|
||||
"account.follow": "Sekvi",
|
||||
"account.followers": "Sekvantoj",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} moviĝis al:",
|
||||
"account.mute": "Silentigi @{name}",
|
||||
"account.mute_notifications": "Silentigi sciigojn el @{name}",
|
||||
"account.posts": "Hupoj",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.muted": "Silentigita",
|
||||
"account.posts": "Mesaĝoj",
|
||||
"account.posts_with_replies": "Mesaĝoj kun respondoj",
|
||||
"account.report": "Signali @{name}",
|
||||
"account.requested": "Atendo de aprobo. Alklaku por nuligi peton de sekvado",
|
||||
"account.share": "Diskonigi la profilon de @{name}",
|
||||
@@ -50,15 +53,17 @@
|
||||
"column_header.show_settings": "Montri agordojn",
|
||||
"column_header.unpin": "Depingli",
|
||||
"column_subheading.navigation": "Navigado",
|
||||
"column_subheading.settings": "Agordoj",
|
||||
"column_subheading.settings": "Agordado",
|
||||
"compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
|
||||
"compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.",
|
||||
"compose_form.lock_disclaimer.lock": "ŝlosita",
|
||||
"compose_form.placeholder": "Pri kio vi pensas?",
|
||||
"compose_form.publish": "Hup",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marki aŭdovidaĵon tikla",
|
||||
"compose_form.spoiler": "Kaŝi tekston malantaŭ averto",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie",
|
||||
"confirmation_modal.cancel": "Nuligi",
|
||||
"confirmations.block.confirm": "Bloki",
|
||||
@@ -147,7 +152,7 @@
|
||||
"navigation_bar.edit_profile": "Redakti profilon",
|
||||
"navigation_bar.favourites": "Stelumoj",
|
||||
"navigation_bar.follow_requests": "Petoj de sekvado",
|
||||
"navigation_bar.info": "Pri ĉiu tiu nodo",
|
||||
"navigation_bar.info": "Pri ĉi tiu nodo",
|
||||
"navigation_bar.keyboard_shortcuts": "Klavaraj mallongigoj",
|
||||
"navigation_bar.lists": "Listoj",
|
||||
"navigation_bar.logout": "Elsaluti",
|
||||
@@ -208,21 +213,22 @@
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "Nuligi",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Plusendi al {target}",
|
||||
"report.forward_hint": "La konto estas en alia servilo. Ĉu sendi sennomigitan kopion de la signalo ankaŭ tien?",
|
||||
"report.hint": "La signalo estos sendita al la kontrolantoj de via nodo. Vi povas doni klarigon pri kial vi signalas ĉi tiun konton sube:",
|
||||
"report.placeholder": "Pliaj komentoj",
|
||||
"report.submit": "Sendi",
|
||||
"report.target": "Signali {target}",
|
||||
"search.placeholder": "Serĉi",
|
||||
"search_popout.search_format": "Detala serĉo",
|
||||
"search_popout.tips.full_text": "Simplaj tekstoj montras la mesaĝojn, kiujn vi skribis, stelumis, diskonigis, aŭ en kiuj vi estis menciita, sed ankaŭ kongruajn uzantnomojn, montratajn nomojn, kaj kradvortojn.",
|
||||
"search_popout.tips.hashtag": "kradvorto",
|
||||
"search_popout.tips.status": "mesaĝoj",
|
||||
"search_popout.tips.text": "Simpla teksto montras la kongruajn afiŝitajn nomojn, uzantnomojn kaj kradvortojn",
|
||||
"search_popout.tips.user": "uzanto",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.accounts": "Homoj",
|
||||
"search_results.hashtags": "Kradvortoj",
|
||||
"search_results.statuses": "Mesaĝoj",
|
||||
"search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
|
||||
"standalone.public_title": "Enrigardo…",
|
||||
"status.block": "Bloki @{name}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Silentigi konversacion",
|
||||
"status.open": "Grandigi ĉi tiun mesaĝon",
|
||||
"status.pin": "Alpingli en la profilo",
|
||||
"status.pinned": "Alpinglita mesaĝo",
|
||||
"status.reblog": "Diskonigi",
|
||||
"status.reblogged_by": "{name} diskonigis",
|
||||
"status.reply": "Respondi",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Tikla enhavo",
|
||||
"status.share": "Diskonigi",
|
||||
"status.show_less": "Malgrandigi",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Grandigi",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Malsilentigi konversacion",
|
||||
"status.unpin": "Depingli de profilo",
|
||||
"tabs_bar.compose": "Ekskribi",
|
||||
"tabs_bar.federated_timeline": "Fratara tempolinio",
|
||||
"tabs_bar.home": "Hejmo",
|
||||
"tabs_bar.local_timeline": "Loka tempolinio",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Altreni kaj lasi por alŝuti",
|
||||
"upload_button.label": "Aldoni aŭdovidaĵon",
|
||||
"upload_form.description": "Priskribi por misvidantaj homoj",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Stuci",
|
||||
"upload_form.undo": "Malfari",
|
||||
"upload_progress.label": "Alŝutado…",
|
||||
"video.close": "Fermi videon",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloquear",
|
||||
"account.block_domain": "Ocultar todo de {domain}",
|
||||
"account.blocked": "Bloqueado",
|
||||
"account.disclaimer_full": "La siguiente información del usuario puede estar incompleta.",
|
||||
"account.domain_blocked": "Dominio oculto",
|
||||
"account.edit_profile": "Editar perfil",
|
||||
"account.follow": "Seguir",
|
||||
"account.followers": "Seguidores",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} se ha mudado a:",
|
||||
"account.mute": "Silenciar a @{name}",
|
||||
"account.mute_notifications": "Silenciar notificaciones de @{name}",
|
||||
"account.posts": "Publicaciones",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.muted": "Silenciado",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots con respuestas",
|
||||
"account.report": "Reportar a @{name}",
|
||||
"account.requested": "Esperando aprobación",
|
||||
"account.share": "Compartir el perfil de @{name}",
|
||||
@@ -51,14 +54,16 @@
|
||||
"column_header.unpin": "Dejar de fijar",
|
||||
"column_subheading.navigation": "Navegación",
|
||||
"column_subheading.settings": "Ajustes",
|
||||
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
|
||||
"compose_form.hashtag_warning": "Este toot no se mostrará bajo hashtags porque no es público. Sólo los toots públicos se pueden buscar por hashtag.",
|
||||
"compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.",
|
||||
"compose_form.lock_disclaimer.lock": "bloqueado",
|
||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
||||
"compose_form.publish": "Tootear",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marcar contenido como sensible",
|
||||
"compose_form.spoiler": "Ocultar texto tras una advertencia",
|
||||
"compose_form.sensitive.marked": "Material marcado como sensible",
|
||||
"compose_form.sensitive.unmarked": "Material no marcado como sensible",
|
||||
"compose_form.spoiler.marked": "Texto oculto tras la advertencia",
|
||||
"compose_form.spoiler.unmarked": "Texto no oculto",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
@@ -66,7 +71,7 @@
|
||||
"confirmations.delete.confirm": "Eliminar",
|
||||
"confirmations.delete.message": "¿Estás seguro de que quieres borrar este toot?",
|
||||
"confirmations.delete_list.confirm": "Delete",
|
||||
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
|
||||
"confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?",
|
||||
"confirmations.domain_block.confirm": "Ocultar dominio entero",
|
||||
"confirmations.domain_block.message": "¿Seguro de que quieres bloquear al dominio entero? En algunos casos es preferible bloquear o silenciar objetivos determinados.",
|
||||
"confirmations.mute.confirm": "Silenciar",
|
||||
@@ -129,18 +134,18 @@
|
||||
"lightbox.close": "Cerrar",
|
||||
"lightbox.next": "Siguiente",
|
||||
"lightbox.previous": "Anterior",
|
||||
"lists.account.add": "Add to list",
|
||||
"lists.account.remove": "Remove from list",
|
||||
"lists.account.add": "Añadir a lista",
|
||||
"lists.account.remove": "Quitar de lista",
|
||||
"lists.delete": "Delete list",
|
||||
"lists.edit": "Edit list",
|
||||
"lists.new.create": "Add list",
|
||||
"lists.new.title_placeholder": "New list title",
|
||||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"lists.edit": "Editar lista",
|
||||
"lists.new.create": "Añadir lista",
|
||||
"lists.new.title_placeholder": "Título de la nueva lista",
|
||||
"lists.search": "Buscar entre la gente a la que sigues",
|
||||
"lists.subheading": "Tus listas",
|
||||
"loading_indicator.label": "Cargando…",
|
||||
"media_gallery.toggle_visible": "Cambiar visibilidad",
|
||||
"missing_indicator.label": "No encontrado",
|
||||
"missing_indicator.sublabel": "This resource could not be found",
|
||||
"missing_indicator.sublabel": "No se encontró este recurso",
|
||||
"mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
|
||||
"navigation_bar.blocks": "Usuarios bloqueados",
|
||||
"navigation_bar.community_timeline": "Historia local",
|
||||
@@ -149,7 +154,7 @@
|
||||
"navigation_bar.follow_requests": "Solicitudes para seguirte",
|
||||
"navigation_bar.info": "Información adicional",
|
||||
"navigation_bar.keyboard_shortcuts": "Atajos de teclado",
|
||||
"navigation_bar.lists": "Lists",
|
||||
"navigation_bar.lists": "Listas",
|
||||
"navigation_bar.logout": "Cerrar sesión",
|
||||
"navigation_bar.mutes": "Usuarios silenciados",
|
||||
"navigation_bar.pins": "Toots fijados",
|
||||
@@ -176,8 +181,8 @@
|
||||
"onboarding.page_four.home": "La línea de tiempo principal muestra toots de gente que sigues.",
|
||||
"onboarding.page_four.notifications": "Las notificaciones se muestran cuando alguien interactúa contigo.",
|
||||
"onboarding.page_one.federation": "Mastodon es una red de servidores federados que conforman una red social aún más grande. Llamamos a estos servidores instancias.",
|
||||
"onboarding.page_one.full_handle": "Your full handle",
|
||||
"onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
|
||||
"onboarding.page_one.full_handle": "Tu sobrenombre completo",
|
||||
"onboarding.page_one.handle_hint": "Esto es lo que dirías a tus amistades que buscaran.",
|
||||
"onboarding.page_one.welcome": "¡Bienvenido a Mastodon!",
|
||||
"onboarding.page_six.admin": "El administrador de tu instancia es {admin}.",
|
||||
"onboarding.page_six.almost_done": "Ya casi…",
|
||||
@@ -200,27 +205,28 @@
|
||||
"privacy.public.short": "Público",
|
||||
"privacy.unlisted.long": "No mostrar en la historia federada",
|
||||
"privacy.unlisted.short": "Sin federar",
|
||||
"regeneration_indicator.label": "Loading…",
|
||||
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
|
||||
"regeneration_indicator.label": "Cargando…",
|
||||
"regeneration_indicator.sublabel": "¡Tu historia de inicio se está preparando!",
|
||||
"relative_time.days": "{number}d",
|
||||
"relative_time.hours": "{number}h",
|
||||
"relative_time.just_now": "ahora",
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Reenviar a {target}",
|
||||
"report.forward_hint": "Esta cuenta es de otro servidor. ¿Enviar una copia anonimizada del informe allí también?",
|
||||
"report.hint": "El informe se enviará a los moderadores de tu instancia. Puedes proporcionar una explicación de por qué informas sobre esta cuenta a continuación:",
|
||||
"report.placeholder": "Comentarios adicionales",
|
||||
"report.submit": "Publicar",
|
||||
"report.target": "Reportando",
|
||||
"search.placeholder": "Buscar",
|
||||
"search_popout.search_format": "Formato de búsqueda avanzada",
|
||||
"search_popout.tips.full_text": "Búsquedas de texto recuperan posts que has escrito, marcado como favoritos, retooteado o en los que has sido mencionado, así como usuarios, nombres y hashtags.",
|
||||
"search_popout.tips.hashtag": "etiqueta",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "El texto simple devuelve correspondencias de nombre, usuario y hashtag",
|
||||
"search_popout.tips.user": "usuario",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.accounts": "Gente",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
|
||||
@@ -234,10 +240,11 @@
|
||||
"status.media_hidden": "Contenido multimedia oculto",
|
||||
"status.mention": "Mencionar",
|
||||
"status.more": "Más",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute": "Silenciar @{name}",
|
||||
"status.mute_conversation": "Silenciar conversación",
|
||||
"status.open": "Expandir estado",
|
||||
"status.pin": "Fijar",
|
||||
"status.pinned": "Toot fijado",
|
||||
"status.reblog": "Retootear",
|
||||
"status.reblogged_by": "Retooteado por {name}",
|
||||
"status.reply": "Responder",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Contenido sensible",
|
||||
"status.share": "Compartir",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.show_less_all": "Mostrar menos para todo",
|
||||
"status.show_more": "Mostrar más",
|
||||
"status.show_more_all": "Mostrar más para todo",
|
||||
"status.unmute_conversation": "Dejar de silenciar conversación",
|
||||
"status.unpin": "Dejar de fijar",
|
||||
"tabs_bar.compose": "Redactar",
|
||||
"tabs_bar.federated_timeline": "Federado",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Arrastra y suelta para subir",
|
||||
"upload_button.label": "Subir multimedia",
|
||||
"upload_form.description": "Describir para los usuarios con dificultad visual",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Recortar",
|
||||
"upload_form.undo": "Deshacer",
|
||||
"upload_progress.label": "Subiendo…",
|
||||
"video.close": "Cerrar video",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "مسدودسازی @{name}",
|
||||
"account.block_domain": "پنهانسازی همه چیز از سرور {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "اطلاعات زیر ممکن است نمایهٔ این کاربر را به تمامی نشان ندهد.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "ویرایش نمایه",
|
||||
"account.follow": "پی بگیرید",
|
||||
"account.followers": "پیگیران",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} منتقل شده است به:",
|
||||
"account.mute": "بیصدا کردن @{name}",
|
||||
"account.mute_notifications": "بیصداکردن اعلانها از طرف @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "نوشتهها",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "گزارش @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "تازه چه خبر؟",
|
||||
"compose_form.publish": "بوق",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "تصاویر حساس هستند",
|
||||
"compose_form.spoiler": "نوشته را پشت هشدار پنهان کنید",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "هشدار محتوا",
|
||||
"confirmation_modal.cancel": "بیخیال",
|
||||
"confirmations.block.confirm": "مسدود کن",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "گزارشدادن",
|
||||
"search.placeholder": "جستجو",
|
||||
"search_popout.search_format": "راهنمای جستجوی پیشرفته",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "هشتگ",
|
||||
"search_popout.tips.status": "نوشته",
|
||||
"search_popout.tips.text": "جستجوی متنی ساده برای نامها، نامهای کاربری، و هشتگها",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "بیصداکردن گفتگو",
|
||||
"status.open": "این نوشته را باز کن",
|
||||
"status.pin": "نوشتهٔ ثابت نمایه",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "بازبوقیدن",
|
||||
"status.reblogged_by": "{name} بازبوقید",
|
||||
"status.reply": "پاسخ",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "محتوای حساس",
|
||||
"status.share": "همرسانی",
|
||||
"status.show_less": "نهفتن",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "نمایش",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "باصداکردن گفتگو",
|
||||
"status.unpin": "برداشتن نوشتهٔ ثابت نمایه",
|
||||
"tabs_bar.compose": "بنویسید",
|
||||
"tabs_bar.federated_timeline": "همگانی",
|
||||
"tabs_bar.home": "خانه",
|
||||
"tabs_bar.local_timeline": "محلی",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Estä @{name}",
|
||||
"account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
|
||||
"account.blocked": "Estetty",
|
||||
"account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.",
|
||||
"account.domain_blocked": "Verkko-osoite piilotettu",
|
||||
"account.edit_profile": "Muokkaa",
|
||||
"account.follow": "Seuraa",
|
||||
"account.followers": "Seuraajia",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} on muuttanut instanssiin:",
|
||||
"account.mute": "Mykistä @{name}",
|
||||
"account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}",
|
||||
"account.muted": "Mykistetty",
|
||||
"account.posts": "Töötit",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "Töötit ja vastaukset",
|
||||
"account.report": "Report @{name}",
|
||||
"account.requested": "Odottaa hyväksyntää. Klikkaa peruuttaaksesi seurauspyynnön",
|
||||
"account.share": "Jaa käyttäjän @{name} profiili",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Mitä sinulla on mielessä?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Merkitse media herkäksi",
|
||||
"compose_form.spoiler": "Piiloita teksti varoituksen taakse",
|
||||
"compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
|
||||
"compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
|
||||
"compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
|
||||
"compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
|
||||
"compose_form.spoiler_placeholder": "Content warning",
|
||||
"confirmation_modal.cancel": "Peruuta",
|
||||
"confirmations.block.confirm": "Estä",
|
||||
@@ -177,13 +182,13 @@
|
||||
"onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.",
|
||||
"onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.",
|
||||
"onboarding.page_one.full_handle": "Koko käyttäjänimesi",
|
||||
"onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
|
||||
"onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.",
|
||||
"onboarding.page_one.welcome": "Tervetuloa Mastodoniin!",
|
||||
"onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.",
|
||||
"onboarding.page_six.almost_done": "Melkein valmista...",
|
||||
"onboarding.page_six.appetoot": "Bon Appetööt!",
|
||||
"onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.",
|
||||
"onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
|
||||
"onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, pyytää ominaisuuksia tai osallistua kehittämiseen GitHub-palvelussa: {github}.",
|
||||
"onboarding.page_six.guidelines": "yhteisön säännöt",
|
||||
"onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!",
|
||||
"onboarding.page_six.various_app": "mobiilisovellukset",
|
||||
@@ -208,21 +213,22 @@
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "Peruuta",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Uudelleenohjaa kohteeseen {target}",
|
||||
"report.forward_hint": "Tämä tili on toiselta serveriltä. Haluatko, että myös sinne lähetetään anonymisoitu kopio ilmiantoraportista?",
|
||||
"report.hint": "Ilmianto lähetetään instanssisi moderaattoreille. Voit antaa kuvauksen käyttäjän ilmiantamisen syystä alle:",
|
||||
"report.placeholder": "Lisäkommentit",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
"search.placeholder": "Hae",
|
||||
"search_popout.search_format": "Tarkennettu haku",
|
||||
"search_popout.tips.full_text": "Tekstihaku palauttaa statuspäivitykset jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä käyttäjänimet, nimimerkit ja hastagit jotka sisältävät tekstin.",
|
||||
"search_popout.tips.hashtag": "hashtagi",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Pelkkä tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
|
||||
"search_popout.tips.user": "käyttäjä",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.accounts": "Ihmiset",
|
||||
"search_results.hashtags": "Hashtagit",
|
||||
"search_results.statuses": "Töötit",
|
||||
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"standalone.public_title": "Kurkistus sisälle...",
|
||||
"status.block": "Block @{name}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mykistä keskustelu",
|
||||
"status.open": "Laajenna statuspäivitys",
|
||||
"status.pin": "Kiinnitä profiiliin",
|
||||
"status.pinned": "Kiinnitetty töötti",
|
||||
"status.reblog": "Buustaa",
|
||||
"status.reblogged_by": "{name} buustasi",
|
||||
"status.reply": "Vastaa",
|
||||
@@ -247,11 +254,12 @@
|
||||
"status.sensitive_warning": "Arkaluontoista sisältöä",
|
||||
"status.share": "Jaa",
|
||||
"status.show_less": "Näytä vähemmän",
|
||||
"status.show_less_all": "Näytä vähemmän kaikista",
|
||||
"status.show_more": "Näytä lisää",
|
||||
"status.show_more_all": "Näytä enemmän kaikista",
|
||||
"status.unmute_conversation": "Poista mykistys keskustelulta",
|
||||
"status.unpin": "Irrota profiilista",
|
||||
"tabs_bar.compose": "Luo",
|
||||
"tabs_bar.federated_timeline": "Federated",
|
||||
"tabs_bar.federated_timeline": "Yleinen",
|
||||
"tabs_bar.home": "Koti",
|
||||
"tabs_bar.local_timeline": "Paikallinen",
|
||||
"tabs_bar.notifications": "Ilmoitukset",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Raahaa ja pudota tähän ladataksesi",
|
||||
"upload_button.label": "Lisää mediaa",
|
||||
"upload_form.description": "Anna kuvaus näkörajoitteisia varten",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Rajaa",
|
||||
"upload_form.undo": "Peru",
|
||||
"upload_progress.label": "Ladataan...",
|
||||
"video.close": "Sulje video",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloquer @{name}",
|
||||
"account.block_domain": "Tout masquer venant de {domain}",
|
||||
"account.blocked": "Bloqué",
|
||||
"account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.",
|
||||
"account.domain_blocked": "Domaine caché",
|
||||
"account.edit_profile": "Modifier le profil",
|
||||
"account.follow": "Suivre",
|
||||
"account.followers": "Abonné⋅e⋅s",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} a déménagé vers :",
|
||||
"account.mute": "Masquer @{name}",
|
||||
"account.mute_notifications": "Ignorer les notifications de @{name}",
|
||||
"account.posts": "Statuts",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.muted": "Silencé",
|
||||
"account.posts": "Pouets",
|
||||
"account.posts_with_replies": "Pouets avec réponses",
|
||||
"account.report": "Signaler",
|
||||
"account.requested": "Invitation envoyée",
|
||||
"account.share": "Partager le profil de @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
||||
"compose_form.publish": "Pouet",
|
||||
"compose_form.publish_loud": "{publish} !",
|
||||
"compose_form.sensitive": "Marquer le média comme sensible",
|
||||
"compose_form.spoiler": "Masquer le texte derrière un avertissement",
|
||||
"compose_form.sensitive.marked": "Média marqué comme sensible",
|
||||
"compose_form.sensitive.unmarked": "Média non marqué comme sensible",
|
||||
"compose_form.spoiler.marked": "Le texte est caché derrière un avertissement",
|
||||
"compose_form.spoiler.unmarked": "Le texte n'est pas caché",
|
||||
"compose_form.spoiler_placeholder": "Écrivez ici votre avertissement",
|
||||
"confirmation_modal.cancel": "Annuler",
|
||||
"confirmations.block.confirm": "Bloquer",
|
||||
@@ -208,21 +213,22 @@
|
||||
"relative_time.minutes": "{number} min",
|
||||
"relative_time.seconds": "{number} s",
|
||||
"reply_indicator.cancel": "Annuler",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Transférer à {target}",
|
||||
"report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport ?",
|
||||
"report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez ce compte ci-dessous :",
|
||||
"report.placeholder": "Commentaires additionnels",
|
||||
"report.submit": "Envoyer",
|
||||
"report.target": "Signalement",
|
||||
"search.placeholder": "Rechercher",
|
||||
"search_popout.search_format": "Recherche avancée",
|
||||
"search_popout.tips.full_text": "Les textes simples retournent les pouets que vous avez écris, mis en favori, épinglés, ou ayant été mentionnés, ainsi que les noms d'utilisateurs, les noms affichés, et les hashtags correspondant.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "statuts",
|
||||
"search_popout.tips.text": "Un texte simple renvoie les noms affichés, les noms d’utilisateur⋅ice et les hashtags correspondants",
|
||||
"search_popout.tips.user": "utilisateur⋅ice",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.accounts": "Personnes",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.statuses": "Pouets",
|
||||
"search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}",
|
||||
"standalone.public_title": "Jeter un coup d’œil…",
|
||||
"status.block": "Block @{name}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Masquer la conversation",
|
||||
"status.open": "Déplier ce statut",
|
||||
"status.pin": "Épingler sur le profil",
|
||||
"status.pinned": "Pouet épinglé",
|
||||
"status.reblog": "Partager",
|
||||
"status.reblogged_by": "{name} a partagé :",
|
||||
"status.reply": "Répondre",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Contenu sensible",
|
||||
"status.share": "Partager",
|
||||
"status.show_less": "Replier",
|
||||
"status.show_less_all": "Tout replier",
|
||||
"status.show_more": "Déplier",
|
||||
"status.show_more_all": "Tout déplier",
|
||||
"status.unmute_conversation": "Ne plus masquer la conversation",
|
||||
"status.unpin": "Retirer du profil",
|
||||
"tabs_bar.compose": "Composer",
|
||||
"tabs_bar.federated_timeline": "Fil public global",
|
||||
"tabs_bar.home": "Accueil",
|
||||
"tabs_bar.local_timeline": "Fil public local",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Glissez et déposez pour envoyer",
|
||||
"upload_button.label": "Joindre un média",
|
||||
"upload_form.description": "Décrire pour les malvoyants",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Recadrer",
|
||||
"upload_form.undo": "Annuler",
|
||||
"upload_progress.label": "Envoi en cours…",
|
||||
"video.close": "Fermer la vidéo",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloquear @{name}",
|
||||
"account.block_domain": "Ocultar calquer contido de {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Editar perfil",
|
||||
"account.follow": "Seguir",
|
||||
"account.followers": "Seguidoras",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} marchou a:",
|
||||
"account.mute": "Acalar @{name}",
|
||||
"account.mute_notifications": "Acalar as notificacións de @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Informar sobre @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "A qué andas?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marcar medios como sensibles",
|
||||
"compose_form.spoiler": "Agochar texto detrás de un aviso",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Escriba o aviso aquí",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Informar {target}",
|
||||
"search.placeholder": "Buscar",
|
||||
"search_popout.search_format": "Formato de busca avanzada",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "etiqueta",
|
||||
"search_popout.tips.status": "estado",
|
||||
"search_popout.tips.text": "Texto simple devolve coincidencias con nomes públicos, nomes de usuaria e etiquetas",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Acalar conversa",
|
||||
"status.open": "Expandir este estado",
|
||||
"status.pin": "Fixar no perfil",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Promover",
|
||||
"status.reblogged_by": "{name} promoveu",
|
||||
"status.reply": "Resposta",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Contido sensible",
|
||||
"status.share": "Compartir",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Mostrar máis",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Non acalar a conversa",
|
||||
"status.unpin": "Despegar do perfil",
|
||||
"tabs_bar.compose": "Compoñer",
|
||||
"tabs_bar.federated_timeline": "Federado",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "חסימת @{name}",
|
||||
"account.block_domain": "להסתיר הכל מהקהילה {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "המידע להלן עשוי להיות לא עדכני או לא שלם.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "עריכת פרופיל",
|
||||
"account.follow": "מעקב",
|
||||
"account.followers": "עוקבים",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "החשבון {name} הועבר אל:",
|
||||
"account.mute": "להשתיק את @{name}",
|
||||
"account.mute_notifications": "להסתיר התראות מאת @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "הודעות",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "לדווח על @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "מה עובר לך בראש?",
|
||||
"compose_form.publish": "ללחוש",
|
||||
"compose_form.publish_loud": "לחצרץ!",
|
||||
"compose_form.sensitive": "סימון תוכן כרגיש",
|
||||
"compose_form.spoiler": "הסתרה מאחורי אזהרת תוכן",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "אזהרת תוכן",
|
||||
"confirmation_modal.cancel": "ביטול",
|
||||
"confirmations.block.confirm": "לחסום",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "דיווח",
|
||||
"search.placeholder": "חיפוש",
|
||||
"search_popout.search_format": "מבנה חיפוש מתקדם",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "האשתג",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "טקסט פשוט מחזיר כינויים, שמות משתמש והאשתגים",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "השתקת שיחה",
|
||||
"status.open": "הרחבת הודעה",
|
||||
"status.pin": "לקבע באודות",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "הדהוד",
|
||||
"status.reblogged_by": "הודהד על ידי {name}",
|
||||
"status.reply": "תגובה",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "תוכן רגיש",
|
||||
"status.share": "שיתוף",
|
||||
"status.show_less": "הראה פחות",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "הראה יותר",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "הסרת השתקת שיחה",
|
||||
"status.unpin": "לשחרר מקיבוע באודות",
|
||||
"tabs_bar.compose": "חיבור",
|
||||
"tabs_bar.federated_timeline": "ציר זמן בין-קהילתי",
|
||||
"tabs_bar.home": "בבית",
|
||||
"tabs_bar.local_timeline": "ציר זמן מקומי",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blokiraj @{name}",
|
||||
"account.block_domain": "Sakrij sve sa {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Uredi profil",
|
||||
"account.follow": "Slijedi",
|
||||
"account.followers": "Sljedbenici",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.mute": "Utišaj @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Postovi",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Prijavi @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Što ti je na umu?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Označi media sadržaj kao osjetljiv",
|
||||
"compose_form.spoiler": "Sakrij text iza upozorenja",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Upozorenje o sadržaju",
|
||||
"confirmation_modal.cancel": "Otkaži",
|
||||
"confirmations.block.confirm": "Blokiraj",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Prijavljivanje",
|
||||
"search.placeholder": "Traži",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Utišaj razgovor",
|
||||
"status.open": "Proširi ovaj status",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Podigni",
|
||||
"status.reblogged_by": "{name} je podigao",
|
||||
"status.reply": "Odgovori",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Osjetljiv sadržaj",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Pokaži manje",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Pokaži više",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Poništi utišavanje razgovora",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Sastavi",
|
||||
"tabs_bar.federated_timeline": "Federalni",
|
||||
"tabs_bar.home": "Dom",
|
||||
"tabs_bar.local_timeline": "Lokalno",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "@{name} letiltása",
|
||||
"account.block_domain": "Minden elrejtése innen: {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Az alul található információk hiányosan mutathatják be a felhasználót.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Profil szerkesztése",
|
||||
"account.follow": "Követés",
|
||||
"account.followers": "Követők",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} átköltözött:",
|
||||
"account.mute": "@{name} némítása",
|
||||
"account.mute_notifications": "@{name} értesítések némítása",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Státuszok",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "@{name} jelentése",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Mire gondolsz?",
|
||||
"compose_form.publish": "Tülk",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Tartalom érzékenynek jelölése",
|
||||
"compose_form.spoiler": "Szöveg figyelmeztetés mögé rejtése",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Figyelmeztetését írja ide",
|
||||
"confirmation_modal.cancel": "Bezár",
|
||||
"confirmations.block.confirm": "Letilt",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Reporting",
|
||||
"search.placeholder": "Keresés",
|
||||
"search_popout.search_format": "Fejlett keresés",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Beszélgetés némítása",
|
||||
"status.open": "Státusz kinagyítása",
|
||||
"status.pin": "Kitűzés a profilra",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Reblog",
|
||||
"status.reblogged_by": "{name} reblogolta",
|
||||
"status.reply": "Válasz",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Érzékeny tartalom",
|
||||
"status.share": "Megosztás",
|
||||
"status.show_less": "Kevesebb",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Többet",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Beszélgetés némításának elvonása",
|
||||
"status.unpin": "Kitűzés eltávolítása a profilról",
|
||||
"tabs_bar.compose": "Összeállítás",
|
||||
"tabs_bar.federated_timeline": "Federált",
|
||||
"tabs_bar.home": "Kezdőlap",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Արգելափակել @{name}֊ին",
|
||||
"account.block_domain": "Թաքցնել ամենը հետեւյալ տիրույթից՝ {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Ներքոհիշյալը կարող է ոչ ամբողջությամբ արտացոլել օգտատիրոջ էջի տվյալները։",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Խմբագրել անձնական էջը",
|
||||
"account.follow": "Հետեւել",
|
||||
"account.followers": "Հետեւվողներ",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name}֊ը տեղափոխվել է՝",
|
||||
"account.mute": "Լռեցնել @{name}֊ին",
|
||||
"account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Գրառումներ",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Բողոքել @{name}֊ից",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Ի՞նչ կա մտքիդ",
|
||||
"compose_form.publish": "Թթել",
|
||||
"compose_form.publish_loud": "Թթե՜լ",
|
||||
"compose_form.sensitive": "Նշել բովանդակությունը որպես կասկածելի",
|
||||
"compose_form.spoiler": "Թաքցնել տեքստը նախազգուշացման ետեւում",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Գրիր նախազգուշացումդ այստեղ",
|
||||
"confirmation_modal.cancel": "Չեղարկել",
|
||||
"confirmations.block.confirm": "Արգելափակել",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Բողոքել {target}֊ի մասին",
|
||||
"search.placeholder": "Փնտրել",
|
||||
"search_popout.search_format": "Փնտրելու առաջադեմ ձեւ",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "պիտակ",
|
||||
"search_popout.tips.status": "թութ",
|
||||
"search_popout.tips.text": "Հասարակ տեքստը կվերադարձնի համընկնող անուններ, օգտանուններ ու պիտակներ",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Լռեցնել խոսակցությունը",
|
||||
"status.open": "Ընդարձակել այս թութը",
|
||||
"status.pin": "Ամրացնել անձնական էջում",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Տարածել",
|
||||
"status.reblogged_by": "{name} տարածել է",
|
||||
"status.reply": "Պատասխանել",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Կասկածելի բովանդակություն",
|
||||
"status.share": "Կիսվել",
|
||||
"status.show_less": "Պակաս",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Ավելին",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Ապալռեցնել խոսակցությունը",
|
||||
"status.unpin": "Հանել անձնական էջից",
|
||||
"tabs_bar.compose": "Շարադրել",
|
||||
"tabs_bar.federated_timeline": "Դաշնային",
|
||||
"tabs_bar.home": "Հիմնական",
|
||||
"tabs_bar.local_timeline": "Տեղական",
|
||||
|
@@ -1,128 +1,133 @@
|
||||
{
|
||||
"account.block": "Blokir @{name}",
|
||||
"account.block_domain": "Hide everything from {domain}",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.block_domain": "Sembunyikan segalanya dari {domain}",
|
||||
"account.blocked": "Terblokir",
|
||||
"account.disclaimer_full": "Informasi di bawah mungkin tidak mencerminkan profil user secara lengkap.",
|
||||
"account.domain_blocked": "Domain disembunyikan",
|
||||
"account.edit_profile": "Ubah profil",
|
||||
"account.follow": "Ikuti",
|
||||
"account.followers": "Pengikut",
|
||||
"account.follows": "Mengikuti",
|
||||
"account.follows_you": "Mengikuti anda",
|
||||
"account.hide_reblogs": "Hide boosts from @{name}",
|
||||
"account.hide_reblogs": "Sembunyikan boosts dari @{name}",
|
||||
"account.media": "Media",
|
||||
"account.mention": "Balasan @{name}",
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.moved_to": "{name} telah pindah ke:",
|
||||
"account.mute": "Bisukan @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.posts": "Postingan",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.mute_notifications": "Sembunyikan notifikasi dari @{name}",
|
||||
"account.muted": "Dibisukan",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Postingan dengan balasan",
|
||||
"account.report": "Laporkan @{name}",
|
||||
"account.requested": "Menunggu persetujuan",
|
||||
"account.share": "Share @{name}'s profile",
|
||||
"account.show_reblogs": "Show boosts from @{name}",
|
||||
"account.requested": "Menunggu persetujuan. Klik untuk membatalkan permintaan",
|
||||
"account.share": "Bagikan profil @{name}",
|
||||
"account.show_reblogs": "Tampilkan boost dari @{name}",
|
||||
"account.unblock": "Hapus blokir @{name}",
|
||||
"account.unblock_domain": "Unhide {domain}",
|
||||
"account.unblock_domain": "Tampilkan {domain}",
|
||||
"account.unfollow": "Berhenti mengikuti",
|
||||
"account.unmute": "Berhenti membisukan @{name}",
|
||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||
"account.view_full_profile": "View full profile",
|
||||
"account.unmute_notifications": "Munculkan notifikasi dari @{name}",
|
||||
"account.view_full_profile": "Lihat profil lengkap",
|
||||
"boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.",
|
||||
"bundle_column_error.retry": "Coba lagi",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"bundle_modal_error.close": "Tutup",
|
||||
"bundle_modal_error.message": "Kesalahan terjadi saat memuat komponen ini.",
|
||||
"bundle_modal_error.retry": "Coba lagi",
|
||||
"column.blocks": "Pengguna diblokir",
|
||||
"column.community": "Linimasa Lokal",
|
||||
"column.favourites": "Favorit",
|
||||
"column.follow_requests": "Permintaan mengikuti",
|
||||
"column.home": "Beranda",
|
||||
"column.lists": "Lists",
|
||||
"column.mutes": "Pengguna dibisukan",
|
||||
"column.lists": "List",
|
||||
"column.mutes": "Pengguna yang dibisukan",
|
||||
"column.notifications": "Notifikasi",
|
||||
"column.pins": "Pinned toot",
|
||||
"column.public": "Linimasa gabunggan",
|
||||
"column.public": "Linimasa gabungan",
|
||||
"column_back_button.label": "Kembali",
|
||||
"column_header.hide_settings": "Hide settings",
|
||||
"column_header.moveLeft_settings": "Move column to the left",
|
||||
"column_header.moveRight_settings": "Move column to the right",
|
||||
"column_header.pin": "Pin",
|
||||
"column_header.show_settings": "Show settings",
|
||||
"column_header.unpin": "Unpin",
|
||||
"column_header.hide_settings": "Sembunyikan pengaturan",
|
||||
"column_header.moveLeft_settings": "Pindahkan kolom ke kiri",
|
||||
"column_header.moveRight_settings": "Pindahkan kolom ke kanan",
|
||||
"column_header.pin": "Sematkan",
|
||||
"column_header.show_settings": "Tampilkan pengaturan",
|
||||
"column_header.unpin": "Lepaskan",
|
||||
"column_subheading.navigation": "Navigasi",
|
||||
"column_subheading.settings": "Pengaturan",
|
||||
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
|
||||
"compose_form.hashtag_warning": "Toot ini tidak akan ada dalam daftar tagar manapun karena telah di set sebagai tidak terdaftar. Hanya postingan publik yang bisa dicari dengan tagar.",
|
||||
"compose_form.lock_disclaimer": "Akun anda tidak {locked}. Semua orang dapat mengikuti anda untuk melihat postingan khusus untuk pengikut anda.",
|
||||
"compose_form.lock_disclaimer.lock": "dikunci",
|
||||
"compose_form.lock_disclaimer.lock": "terkunci",
|
||||
"compose_form.placeholder": "Apa yang ada di pikiran anda?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Tandai media sensitif",
|
||||
"compose_form.spoiler": "Sembunyikan teks dibalik peringatan",
|
||||
"compose_form.sensitive.marked": "Sumber ini telah ditandai sebagai sumber sensitif.",
|
||||
"compose_form.sensitive.unmarked": "Sumber ini tidak ditandai sebagai sumber sensitif",
|
||||
"compose_form.spoiler.marked": "Teks disembunyikan dibalik peringatan",
|
||||
"compose_form.spoiler.unmarked": "Teks tidak tersembunyi",
|
||||
"compose_form.spoiler_placeholder": "Peringatan konten",
|
||||
"confirmation_modal.cancel": "Batal",
|
||||
"confirmations.block.confirm": "Blokir",
|
||||
"confirmations.block.message": "Apa anda yakin ingin memblokir {name}?",
|
||||
"confirmations.delete.confirm": "Hapus",
|
||||
"confirmations.delete.message": "Apa anda yakin akan menghapus status ini?",
|
||||
"confirmations.delete.message": "Apa anda yakin untuk menghapus status ini?",
|
||||
"confirmations.delete_list.confirm": "Delete",
|
||||
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
|
||||
"confirmations.domain_block.confirm": "Hide entire domain",
|
||||
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
|
||||
"confirmations.delete_list.message": "Apakah anda yakin untuk menghapus daftar ini secara permanen?",
|
||||
"confirmations.domain_block.confirm": "Sembunyikan keseluruhan domain",
|
||||
"confirmations.domain_block.message": "Apakah anda benar benar yakin untuk memblokir keseluruhan {domain}? Dalam kasus tertentu beberapa pemblokiran atau penyembunyian lebih baik.",
|
||||
"confirmations.mute.confirm": "Bisukan",
|
||||
"confirmations.mute.message": "Apa anda yakin ingin membisukan {name}?",
|
||||
"confirmations.unfollow.confirm": "Unfollow",
|
||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||
"embed.instructions": "Embed this status on your website by copying the code below.",
|
||||
"embed.preview": "Here is what it will look like:",
|
||||
"confirmations.unfollow.confirm": "Berhenti mengikuti",
|
||||
"confirmations.unfollow.message": "Apakah anda ingin berhenti mengikuti {name}?",
|
||||
"embed.instructions": "Sematkan status ini di website anda dengan menyalin kode di bawah ini.",
|
||||
"embed.preview": "Seperti ini nantinya:",
|
||||
"emoji_button.activity": "Aktivitas",
|
||||
"emoji_button.custom": "Custom",
|
||||
"emoji_button.custom": "Kustom",
|
||||
"emoji_button.flags": "Bendera",
|
||||
"emoji_button.food": "Makanan & Minuman",
|
||||
"emoji_button.label": "Tambahkan emoji",
|
||||
"emoji_button.nature": "Alam",
|
||||
"emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
|
||||
"emoji_button.not_found": "Katakan tidak pada emoji!! (╯°□°)╯︵ ┻━┻",
|
||||
"emoji_button.objects": "Benda-benda",
|
||||
"emoji_button.people": "Orang",
|
||||
"emoji_button.recent": "Frequently used",
|
||||
"emoji_button.recent": "Yang sering digunakan",
|
||||
"emoji_button.search": "Cari...",
|
||||
"emoji_button.search_results": "Search results",
|
||||
"emoji_button.search_results": "Hasil pencarian",
|
||||
"emoji_button.symbols": "Simbol",
|
||||
"emoji_button.travel": "Tempat Wisata",
|
||||
"empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
|
||||
"empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
|
||||
"empty_column.home": "Anda sedang tidak mengikuti siapapun. Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
|
||||
"empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
|
||||
"empty_column.home.public_timeline": "linimasa publik",
|
||||
"empty_column.list": "There is nothing in this list yet.",
|
||||
"empty_column.list": "Tidak ada postingan di list ini. Ketika anggota dari list ini memposting status baru, status tersebut akan tampil disini.",
|
||||
"empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.",
|
||||
"empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisinya secara manual",
|
||||
"empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisi ini",
|
||||
"follow_request.authorize": "Izinkan",
|
||||
"follow_request.reject": "Tolak",
|
||||
"getting_started.appsshort": "Apps",
|
||||
"getting_started.appsshort": "Aplikasi",
|
||||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "Mulai",
|
||||
"getting_started.open_source_notice": "Mastodon adalah perangkat lunak yang bersifat open source. Anda dapat berkontribusi atau melaporkan permasalahan/bug di Github {github}.",
|
||||
"getting_started.userguide": "User Guide",
|
||||
"getting_started.open_source_notice": "Mastodon adalah perangkat lunak yang bersifat terbuka. Anda dapat berkontribusi atau melaporkan permasalahan/bug di Github {github}.",
|
||||
"getting_started.userguide": "Panduan Pengguna",
|
||||
"home.column_settings.advanced": "Tingkat Lanjut",
|
||||
"home.column_settings.basic": "Dasar",
|
||||
"home.column_settings.filter_regex": "Penyaringan dengan Regular Expression",
|
||||
"home.column_settings.show_reblogs": "Tampilkan Boost",
|
||||
"home.column_settings.filter_regex": "Saring dengan regular expressions",
|
||||
"home.column_settings.show_reblogs": "Tampilkan boost",
|
||||
"home.column_settings.show_replies": "Tampilkan balasan",
|
||||
"home.settings": "Pengaturan kolom",
|
||||
"keyboard_shortcuts.back": "to navigate back",
|
||||
"keyboard_shortcuts.boost": "to boost",
|
||||
"keyboard_shortcuts.column": "to focus a status in one of the columns",
|
||||
"keyboard_shortcuts.compose": "to focus the compose textarea",
|
||||
"keyboard_shortcuts.description": "Description",
|
||||
"keyboard_shortcuts.down": "to move down in the list",
|
||||
"keyboard_shortcuts.enter": "to open status",
|
||||
"keyboard_shortcuts.favourite": "to favourite",
|
||||
"keyboard_shortcuts.heading": "Keyboard Shortcuts",
|
||||
"keyboard_shortcuts.back": "untuk kembali",
|
||||
"keyboard_shortcuts.boost": "untuk menyebarkan",
|
||||
"keyboard_shortcuts.column": "untuk fokus kepada sebuah status di sebuah kolom",
|
||||
"keyboard_shortcuts.compose": "untuk fokus ke area penulisan",
|
||||
"keyboard_shortcuts.description": "Deskripsi",
|
||||
"keyboard_shortcuts.down": "untuk pindah ke bawah dalam sebuah daftar",
|
||||
"keyboard_shortcuts.enter": "untuk membuka status",
|
||||
"keyboard_shortcuts.favourite": "untuk memfavoritkan",
|
||||
"keyboard_shortcuts.heading": "Pintasan keyboard",
|
||||
"keyboard_shortcuts.hotkey": "Hotkey",
|
||||
"keyboard_shortcuts.legend": "to display this legend",
|
||||
"keyboard_shortcuts.mention": "to mention author",
|
||||
"keyboard_shortcuts.reply": "to reply",
|
||||
"keyboard_shortcuts.search": "to focus search",
|
||||
"keyboard_shortcuts.search": "untuk fokus mencari",
|
||||
"keyboard_shortcuts.toot": "to start a brand new toot",
|
||||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||
"keyboard_shortcuts.up": "to move up in the list",
|
||||
@@ -194,14 +199,14 @@
|
||||
"privacy.change": "Tentukan privasi status",
|
||||
"privacy.direct.long": "Kirim hanya ke pengguna yang disebut",
|
||||
"privacy.direct.short": "Langsung",
|
||||
"privacy.private.long": "Kirim hanya ke pengikut",
|
||||
"privacy.private.long": "Kirim postingan hanya kepada pengikut",
|
||||
"privacy.private.short": "Pribadi",
|
||||
"privacy.public.long": "Kirim ke linimasa publik",
|
||||
"privacy.public.short": "Publik",
|
||||
"privacy.unlisted.long": "Tidak ditampilkan di linimasa publik",
|
||||
"privacy.unlisted.short": "Tak Terdaftar",
|
||||
"regeneration_indicator.label": "Loading…",
|
||||
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
|
||||
"regeneration_indicator.sublabel": "Linimasa anda sedang disiapkan!",
|
||||
"relative_time.days": "{number}d",
|
||||
"relative_time.hours": "{number}h",
|
||||
"relative_time.just_now": "now",
|
||||
@@ -216,14 +221,15 @@
|
||||
"report.target": "Melaporkan",
|
||||
"search.placeholder": "Pencarian",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "tagar",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
"search_popout.tips.user": "user",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.total": "{count} {count, plural, one {hasil} other {hasil}}",
|
||||
"search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}",
|
||||
"standalone.public_title": "A look inside...",
|
||||
"status.block": "Block @{name}",
|
||||
"status.cannot_reblog": "This post cannot be boosted",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Tampilkan status ini",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblogged_by": "di-boost {name}",
|
||||
"status.reply": "Balas",
|
||||
@@ -247,24 +254,25 @@
|
||||
"status.sensitive_warning": "Konten sensitif",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Tampilkan lebih sedikit",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Tampilkan semua",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Unmute conversation",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Tulis",
|
||||
"tabs_bar.federated_timeline": "Gabungan",
|
||||
"tabs_bar.home": "Beranda",
|
||||
"tabs_bar.local_timeline": "Lokal",
|
||||
"tabs_bar.notifications": "Notifikasi",
|
||||
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
|
||||
"ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
|
||||
"upload_area.title": "Seret & lepaskan untuk mengunggah",
|
||||
"upload_button.label": "Tambahkan media",
|
||||
"upload_form.description": "Describe for the visually impaired",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.description": "Deskripsikan untuk mereka yang tidak bisa melihat dengan jelas",
|
||||
"upload_form.focus": "Potong",
|
||||
"upload_form.undo": "Undo",
|
||||
"upload_progress.label": "Mengunggah...",
|
||||
"video.close": "Close video",
|
||||
"video.exit_fullscreen": "Exit full screen",
|
||||
"video.expand": "Expand video",
|
||||
"video.exit_fullscreen": "Keluar dari layar penuh",
|
||||
"video.expand": "Perbesar video",
|
||||
"video.fullscreen": "Full screen",
|
||||
"video.hide": "Hide video",
|
||||
"video.mute": "Mute sound",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blokusar @{name}",
|
||||
"account.block_domain": "Hide everything from {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Modifikar profilo",
|
||||
"account.follow": "Sequar",
|
||||
"account.followers": "Sequanti",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.mute": "Celar @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Mesaji",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Denuncar @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Quo esas en tua spirito?",
|
||||
"compose_form.publish": "Siflar",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Markizar kontenajo kom trubliva",
|
||||
"compose_form.spoiler": "Celar texto dop averto",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Averto di kontenajo",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.confirm": "Block",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Denuncante",
|
||||
"search.placeholder": "Serchez",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Detaligar ca mesajo",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Repetar",
|
||||
"status.reblogged_by": "{name} repetita",
|
||||
"status.reply": "Respondar",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Trubliva kontenajo",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Montrar mine",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Montrar plue",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Unmute conversation",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Kompozar",
|
||||
"tabs_bar.federated_timeline": "Federata",
|
||||
"tabs_bar.home": "Hemo",
|
||||
"tabs_bar.local_timeline": "Lokala",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blocca @{name}",
|
||||
"account.block_domain": "Hide everything from {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Modifica profilo",
|
||||
"account.follow": "Segui",
|
||||
"account.followers": "Seguaci",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.mute": "Silenzia @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Posts",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Segnala @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "A cosa stai pensando?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Segnala file come sensibile",
|
||||
"compose_form.spoiler": "Nascondi testo con avvertimento",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Content warning",
|
||||
"confirmation_modal.cancel": "Cancel",
|
||||
"confirmations.block.confirm": "Block",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Invio la segnalazione",
|
||||
"search.placeholder": "Cerca",
|
||||
"search_popout.search_format": "Advanced search format",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Mute conversation",
|
||||
"status.open": "Espandi questo post",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Condividi",
|
||||
"status.reblogged_by": "{name} ha condiviso",
|
||||
"status.reply": "Rispondi",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Materiale sensibile",
|
||||
"status.share": "Share",
|
||||
"status.show_less": "Mostra meno",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Mostra di più",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Unmute conversation",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"tabs_bar.compose": "Scrivi",
|
||||
"tabs_bar.federated_timeline": "Federazione",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.local_timeline": "Locale",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "@{name}さんをブロック",
|
||||
"account.block_domain": "{domain}全体を非表示",
|
||||
"account.blocked": "ブロック済み",
|
||||
"account.disclaimer_full": "以下の情報は不正確な可能性があります。",
|
||||
"account.domain_blocked": "ドメイン非表示中",
|
||||
"account.edit_profile": "プロフィールを編集",
|
||||
"account.follow": "フォロー",
|
||||
"account.followers": "フォロワー",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name}さんは引っ越しました:",
|
||||
"account.mute": "@{name}さんをミュート",
|
||||
"account.mute_notifications": "@{name}さんからの通知を受け取らない",
|
||||
"account.muted": "ミュート済み",
|
||||
"account.posts": "投稿",
|
||||
"account.posts_with_replies": "トゥートと返信",
|
||||
"account.posts_with_replies": "投稿と返信",
|
||||
"account.report": "@{name}さんを通報",
|
||||
"account.requested": "フォロー承認待ちです。クリックしてキャンセル",
|
||||
"account.share": "@{name}さんのプロフィールを共有する",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "今なにしてる?",
|
||||
"compose_form.publish": "トゥート",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "メディアを閲覧注意としてマークする",
|
||||
"compose_form.spoiler": "テキストを隠す",
|
||||
"compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
|
||||
"compose_form.sensitive.unmarked": "メディアに閲覧注意が設定されていません",
|
||||
"compose_form.spoiler.marked": "閲覧注意が設定されています",
|
||||
"compose_form.spoiler.unmarked": "閲覧注意が設定されていません",
|
||||
"compose_form.spoiler_placeholder": "ここに警告を書いてください",
|
||||
"confirmation_modal.cancel": "キャンセル",
|
||||
"confirmations.block.confirm": "ブロック",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "{target}さんを通報する",
|
||||
"search.placeholder": "検索",
|
||||
"search_popout.search_format": "高度な検索フォーマット",
|
||||
"search_popout.tips.full_text": "表示名やユーザー名、ハッシュタグのほか、あなたのトゥートやお気に入り、ブーストしたトゥート、返信に一致する単純なテキスト。",
|
||||
"search_popout.tips.hashtag": "ハッシュタグ",
|
||||
"search_popout.tips.status": "トゥート",
|
||||
"search_popout.tips.text": "表示名やユーザー名、ハッシュタグに一致する単純なテキスト",
|
||||
@@ -238,8 +244,9 @@
|
||||
"status.mute_conversation": "会話をミュート",
|
||||
"status.open": "詳細を表示",
|
||||
"status.pin": "プロフィールに固定表示",
|
||||
"status.pinned": "固定されたトゥート",
|
||||
"status.reblog": "ブースト",
|
||||
"status.reblogged_by": "{name}さんにブーストされました",
|
||||
"status.reblogged_by": "{name}さんがブースト",
|
||||
"status.reply": "返信",
|
||||
"status.replyAll": "全員に返信",
|
||||
"status.report": "@{name}さんを通報",
|
||||
@@ -247,15 +254,16 @@
|
||||
"status.sensitive_warning": "閲覧注意",
|
||||
"status.share": "共有",
|
||||
"status.show_less": "隠す",
|
||||
"status.show_less_all": "全て隠す",
|
||||
"status.show_more": "もっと見る",
|
||||
"status.show_more_all": "全て見る",
|
||||
"status.unmute_conversation": "会話のミュートを解除",
|
||||
"status.unpin": "プロフィールの固定表示を解除",
|
||||
"tabs_bar.compose": "投稿",
|
||||
"tabs_bar.federated_timeline": "連合",
|
||||
"tabs_bar.home": "ホーム",
|
||||
"tabs_bar.local_timeline": "ローカル",
|
||||
"tabs_bar.notifications": "通知",
|
||||
"ui.beforeunload": "Mastodonから離れるとあなたのドラフトは失われます。",
|
||||
"ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
|
||||
"upload_area.title": "ドラッグ&ドロップでアップロード",
|
||||
"upload_button.label": "メディアを追加",
|
||||
"upload_form.description": "視覚障害者のための説明",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "@{name}을 차단",
|
||||
"account.block_domain": "{domain} 전체를 숨김",
|
||||
"account.blocked": "차단 됨",
|
||||
"account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.",
|
||||
"account.domain_blocked": "도메인 숨겨짐",
|
||||
"account.edit_profile": "프로필 편집",
|
||||
"account.follow": "팔로우",
|
||||
"account.followers": "팔로워",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name}는 계정을 이동했습니다:",
|
||||
"account.mute": "@{name} 뮤트",
|
||||
"account.mute_notifications": "@{name}의 알림을 뮤트",
|
||||
"account.muted": "뮤트 됨",
|
||||
"account.posts": "게시물",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "툿과 답장",
|
||||
"account.report": "@{name} 신고",
|
||||
"account.requested": "승인 대기 중. 클릭해서 취소하기",
|
||||
"account.share": "@{name}의 프로파일 공유",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "지금 무엇을 하고 있나요?",
|
||||
"compose_form.publish": "툿",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "이 미디어를 민감한 미디어로 취급",
|
||||
"compose_form.spoiler": "텍스트 숨기기",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "경고",
|
||||
"confirmation_modal.cancel": "취소",
|
||||
"confirmations.block.confirm": "차단",
|
||||
@@ -208,21 +213,22 @@
|
||||
"relative_time.minutes": "{number}분 전",
|
||||
"relative_time.seconds": "{number}초 전",
|
||||
"reply_indicator.cancel": "취소",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "{target}에 포워드 됨",
|
||||
"report.forward_hint": "이 계정은 다른 서버에 있습니다. 익명화 된 사본을 해당 서버에도 전송할까요?",
|
||||
"report.hint": "신고는 당신의 서버 스태프에게 전송 됩니다. 왜 이 계정을 신고하는 지에 대한 설명을 아래에 작성할 수 있습니다:",
|
||||
"report.placeholder": "코멘트",
|
||||
"report.submit": "신고하기",
|
||||
"report.target": "문제가 된 사용자",
|
||||
"search.placeholder": "검색",
|
||||
"search_popout.search_format": "고급 검색 방법",
|
||||
"search_popout.tips.full_text": "단순한 텍스트 검색은 당신이 작성했거나, 관심글로 지정했거나, 부스트했거나, 멘션을 받은 게시글, 그리고 유저네임, 디스플레이네임, 해시태그를 반환합니다.",
|
||||
"search_popout.tips.hashtag": "해시태그",
|
||||
"search_popout.tips.status": "툿",
|
||||
"search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다",
|
||||
"search_popout.tips.user": "유저",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.accounts": "사람",
|
||||
"search_results.hashtags": "해시태그",
|
||||
"search_results.statuses": "툿",
|
||||
"search_results.total": "{count, number}건의 결과",
|
||||
"standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
|
||||
"status.block": "@{name} 차단",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "이 대화를 뮤트",
|
||||
"status.open": "상세 정보 표시",
|
||||
"status.pin": "고정",
|
||||
"status.pinned": "고정 된 툿",
|
||||
"status.reblog": "부스트",
|
||||
"status.reblogged_by": "{name}님이 부스트 했습니다",
|
||||
"status.reply": "답장",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "민감한 미디어",
|
||||
"status.share": "공유",
|
||||
"status.show_less": "숨기기",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "더 보기",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "이 대화의 뮤트 해제하기",
|
||||
"status.unpin": "고정 해제",
|
||||
"tabs_bar.compose": "포스트",
|
||||
"tabs_bar.federated_timeline": "연합",
|
||||
"tabs_bar.home": "홈",
|
||||
"tabs_bar.local_timeline": "로컬",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "드래그 & 드롭으로 업로드",
|
||||
"upload_button.label": "미디어 추가",
|
||||
"upload_form.description": "시각장애인을 위한 설명",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "크롭",
|
||||
"upload_form.undo": "재시도",
|
||||
"upload_progress.label": "업로드 중...",
|
||||
"video.close": "동영상 닫기",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blokkeer @{name}",
|
||||
"account.block_domain": "Negeer alles van {domain}",
|
||||
"account.blocked": "Geblokkeerd",
|
||||
"account.disclaimer_full": "De informatie hieronder kan mogelijk een incompleet beeld geven van dit gebruikersprofiel.",
|
||||
"account.domain_blocked": "Domein verborgen",
|
||||
"account.edit_profile": "Profiel bewerken",
|
||||
"account.follow": "Volgen",
|
||||
"account.followers": "Volgers",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} is verhuisd naar:",
|
||||
"account.mute": "Negeer @{name}",
|
||||
"account.mute_notifications": "Negeer meldingen van @{name}",
|
||||
"account.muted": "Genegeerd",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.posts_with_replies": "Toots met reacties",
|
||||
"account.report": "Rapporteer @{name}",
|
||||
"account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren",
|
||||
"account.share": "Profiel van @{name} delen",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Wat wil je kwijt?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Media als gevoelig markeren (nsfw)",
|
||||
"compose_form.spoiler": "Tekst achter waarschuwing verbergen",
|
||||
"compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd",
|
||||
"compose_form.sensitive.unmarked": "Media is niet als gevoelig gemarkeerd",
|
||||
"compose_form.spoiler.marked": "Tekst is achter een waarschuwing verborgen",
|
||||
"compose_form.spoiler.unmarked": "Tekst is niet verborgen",
|
||||
"compose_form.spoiler_placeholder": "Waarschuwingstekst",
|
||||
"confirmation_modal.cancel": "Annuleren",
|
||||
"confirmations.block.confirm": "Blokkeren",
|
||||
@@ -208,19 +213,20 @@
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "Annuleren",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Doorsturen naar {target}",
|
||||
"report.forward_hint": "Het account bevindt zich op een andere server. Stuur daar eveneens een geanonimiseerde kopie van de gerapporteerde toot(s) naartoe?",
|
||||
"report.hint": "De gerapporteerde toot(s) worden naar de moderatoren van jouw server gestuurd. Je kunt hieronder een uitleg geven waarom je dit account rapporteert:",
|
||||
"report.placeholder": "Extra opmerkingen",
|
||||
"report.submit": "Verzenden",
|
||||
"report.target": "Rapporteer {target}",
|
||||
"search.placeholder": "Zoeken",
|
||||
"search_popout.search_format": "Geavanceerd zoeken",
|
||||
"search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken naar jouw toots, gebooste toots, favorieten en naar toots waarin jij bent vermeldt, en naar gebruikersnamen, weergavenamen en hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "toot",
|
||||
"search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags",
|
||||
"search_popout.tips.user": "gebruiker",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.accounts": "Gebruikers",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Negeer conversatie",
|
||||
"status.open": "Toot volledig tonen",
|
||||
"status.pin": "Aan profielpagina vastmaken",
|
||||
"status.pinned": "Vastgemaakte toot",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblogged_by": "{name} boostte",
|
||||
"status.reply": "Reageren",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Gevoelige inhoud",
|
||||
"status.share": "Delen",
|
||||
"status.show_less": "Minder tonen",
|
||||
"status.show_less_all": "Alles minder tonen",
|
||||
"status.show_more": "Meer tonen",
|
||||
"status.show_more_all": "Alles meer tonen",
|
||||
"status.unmute_conversation": "Conversatie niet meer negeren",
|
||||
"status.unpin": "Van profielpagina losmaken",
|
||||
"tabs_bar.compose": "Schrijven",
|
||||
"tabs_bar.federated_timeline": "Globaal",
|
||||
"tabs_bar.home": "Start",
|
||||
"tabs_bar.local_timeline": "Lokaal",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Hierin slepen om te uploaden",
|
||||
"upload_button.label": "Media toevoegen",
|
||||
"upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Bijsnijden",
|
||||
"upload_form.undo": "Ongedaan maken",
|
||||
"upload_progress.label": "Uploaden...",
|
||||
"video.close": "Video sluiten",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blokkér @{name}",
|
||||
"account.block_domain": "Skjul alt fra {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Informasjonen nedenfor kan gi et ufullstendig bilde av brukerens profil.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Rediger profil",
|
||||
"account.follow": "Følg",
|
||||
"account.followers": "Følgere",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} har flyttet til:",
|
||||
"account.mute": "Demp @{name}",
|
||||
"account.mute_notifications": "Ignorer varsler fra @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Innlegg",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Rapportér @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Hva har du på hjertet?",
|
||||
"compose_form.publish": "Tut",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Merk media som følsomt",
|
||||
"compose_form.spoiler": "Skjul tekst bak advarsel",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Innholdsadvarsel",
|
||||
"confirmation_modal.cancel": "Avbryt",
|
||||
"confirmations.block.confirm": "Blokkèr",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Rapporterer",
|
||||
"search.placeholder": "Søk",
|
||||
"search_popout.search_format": "Avansert søkeformat",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "emneknagg",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Enkel tekst returnerer matchende visningsnavn, brukernavn og emneknagger",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Demp samtale",
|
||||
"status.open": "Utvid denne statusen",
|
||||
"status.pin": "Fest på profilen",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Fremhev",
|
||||
"status.reblogged_by": "Fremhevd av {name}",
|
||||
"status.reply": "Svar",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Følsomt innhold",
|
||||
"status.share": "Del",
|
||||
"status.show_less": "Vis mindre",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Vis mer",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Ikke demp samtale",
|
||||
"status.unpin": "Angre festing på profilen",
|
||||
"tabs_bar.compose": "Komponer",
|
||||
"tabs_bar.federated_timeline": "Felles",
|
||||
"tabs_bar.home": "Hjem",
|
||||
"tabs_bar.local_timeline": "Lokal",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blocar @{name}",
|
||||
"account.block_domain": "Tot amagar del domeni {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Aquelas informacions de perfil pòdon èsser incomplètas.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Modificar lo perfil",
|
||||
"account.follow": "Sègre",
|
||||
"account.followers": "Seguidors",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} a mudat los catons a :",
|
||||
"account.mute": "Rescondre @{name}",
|
||||
"account.mute_notifications": "Rescondre las notificacions de @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Estatuts",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Senhalar @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "A de qué pensatz ?",
|
||||
"compose_form.publish": "Tut",
|
||||
"compose_form.publish_loud": "{publish} !",
|
||||
"compose_form.sensitive": "Marcar lo mèdia coma sensible",
|
||||
"compose_form.spoiler": "Rescondre lo tèxte darrièr un avertiment",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Escrivètz l’avertiment aquí",
|
||||
"confirmation_modal.cancel": "Anullar",
|
||||
"confirmations.block.confirm": "Blocar",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Senhalar {target}",
|
||||
"search.placeholder": "Recercar",
|
||||
"search_popout.search_format": "Format recèrca avançada",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "etiqueta",
|
||||
"search_popout.tips.status": "estatut",
|
||||
"search_popout.tips.text": "Lo tèxt brut tòrna escais, noms d’utilizaire e etiquetas correspondents",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Rescondre la conversacion",
|
||||
"status.open": "Desplegar aqueste estatut",
|
||||
"status.pin": "Penjar al perfil",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Partejar",
|
||||
"status.reblogged_by": "{name} a partejat",
|
||||
"status.reply": "Respondre",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Contengut sensible",
|
||||
"status.share": "Partejar",
|
||||
"status.show_less": "Tornar plegar",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Desplegar",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Tornar mostrar la conversacion",
|
||||
"status.unpin": "Tirar del perfil",
|
||||
"tabs_bar.compose": "Compausar",
|
||||
"tabs_bar.federated_timeline": "Flux public global",
|
||||
"tabs_bar.home": "Acuèlh",
|
||||
"tabs_bar.local_timeline": "Flux public local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Blokuj @{name}",
|
||||
"account.block_domain": "Blokuj wszystko z {domain}",
|
||||
"account.blocked": "Zablokowany",
|
||||
"account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.",
|
||||
"account.domain_blocked": "Ukryto domenę",
|
||||
"account.edit_profile": "Edytuj profil",
|
||||
"account.follow": "Śledź",
|
||||
"account.followers": "Śledzący",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} przeniósł się do:",
|
||||
"account.mute": "Wycisz @{name}",
|
||||
"account.mute_notifications": "Wycisz powiadomienia o @{name}",
|
||||
"account.muted": "Wyciszony",
|
||||
"account.posts": "Wpisy",
|
||||
"account.posts_with_replies": "Wpisy z odpowiedziami",
|
||||
"account.report": "Zgłoś @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Co Ci chodzi po głowie?",
|
||||
"compose_form.publish": "Wyślij",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Oznacz treści jako wrażliwe",
|
||||
"compose_form.spoiler": "Ukryj tekst za ostrzeżeniem",
|
||||
"compose_form.sensitive.marked": "Zawartość multimedia jest oznaczona jako wrażliwa",
|
||||
"compose_form.sensitive.unmarked": "Zawartość multimedialna nie jest oznaczona jako wrażliwa",
|
||||
"compose_form.spoiler.marked": "Tekst jest ukryty za ostrzeżeniem",
|
||||
"compose_form.spoiler.unmarked": "Tekst nie jest ukryty",
|
||||
"compose_form.spoiler_placeholder": "Wprowadź swoje ostrzeżenie o zawartości",
|
||||
"confirmation_modal.cancel": "Anuluj",
|
||||
"confirmations.block.confirm": "Zablokuj",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Zgłaszanie {target}",
|
||||
"search.placeholder": "Szukaj",
|
||||
"search_popout.search_format": "Zaawansowane wyszukiwanie",
|
||||
"search_popout.tips.full_text": "Pozwala na wyszukiwanie wpisów które napisałeś, dodałeś do ulubionych, podbiłeś w których o Tobie wspomniano, oraz pasujące nazwy użytkowników, pełne nazwy i hashtagi.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "wpis",
|
||||
"search_popout.tips.text": "Proste wyszukiwanie pasujących pseudonimów, nazw użytkowników i hashtagów",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Wycisz konwersację",
|
||||
"status.open": "Rozszerz ten wpis",
|
||||
"status.pin": "Przypnij do profilu",
|
||||
"status.pinned": "Przypięty wpis",
|
||||
"status.reblog": "Podbij",
|
||||
"status.reblogged_by": "{name} podbił",
|
||||
"status.reply": "Odpowiedz",
|
||||
@@ -246,11 +253,12 @@
|
||||
"status.sensitive_toggle": "Naciśnij aby wyświetlić",
|
||||
"status.sensitive_warning": "Wrażliwa zawartość",
|
||||
"status.share": "Udostępnij",
|
||||
"status.show_less": "Pokaż mniej",
|
||||
"status.show_more": "Pokaż więcej",
|
||||
"status.show_less": "Zwiń",
|
||||
"status.show_less_all": "Zwiń wszystkie",
|
||||
"status.show_more": "Rozwiń",
|
||||
"status.show_more_all": "Rozwiń wszystkie",
|
||||
"status.unmute_conversation": "Cofnij wyciszenie konwersacji",
|
||||
"status.unpin": "Odepnij z profilu",
|
||||
"tabs_bar.compose": "Napisz",
|
||||
"tabs_bar.federated_timeline": "Globalne",
|
||||
"tabs_bar.home": "Strona główna",
|
||||
"tabs_bar.local_timeline": "Lokalne",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Przeciągnij i upuść aby wysłać",
|
||||
"upload_button.label": "Dodaj zawartość multimedialną",
|
||||
"upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Przytnij",
|
||||
"upload_form.undo": "Cofnij",
|
||||
"upload_progress.label": "Wysyłanie",
|
||||
"video.close": "Zamknij film",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloquear @{name}",
|
||||
"account.block_domain": "Esconder tudo de {domain}",
|
||||
"account.blocked": "Bloqueado",
|
||||
"account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de maneira incompleta.",
|
||||
"account.domain_blocked": "Domínio escondido",
|
||||
"account.edit_profile": "Editar perfil",
|
||||
"account.follow": "Seguir",
|
||||
"account.followers": "Seguidores",
|
||||
@@ -13,8 +15,9 @@
|
||||
"account.moved_to": "{name} se mudou para:",
|
||||
"account.mute": "Silenciar @{name}",
|
||||
"account.mute_notifications": "Silenciar notificações de @{name}",
|
||||
"account.posts": "Posts",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.muted": "Silenciado",
|
||||
"account.posts": "Toots",
|
||||
"account.posts_with_replies": "Toots e respostas",
|
||||
"account.report": "Denunciar @{name}",
|
||||
"account.requested": "Aguardando aprovação. Clique para cancelar a solicitação",
|
||||
"account.share": "Compartilhar perfil de @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "No que você está pensando?",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marcar mídia como conteúdo sensível",
|
||||
"compose_form.spoiler": "Esconder texto com aviso de conteúdo",
|
||||
"compose_form.sensitive.marked": "Mídia está marcada como sensível",
|
||||
"compose_form.sensitive.unmarked": "Mídia não está marcada como sensível",
|
||||
"compose_form.spoiler.marked": "O texto está escondido por um aviso de conteúdo",
|
||||
"compose_form.spoiler.unmarked": "O texto não está escondido",
|
||||
"compose_form.spoiler_placeholder": "Aviso de conteúdo",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.confirm": "Bloquear",
|
||||
@@ -208,19 +213,20 @@
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"report.forward": "Forward to {target}",
|
||||
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
|
||||
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
|
||||
"report.forward": "Encaminhar para {target}",
|
||||
"report.forward_hint": "Essa conta pertence à um outro servidor. Encaminhar uma cópia da denúncia com seus dados tornados anônimos para esse servidor?",
|
||||
"report.hint": "A sua denúncia será enviada aos moderadores da instância. Você pode adicionar uma explicação de porque você está denunciando essa conta abaixo:",
|
||||
"report.placeholder": "Comentários adicionais",
|
||||
"report.submit": "Enviar",
|
||||
"report.target": "Denunciar",
|
||||
"search.placeholder": "Pesquisar",
|
||||
"search_popout.search_format": "Formato de busca avançado",
|
||||
"search_popout.tips.full_text": "Texto simples retorna status que você escreveu, favoritou, compartilhou ou em que tenha sido mencionado; também retorna nomes de exibição, usuários e hashtags correspondentes.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "Texto simples retorna nomes de exibição, usuários e hashtags correspondentes",
|
||||
"search_popout.tips.user": "usuário",
|
||||
"search_results.accounts": "People",
|
||||
"search_results.accounts": "Pessoas",
|
||||
"search_results.hashtags": "Hashtags",
|
||||
"search_results.statuses": "Toots",
|
||||
"search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Silenciar conversa",
|
||||
"status.open": "Expandir",
|
||||
"status.pin": "Fixar no perfil",
|
||||
"status.pinned": "Toot fixado",
|
||||
"status.reblog": "Compartilhar",
|
||||
"status.reblogged_by": "{name} compartilhou",
|
||||
"status.reply": "Responder",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Conteúdo sensível",
|
||||
"status.share": "Compartilhar",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.show_less_all": "Mostrar menos para todas as mensagens",
|
||||
"status.show_more": "Mostrar mais",
|
||||
"status.show_more_all": "Mostrar mais para todas as mensagens",
|
||||
"status.unmute_conversation": "Desativar silêncio desta conversa",
|
||||
"status.unpin": "Desafixar do perfil",
|
||||
"tabs_bar.compose": "Criar",
|
||||
"tabs_bar.federated_timeline": "Global",
|
||||
"tabs_bar.home": "Página inicial",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
@@ -259,7 +267,7 @@
|
||||
"upload_area.title": "Arraste e solte para enviar",
|
||||
"upload_button.label": "Adicionar mídia",
|
||||
"upload_form.description": "Descreva a imagem para deficientes visuais",
|
||||
"upload_form.focus": "Crop",
|
||||
"upload_form.focus": "Recortar",
|
||||
"upload_form.undo": "Desfazer",
|
||||
"upload_progress.label": "Salvando...",
|
||||
"video.close": "Fechar vídeo",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Bloquear @{name}",
|
||||
"account.block_domain": "Esconder tudo do domínio {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Editar perfil",
|
||||
"account.follow": "Seguir",
|
||||
"account.followers": "Seguidores",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "{name} mudou a sua conta para:",
|
||||
"account.mute": "Silenciar @{name}",
|
||||
"account.mute_notifications": "Silenciar notificações de @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Posts",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Denunciar @{name}",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "Em que estás a pensar?",
|
||||
"compose_form.publish": "Publicar",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marcar media como conteúdo sensível",
|
||||
"compose_form.spoiler": "Esconder texto com aviso",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Aviso de conteúdo",
|
||||
"confirmation_modal.cancel": "Cancelar",
|
||||
"confirmations.block.confirm": "Block",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Denunciar",
|
||||
"search.placeholder": "Pesquisar",
|
||||
"search_popout.search_format": "Formato avançado de pesquisa",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "hashtag",
|
||||
"search_popout.tips.status": "status",
|
||||
"search_popout.tips.text": "O texto simples retorna a correspondência de nomes, utilizadores e hashtags",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Silenciar conversa",
|
||||
"status.open": "Expandir",
|
||||
"status.pin": "Fixar no perfil",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Partilhar",
|
||||
"status.reblogged_by": "{name} partilhou",
|
||||
"status.reply": "Responder",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Conteúdo sensível",
|
||||
"status.share": "Compartilhar",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Mostrar mais",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Deixar de silenciar esta conversa",
|
||||
"status.unpin": "Não fixar no perfil",
|
||||
"tabs_bar.compose": "Criar",
|
||||
"tabs_bar.federated_timeline": "Global",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"account.block": "Блокировать",
|
||||
"account.block_domain": "Блокировать все с {domain}",
|
||||
"account.blocked": "Blocked",
|
||||
"account.disclaimer_full": "Нижеуказанная информация может не полностью отражать профиль пользователя.",
|
||||
"account.domain_blocked": "Domain hidden",
|
||||
"account.edit_profile": "Изменить профиль",
|
||||
"account.follow": "Подписаться",
|
||||
"account.followers": "Подписаны",
|
||||
@@ -13,6 +15,7 @@
|
||||
"account.moved_to": "Ищите {name} здесь:",
|
||||
"account.mute": "Заглушить",
|
||||
"account.mute_notifications": "Скрыть уведомления от @{name}",
|
||||
"account.muted": "Muted",
|
||||
"account.posts": "Посты",
|
||||
"account.posts_with_replies": "Toots with replies",
|
||||
"account.report": "Пожаловаться",
|
||||
@@ -57,8 +60,10 @@
|
||||
"compose_form.placeholder": "О чем Вы думаете?",
|
||||
"compose_form.publish": "Трубить",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Отметить как чувствительный контент",
|
||||
"compose_form.spoiler": "Скрыть текст за предупреждением",
|
||||
"compose_form.sensitive.marked": "Media is marked as sensitive",
|
||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"compose_form.spoiler_placeholder": "Напишите свое предупреждение здесь",
|
||||
"confirmation_modal.cancel": "Отмена",
|
||||
"confirmations.block.confirm": "Заблокировать",
|
||||
@@ -216,6 +221,7 @@
|
||||
"report.target": "Жалуемся на",
|
||||
"search.placeholder": "Поиск",
|
||||
"search_popout.search_format": "Продвинутый формат поиска",
|
||||
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
|
||||
"search_popout.tips.hashtag": "хэштег",
|
||||
"search_popout.tips.status": "статус",
|
||||
"search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги",
|
||||
@@ -238,6 +244,7 @@
|
||||
"status.mute_conversation": "Заглушить тред",
|
||||
"status.open": "Развернуть статус",
|
||||
"status.pin": "Закрепить в профиле",
|
||||
"status.pinned": "Pinned toot",
|
||||
"status.reblog": "Продвинуть",
|
||||
"status.reblogged_by": "{name} продвинул(а)",
|
||||
"status.reply": "Ответить",
|
||||
@@ -247,10 +254,11 @@
|
||||
"status.sensitive_warning": "Чувствительный контент",
|
||||
"status.share": "Поделиться",
|
||||
"status.show_less": "Свернуть",
|
||||
"status.show_less_all": "Show less for all",
|
||||
"status.show_more": "Развернуть",
|
||||
"status.show_more_all": "Show more for all",
|
||||
"status.unmute_conversation": "Снять глушение с треда",
|
||||
"status.unpin": "Открепить от профиля",
|
||||
"tabs_bar.compose": "Написать",
|
||||
"tabs_bar.federated_timeline": "Глобальная",
|
||||
"tabs_bar.home": "Главная",
|
||||
"tabs_bar.local_timeline": "Локальная",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user