Browse Source

Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- .github/ISSUE_TEMPLATE/bug_report.md
  Took our version.
- CONTRIBUTING.md
  Updated the embedded copy of upstream's version.
- README.md
  Took our version.
- app/policies/status_policy.rb
  Not a real conflict, took code from both.
- app/views/layouts/embedded.html.haml
  Added upstream's changes (dns-prefetch) and fixed
  `%body.embed`
- app/views/settings/preferences/show.html.haml
  Reverted some of upstream changes, as we have a
  page dedicated for flavours and skins.
- config/initializers/content_security_policy.rb
  Kept our version of the CSP.
- config/initializers/doorkeeper.rb
  Not a real conflict, took code from both.
fox-glitch-changes
Thibaut Girka 10 months ago
parent
commit
dcded13a99
100 changed files with 1232 additions and 667 deletions
  1. 3
    0
      .circleci/config.yml
  2. 0
    4
      .env.test
  3. 10
    4
      .github/ISSUE_TEMPLATE/feature_request.md
  4. 10
    0
      .github/ISSUE_TEMPLATE/support.md
  5. 1
    1
      .ruby-version
  6. 52
    31
      AUTHORS.md
  7. 107
    0
      CHANGELOG.md
  8. 22
    40
      CONTRIBUTING.md
  9. 4
    4
      Gemfile
  10. 15
    15
      Gemfile.lock
  11. 1
    1
      app/controllers/admin/domain_blocks_controller.rb
  12. 9
    0
      app/controllers/admin/reports_controller.rb
  13. 18
    2
      app/controllers/api/v1/conversations_controller.rb
  14. 0
    1
      app/controllers/api/v1/reports_controller.rb
  15. 3
    3
      app/helpers/application_helper.rb
  16. 8
    13
      app/javascript/core/admin.js
  17. 15
    14
      app/javascript/mastodon/actions/compose.js
  18. 22
    0
      app/javascript/mastodon/actions/conversations.js
  19. 96
    0
      app/javascript/mastodon/components/avatar_composite.js
  20. 12
    8
      app/javascript/mastodon/components/display_name.js
  21. 17
    6
      app/javascript/mastodon/components/status.js
  22. 5
    1
      app/javascript/mastodon/features/compose/components/compose_form.js
  23. 1
    1
      app/javascript/mastodon/features/compose/components/privacy_dropdown.js
  24. 5
    1
      app/javascript/mastodon/features/compose/components/upload.js
  25. 2
    2
      app/javascript/mastodon/features/compose/containers/compose_form_container.js
  26. 2
    2
      app/javascript/mastodon/features/compose/containers/upload_container.js
  27. 2
    1
      app/javascript/mastodon/features/compose/index.js
  28. 20
    42
      app/javascript/mastodon/features/direct_timeline/components/conversation.js
  29. 8
    4
      app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js
  30. 4
    1
      app/javascript/mastodon/features/direct_timeline/index.js
  31. 1
    1
      app/javascript/mastodon/features/status/index.js
  32. 1
    1
      app/javascript/mastodon/features/ui/index.js
  33. 1
    0
      app/javascript/mastodon/initial_state.js
  34. 10
    6
      app/javascript/mastodon/locales/ar.json
  35. 4
    0
      app/javascript/mastodon/locales/ast.json
  36. 4
    0
      app/javascript/mastodon/locales/bg.json
  37. 4
    0
      app/javascript/mastodon/locales/ca.json
  38. 4
    0
      app/javascript/mastodon/locales/co.json
  39. 6
    2
      app/javascript/mastodon/locales/cs.json
  40. 338
    333
      app/javascript/mastodon/locales/cy.json
  41. 4
    0
      app/javascript/mastodon/locales/da.json
  42. 4
    0
      app/javascript/mastodon/locales/de.json
  43. 9
    0
      app/javascript/mastodon/locales/defaultMessages.json
  44. 5
    1
      app/javascript/mastodon/locales/el.json
  45. 1
    0
      app/javascript/mastodon/locales/en.json
  46. 4
    0
      app/javascript/mastodon/locales/eo.json
  47. 4
    0
      app/javascript/mastodon/locales/es.json
  48. 4
    0
      app/javascript/mastodon/locales/eu.json
  49. 5
    1
      app/javascript/mastodon/locales/fa.json
  50. 4
    0
      app/javascript/mastodon/locales/fi.json
  51. 5
    1
      app/javascript/mastodon/locales/fr.json
  52. 4
    0
      app/javascript/mastodon/locales/gl.json
  53. 4
    0
      app/javascript/mastodon/locales/he.json
  54. 4
    0
      app/javascript/mastodon/locales/hr.json
  55. 4
    0
      app/javascript/mastodon/locales/hu.json
  56. 4
    0
      app/javascript/mastodon/locales/hy.json
  57. 4
    0
      app/javascript/mastodon/locales/id.json
  58. 4
    0
      app/javascript/mastodon/locales/io.json
  59. 4
    0
      app/javascript/mastodon/locales/it.json
  60. 5
    4
      app/javascript/mastodon/locales/ja.json
  61. 4
    0
      app/javascript/mastodon/locales/ka.json
  62. 4
    0
      app/javascript/mastodon/locales/ko.json
  63. 4
    0
      app/javascript/mastodon/locales/nl.json
  64. 4
    0
      app/javascript/mastodon/locales/no.json
  65. 4
    0
      app/javascript/mastodon/locales/oc.json
  66. 1
    0
      app/javascript/mastodon/locales/pl.json
  67. 4
    0
      app/javascript/mastodon/locales/pt-BR.json
  68. 4
    0
      app/javascript/mastodon/locales/pt.json
  69. 4
    0
      app/javascript/mastodon/locales/ro.json
  70. 4
    0
      app/javascript/mastodon/locales/ru.json
  71. 4
    0
      app/javascript/mastodon/locales/sk.json
  72. 4
    0
      app/javascript/mastodon/locales/sl.json
  73. 4
    0
      app/javascript/mastodon/locales/sr-Latn.json
  74. 4
    0
      app/javascript/mastodon/locales/sr.json
  75. 4
    0
      app/javascript/mastodon/locales/sv.json
  76. 4
    0
      app/javascript/mastodon/locales/ta.json
  77. 4
    0
      app/javascript/mastodon/locales/te.json
  78. 4
    0
      app/javascript/mastodon/locales/th.json
  79. 4
    0
      app/javascript/mastodon/locales/tr.json
  80. 4
    0
      app/javascript/mastodon/locales/uk.json
  81. 4
    0
      app/javascript/mastodon/locales/zh-CN.json
  82. 4
    0
      app/javascript/mastodon/locales/zh-HK.json
  83. 4
    0
      app/javascript/mastodon/locales/zh-TW.json
  84. 17
    0
      app/javascript/mastodon/reducers/conversations.js
  85. 0
    14
      app/javascript/styles/mastodon-light/diff.scss
  86. 14
    43
      app/javascript/styles/mastodon/components.scss
  87. 14
    3
      app/javascript/styles/mastodon/forms.scss
  88. 55
    4
      app/javascript/styles/mastodon/rtl.scss
  89. 0
    1
      app/javascript/styles/mastodon/stream_entries.scss
  90. 1
    1
      app/lib/activitypub/activity.rb
  91. 74
    33
      app/lib/activitypub/activity/create.rb
  92. 8
    0
      app/lib/activitypub/activity/flag.rb
  93. 3
    3
      app/lib/activitypub/tag_manager.rb
  94. 4
    4
      app/lib/feed_manager.rb
  95. 1
    1
      app/lib/formatter.rb
  96. 1
    1
      app/lib/ostatus/atom_serializer.rb
  97. 3
    1
      app/models/account_conversation.rb
  98. 7
    6
      app/models/domain_block.rb
  99. 8
    0
      app/models/mention.rb
  100. 0
    0
      app/models/notification.rb

+ 3
- 0
.circleci/config.yml View File

@@ -13,6 +13,9 @@ aliases:
ALLOW_NOPAM: true
CONTINUOUS_INTEGRATION: true
DISABLE_SIMPLECOV: true
PAM_ENABLED: true
PAM_DEFAULT_SERVICE: pam_test
PAM_CONTROLLED_SERVICE: pam_test_controlled
working_directory: ~/projects/mastodon/

- &attach_workspace

+ 0
- 4
.env.test View File

@@ -3,7 +3,3 @@ NODE_ENV=test
# Federation
LOCAL_DOMAIN=cb6e6126.ngrok.io
LOCAL_HTTPS=true
# test pam authentication
PAM_ENABLED=true
PAM_DEFAULT_SERVICE=pam_test
PAM_CONTROLLED_SERVICE=pam_test_controlled

+ 10
- 4
.github/ISSUE_TEMPLATE/feature_request.md View File

@@ -1,11 +1,17 @@
---
name: Feature Request
about: Suggest an idea for this project
about: I have a suggestion

---

[Issue text goes here].
<!-- Please use a concise and distinct title for the issue -->

* * * *
<!-- Consider: Could it be implemented as a 3rd party app using the REST API instead? -->

- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
### Pitch

<!-- Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before -->

### Motivation

<!-- Why do you think this feature is needed? Who would benefit from it? -->

+ 10
- 0
.github/ISSUE_TEMPLATE/support.md View File

@@ -0,0 +1,10 @@
---
name: Support
about: Ask for help with your deployment

---

We primarily use GitHub as a bug and feature tracker. For usage questions, troubleshooting of deployments and other individual technical assistance, please use one of the resources below:

- https://discourse.joinmastodon.org
- #mastodon on irc.freenode.net

+ 1
- 1
.ruby-version View File

@@ -1 +1 @@
2.5.1
2.5.3

+ 52
- 31
AUTHORS.md View File

@@ -5,44 +5,46 @@ and provided thanks to the work of the following contributors:
* [ykzts](https://github.com/ykzts)
* [akihikodaki](https://github.com/akihikodaki)
* [mjankowski](https://github.com/mjankowski)
* [unarist](https://github.com/unarist)
* [ThibG](https://github.com/ThibG)
* [unarist](https://github.com/unarist)
* [m4sk1n](https://github.com/m4sk1n)
* [yiskah](https://github.com/yiskah)
* [nolanlawson](https://github.com/nolanlawson)
* [sorin-davidoi](https://github.com/sorin-davidoi)
* [abcang](https://github.com/abcang)
* [lynlynlynx](https://github.com/lynlynlynx)
* [dependabot[bot]](https://github.com/apps/dependabot)
* [alpaca-tc](https://github.com/alpaca-tc)
* [nclm](https://github.com/nclm)
* [ineffyble](https://github.com/ineffyble)
* [renatolond](https://github.com/renatolond)
* [jeroenpraat](https://github.com/jeroenpraat)
* [mayaeh](https://github.com/mayaeh)
* [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)
* [mayaeh](https://github.com/mayaeh)
* [ysksn](https://github.com/ysksn)
* [shuheiktgw](https://github.com/shuheiktgw)
* [ashfurrow](https://github.com/ashfurrow)
* [mabkenar](https://github.com/mabkenar)
* [zunda](https://github.com/zunda)
* [eramdam](https://github.com/eramdam)
* [Kjwon15](https://github.com/Kjwon15)
* [eramdam](https://github.com/eramdam)
* [masarakki](https://github.com/masarakki)
* [ticky](https://github.com/ticky)
* [takayamaki](https://github.com/takayamaki)
* [Quenty31](https://github.com/Quenty31)
* [danhunsaker](https://github.com/danhunsaker)
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
* [hcmiya](https://github.com/hcmiya)
* [stephenburgess8](https://github.com/stephenburgess8)
* [Wonderfall](https://github.com/Wonderfall)
* [takayamaki](https://github.com/takayamaki)
* [matteoaquila](https://github.com/matteoaquila)
* [rkarabut](https://github.com/rkarabut)
* [Artoria2e5](https://github.com/Artoria2e5)
* [yukimochi](https://github.com/yukimochi)
* [Artoria2e5](https://github.com/Artoria2e5)
* [marrus-sh](https://github.com/marrus-sh)
* [krainboltgreene](https://github.com/krainboltgreene)
* [patf](https://github.com/patf)
@@ -56,7 +58,7 @@ and provided thanks to the work of the following contributors:
* [MasterGroosha](https://github.com/MasterGroosha)
* [JeanGauthier](https://github.com/JeanGauthier)
* [kschaper](https://github.com/kschaper)
* [mabkenar](https://github.com/mabkenar)
* [MaciekBaron](https://github.com/MaciekBaron)
* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
* [beatrix-bitrot](https://github.com/beatrix-bitrot)
* [adbelle](https://github.com/adbelle)
@@ -64,9 +66,9 @@ and provided thanks to the work of the following contributors:
* [MightyPork](https://github.com/MightyPork)
* [yhirano55](https://github.com/yhirano55)
* [camponez](https://github.com/camponez)
* [MaciekBaron](https://github.com/MaciekBaron)
* [SerCom-KC](https://github.com/SerCom-KC)
* [aschmitz](https://github.com/aschmitz)
* [devkral](https://github.com/devkral)
* [fpiesche](https://github.com/fpiesche)
* [gandaro](https://github.com/gandaro)
* [johnsudaar](https://github.com/johnsudaar)
@@ -75,8 +77,6 @@ and provided thanks to the work of the following contributors:
* [lindwurm](https://github.com/lindwurm)
* [victorhck](mailto:victorhck@geeko.site)
* [voidsatisfaction](https://github.com/voidsatisfaction)
* [valentin2105](https://github.com/valentin2105)
* [devkral](https://github.com/devkral)
* [hikari-no-yume](https://github.com/hikari-no-yume)
* [angristan](https://github.com/angristan)
* [seefood](https://github.com/seefood)
@@ -93,6 +93,7 @@ and provided thanks to the work of the following contributors:
* [tsuwatch](https://github.com/tsuwatch)
* [victorhck](https://github.com/victorhck)
* [puckipedia](https://github.com/puckipedia)
* [fvh-P](https://github.com/fvh-P)
* [contraexemplo](https://github.com/contraexemplo)
* [hugogameiro](https://github.com/hugogameiro)
* [kazu9su](https://github.com/kazu9su)
@@ -102,11 +103,11 @@ and provided thanks to the work of the following contributors:
* [Neetshin](mailto:neetshin@neetsh.in)
* [rainyday](https://github.com/rainyday)
* [ProgVal](https://github.com/ProgVal)
* [valentin2105](https://github.com/valentin2105)
* [yuntan](https://github.com/yuntan)
* [goofy-bz](mailto:goofy@babelzilla.org)
* [kadiix](https://github.com/kadiix)
* [kodacs](https://github.com/kodacs)
* [fvh-P](https://github.com/fvh-P)
* [rtucker](https://github.com/rtucker)
* [KScl](https://github.com/KScl)
* [sterdev](https://github.com/sterdev)
@@ -116,6 +117,7 @@ and provided thanks to the work of the following contributors:
* [cpytel](https://github.com/cpytel)
* [northerner](https://github.com/northerner)
* [fhemberger](https://github.com/fhemberger)
* [greysteil](https://github.com/greysteil)
* [hnrysmth](https://github.com/hnrysmth)
* [d6rkaiz](https://github.com/d6rkaiz)
* [JMendyk](https://github.com/JMendyk)
@@ -125,12 +127,14 @@ and provided thanks to the work of the following contributors:
* [reneklacan](https://github.com/reneklacan)
* [ekiru](https://github.com/ekiru)
* [tcitworld](https://github.com/tcitworld)
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
* [geta6](https://github.com/geta6)
* [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)
* [theboss](https://github.com/theboss)
* [178inaba](https://github.com/178inaba)
* [alyssais](https://github.com/alyssais)
* [kodnaplakal](https://github.com/kodnaplakal)
@@ -140,6 +144,7 @@ and provided thanks to the work of the following contributors:
* [halkeye](https://github.com/halkeye)
* [hinaloe](https://github.com/hinaloe)
* [treby](https://github.com/treby)
* [Reverite](https://github.com/Reverite)
* [jpdevries](https://github.com/jpdevries)
* [00x9d](https://github.com/00x9d)
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
@@ -147,15 +152,16 @@ and provided thanks to the work of the following contributors:
* [nevillepark](https://github.com/nevillepark)
* [ornithocoder](https://github.com/ornithocoder)
* [pierreozoux](https://github.com/pierreozoux)
* [qguv](https://github.com/qguv)
* [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
* [harukasan](https://github.com/harukasan)
* [stamak](https://github.com/stamak)
* [theboss](https://github.com/theboss)
* [Technowix](mailto:technowix@users.noreply.github.com)
* [Eychics](https://github.com/Eychics)
* [Thor Harald Johansen](mailto:thj@thj.no)
* [0x70b1a5](https://github.com/0x70b1a5)
* [gled-rs](https://github.com/gled-rs)
* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
* [R0ckweb](https://github.com/R0ckweb)
* [caasi](https://github.com/caasi)
* [esetomo](https://github.com/esetomo)
@@ -168,6 +174,7 @@ and provided thanks to the work of the following contributors:
* [vahnj](https://github.com/vahnj)
* [ikuradon](https://github.com/ikuradon)
* [AndreLewin](https://github.com/AndreLewin)
* [rinsuki](https://github.com/rinsuki)
* [redtachyons](https://github.com/redtachyons)
* [thurloat](https://github.com/thurloat)
* [aaribaud](https://github.com/aaribaud)
@@ -188,14 +195,12 @@ and provided thanks to the work of the following contributors:
* [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)
* [immae](https://github.com/immae)
* [Reverite](https://github.com/Reverite)
* [foozmeat](https://github.com/foozmeat)
* [jasonrhodes](https://github.com/jasonrhodes)
* [Jason Snell](mailto:jason@newrelic.com)
@@ -232,6 +237,8 @@ and provided thanks to the work of the following contributors:
* [zacanger](https://github.com/zacanger)
* [amazedkoumei](https://github.com/amazedkoumei)
* [anon5r](https://github.com/anon5r)
* [aus-social](https://github.com/aus-social)
* [imbsky](https://github.com/imbsky)
* [bsky](mailto:me@imbsky.net)
* [chr-1x](https://github.com/chr-1x)
* [codl](https://github.com/codl)
@@ -249,12 +256,12 @@ and provided thanks to the work of the following contributors:
* [oliverkeeble](https://github.com/oliverkeeble)
* [pinfort](https://github.com/pinfort)
* [rbaumert](https://github.com/rbaumert)
* [rhoio](https://github.com/rhoio)
* [trwnh](https://github.com/trwnh)
* [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)
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
* [cygnan](https://github.com/cygnan)
* [Awea](https://github.com/Awea)
@@ -282,6 +289,7 @@ and provided thanks to the work of the following contributors:
* [ameliavoncat](https://github.com/ameliavoncat)
* [ilpianista](https://github.com/ilpianista)
* [Andreas Drop](mailto:andy@remline.de)
* [andi1984](https://github.com/andi1984)
* [schas002](https://github.com/schas002)
* [abackstrom](https://github.com/abackstrom)
* [jumbosushi](https://github.com/jumbosushi)
@@ -303,8 +311,9 @@ and provided thanks to the work of the following contributors:
* [chriswk](https://github.com/chriswk)
* [csu](https://github.com/csu)
* [kklleemm](https://github.com/kklleemm)
* [colindean](https://github.com/colindean)
* [dachinat](https://github.com/dachinat)
* [monsterpit-daggertooth](https://github.com/monsterpit-daggertooth)
* [multiple-creatures](https://github.com/multiple-creatures)
* [watilde](https://github.com/watilde)
* [daprice](https://github.com/daprice)
* [dar5hak](https://github.com/dar5hak)
@@ -328,14 +337,17 @@ and provided thanks to the work of the following contributors:
* [espenronnevik](https://github.com/espenronnevik)
* [Finariel](https://github.com/Finariel)
* [siuying](https://github.com/siuying)
* [GenbuHase](https://github.com/GenbuHase)
* [hattori6789](https://github.com/hattori6789)
* [algernon](https://github.com/algernon)
* [Fastbyte01](https://github.com/Fastbyte01)
* [Gomasy](https://github.com/Gomasy)
* [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)
* [lanodan](https://github.com/lanodan)
* [Harmon758](https://github.com/Harmon758)
* [HellPie](https://github.com/HellPie)
* [Habu-Kagumba](https://github.com/Habu-Kagumba)
@@ -360,6 +372,7 @@ and provided thanks to the work of the following contributors:
* [jguerder](https://github.com/jguerder)
* [Jehops](https://github.com/Jehops)
* [joshuap](https://github.com/joshuap)
* [YuleZ](https://github.com/YuleZ)
* [Tiwy57](https://github.com/Tiwy57)
* [xuv](https://github.com/xuv)
* [Jnsll](https://github.com/Jnsll)
@@ -388,6 +401,7 @@ and provided thanks to the work of the following contributors:
* [otsune](https://github.com/otsune)
* [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
* [matt-auckland](https://github.com/matt-auckland)
* [webroo](https://github.com/webroo)
* [matthiasbeyer](https://github.com/matthiasbeyer)
* [mattjmattj](https://github.com/mattjmattj)
* [mtparet](https://github.com/mtparet)
@@ -400,6 +414,7 @@ and provided thanks to the work of the following contributors:
* [mike-burns](https://github.com/mike-burns)
* [verymilan](https://github.com/verymilan)
* [milmazz](https://github.com/milmazz)
* [premist](https://github.com/premist)
* [Mnkai](https://github.com/Mnkai)
* [mitchhentges](https://github.com/mitchhentges)
* [moritzheiber](https://github.com/moritzheiber)
@@ -413,7 +428,7 @@ and provided thanks to the work of the following contributors:
* [vonneudeck](https://github.com/vonneudeck)
* [Ninetailed](https://github.com/Ninetailed)
* [k24](https://github.com/k24)
* [Noiob](mailto:noiob@users.noreply.github.com)
* [noiob](https://github.com/noiob)
* [kwaio](https://github.com/kwaio)
* [norayr](https://github.com/norayr)
* [joyeusenoelle](https://github.com/joyeusenoelle)
@@ -425,7 +440,6 @@ and provided thanks to the work of the following contributors:
* [Pangoraw](https://github.com/Pangoraw)
* [peterkeen](https://github.com/peterkeen)
* [pgate](https://github.com/pgate)
* [qguv](https://github.com/qguv)
* [remram44](https://github.com/remram44)
* [retokromer](https://github.com/retokromer)
* [rfwatson](https://github.com/rfwatson)
@@ -436,6 +450,7 @@ and provided thanks to the work of the following contributors:
* [staticsafe](https://github.com/staticsafe)
* [snwh](https://github.com/snwh)
* [sts10](https://github.com/sts10)
* [sascha-sl](https://github.com/sascha-sl)
* [skoji](https://github.com/skoji)
* [ScienJus](https://github.com/ScienJus)
* [larkinscott](https://github.com/larkinscott)
@@ -450,20 +465,20 @@ and provided thanks to the work of the following contributors:
* [Sina Mashek](mailto:sina@mashek.xyz)
* [sossii](https://github.com/sossii)
* [SpankyWorks](https://github.com/SpankyWorks)
* [StefOfficiel](https://github.com/StefOfficiel)
* [svetlik](https://github.com/svetlik)
* [dereckson](https://github.com/dereckson)
* [phaedryx](https://github.com/phaedryx)
* [takp](https://github.com/takp)
* [tkusano](https://github.com/tkusano)
* [TakesxiSximada](https://github.com/TakesxiSximada)
* [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)
* [treyssatvincent](https://github.com/treyssatvincent)
* [StefOfficiel](mailto:pichard.stephane@free.fr)
* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
* [Sébastien Santoro](mailto:dereckson@espace-win.org)
* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
* [Takayuki KUSANO](mailto:github@tkusano.jp)
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
* [Toby Pinder](mailto:gigitrix@gmail.com)
* [Tomonori Murakami](mailto:crosslife777@gmail.com)
* [TomoyaShibata](mailto:wind.of.hometown@gmail.com)
* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
* [Udo Kramer](mailto:optik@fluffel.io)
* [Una](mailto:una@unascribed.com)
* [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
@@ -489,6 +504,7 @@ and provided thanks to the work of the following contributors:
* [benklop](mailto:benklop@gmail.com)
* [bsky](mailto:git@imbsky.net)
* [caesarologia](mailto:lopesgemelli.1@gmail.com)
* [cbayerlein](mailto:c.bayerlein@gmail.com)
* [chrolis](mailto:chrolis@users.noreply.github.com)
* [cormo](mailto:cormorant2+github@gmail.com)
* [d0p1](mailto:dopi-sama@hush.com)
@@ -500,19 +516,23 @@ and provided thanks to the work of the following contributors:
* [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
* [haosbvnker](mailto:github@chaosbunker.com)
* [isati](mailto:phil@juchnowi.cz)
* [jacob](mailto:jacobherringtondeveloper@gmail.com)
* [jenn kaplan](mailto:me@jkap.io)
* [jirayudech](mailto:jirayudech@gmail.com)
* [jooops](mailto:joops@autistici.org)
* [jukper](mailto:jukkaperanto@gmail.com)
* [jumoru](mailto:jumoru@mailbox.org)
* [karlyeurl](mailto:karl.yeurl@gmail.com)
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
* [kuro5hin](mailto:rusty@kuro5hin.org)
* [luzpaz](mailto:luzpaz@users.noreply.github.com)
* [maxypy](mailto:maxime@mpigou.fr)
* [mhe](mailto:mail@marcus-herrmann.com)
* [mimikun](mailto:dzdzble_effort_311@outlook.jp)
* [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
* [muan](mailto:muan@github.com)
* [neetshin](mailto:neetshin@neetsh.in)
* [nightpool](mailto:nightpool@users.noreply.github.com)
* [rch850](mailto:rich850@gmail.com)
* [roikale](mailto:roikale@users.noreply.github.com)
* [rysiekpl](mailto:rysiek@hackerspace.pl)
@@ -524,6 +544,7 @@ and provided thanks to the work of the following contributors:
* [tackeyy](mailto:mailto.takita.yusuke@gmail.com)
* [tateisu](mailto:tateisu@gmail.com)
* [tmyt](mailto:shigure@refy.net)
* [trevDev()](mailto:trev@trevdev.ca)
* [utam0k](mailto:k0ma@utam0k.jp)
* [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
* [walfie](mailto:walfington@gmail.com)

+ 107
- 0
CHANGELOG.md View File

@@ -0,0 +1,107 @@
Changelog
=========

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

## [Unreleased]
### Added

- Add link ownership verification (#8703)
- Add conversations API (#8832)
- Add limit for the number of people that can be followed from one account (#8807)
- Add admin setting to customize mascot (#8766)
- Add support for more granular ActivityPub audiences from other software, i.e. circles (#8950)
- Add option to block all reports from a domain (#8830)
- Add user preference to always expand toots marked with content warnings (#8762)
- Add user preference to always hide all media (#8569)
- Add `force_login` param to OAuth authorize page (#8655)
- Add `tootctl accounts backup` (#8642, #8811)
- Add `tootctl accounts create` (#8642, #8811)
- Add `tootctl accounts cull` (#8642, #8811)
- Add `tootctl accounts delete` (#8642, #8811)
- Add `tootctl accounts modify` (#8642, #8811)
- Add `tootctl accounts refresh` (#8642, #8811)
- Add `tootctl feeds build` (#8642, #8811)
- Add `tootctl feeds clear` (#8642, #8811)
- Add `tootctl settings registrations open` (#8642, #8811)
- Add `tootctl settings registrations close` (#8642, #8811)
- Add `min_id` param to REST API to support backwards pagination (#8736)
- Add a confirmation dialog when hitting reply and the compose box isn't empty (#8893)
- Add PostgreSQL disk space growth tracking in PGHero (#8906)
- Add button for disabling local account to report quick actions bar (#9024)
- Add Czech language (#8594)
- Add `Clear-Site-Data` header when logging out (#8627)
- Add `same-site` (`lax`) attribute to cookies (#8626)
- Add support for styled scrollbars in Firefox Nightly (#8653)
- Add highlight to the active tab in web UI profiles (#8673)
- Add auto-focus for comment textarea in report modal (#8689)
- Add auto-focus for emoji picker's search field (#8688)
- Add nginx and systemd templates to `dist/` directory (#8770)
- Add support for `/.well-known/change-password` (#8828)
- Add option to override FFMPEG binary path (#8855)
- Add `dns-prefetch` tag when using different host for assets or uploads (#8942)
- Add `description` meta tag (#8941)
- Add `Content-Security-Policy` header (#8957)
- Add cache for the instance info API (#8765)

### Changed

- Change forms design (#8703)
- Change reports overview to group by target account (#8674)
- Change web UI to show "read more" link on overly long in-stream statuses (#8205)
- Change design of direct messages column (#8832, #9022)
- Change home timelines to exclude DMs (#8940)
- Change list timelines to exclude all replies (#8683)
- Change admin accounts UI default sort to most recent (#8813)
- Change documentation URL in the UI (#8898)
- Change style of success and failure messages (#8973)
- Change DM filtering to always allow DMs from staff (#8993)
- Change recommended Ruby version to 2.5.3 (#9003)

### Deprecated

- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` (#8832)
- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` (#8905)

### Removed

- Remove "on this device" label in column push settings (#8704)
- Remove rake tasks in favour of tootctl commands (#8675)

### Fixed

- Fix remote statuses using instance's default locale if no language given (#8861)
- Fix streaming API not exiting when port or socket is unavailable (#9023)
- Fix network calls being performed in database transaction in ActivityPub handler (#8951)
- Fix dropdown arrow position (#8637)
- Fix first element of dropdowns being focused even if not using keyboard (#8679)
- Fix tootctl requiring `bundle exec` invocation (#8619)
- Fix public pages not using animation preference for avatars (#8614)
- Fix OEmbed/OpenGraph cards not understanding relative URLs (#8669)
- Fix some dark emojis not having a white outline (#8597)
- Fix media description not being displayed in various media modals (#8678)
- Fix generated URLs of desktop notifications missing base URL (#8758)
- Fix RTL styles (#8764, #8767, #8823, #8897, #9005, #9007, #9018, #9021)
- Fix crash in streaming API when tag param missing (#8955)
- Fix hotkeys not working when no element is focused (#8998)
- Fix some hotkeys not working on detailed status view (#9006)

## [2.5.2] - 2018-10-12
### Security

- Fix XSS vulnerability (#8959)

## [2.5.1] - 2018-10-07
### Fixed

- Fix database migrations for PostgreSQL below 9.5 (#8903)
- Fix class autoloading issue in ActivityPub Create handler (#8820)
- Fix cache statistics not being sent via statsd when statsd enabled (#8831)
- Bump puma from 3.11.4 to 3.12.0 (#8883)

### Security

- Fix some local images not having their EXIF metadata stripped on upload (#8714)
- Fix being able to enable a disabled relay via ActivityPub Accept handler (#8864)
- Bump nokogiri from 1.8.4 to 1.8.5 (#8881)
- Fix being able to report statuses not belonging to the reported account (#8916)

+ 22
- 40
CONTRIBUTING.md View File

@@ -32,60 +32,42 @@ You should also try to follow the guidelines set out in the original `CONTRIBUTI
<blockquote>

CONTRIBUTING
============
=======
Contributing

There are three ways in which you can contribute to this repository:
Thank you for considering contributing to Mastodon 🐘

1. By improving the documentation
2. By working on the back-end application
3. By working on the front-end application
You can contribute in the following ways:

Choosing what to work on in a large open source project is not easy. The list of [GitHub issues](https://github.com/tootsuite/mastodon/issues) may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise. If your addition creates a new feature or setting, or otherwise changes how things work in some substantial way, please remember to submit a correlating pull request to document your changes in the [documentation](http://github.com/tootsuite/documentation).
- Finding and reporting bugs
- Translating the Mastodon interface into various languages
- Contributing code to Mastodon by fixing bugs or implementing features
- Improving the documentation

Below are the guidelines for working on pull requests:
## Bug reports

## General
Bug reports and feature suggestions can be submitted to [GitHub Issues](https://github.com/tootsuite/mastodon/issues). Please make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected in the past using the search function. Please also use descriptive, concise titles.

- 2 spaces indentation
## Translations

## Documentation

- No spelling mistakes
- No orthographic mistakes
- No Markdown syntax errors

## Requirements

- Ruby
- Node.js
- PostgreSQL
- Redis
- Nginx (optional)

## Back-end application
You can submit translations via [Weblate](https://weblate.joinmastodon.org/). They are periodically merged into the codebase.

It is expected that you have a working development environment set up. The development environment includes [rubocop](https://github.com/bbatsov/rubocop), which checks your Ruby code for compliance with our style guide and best practices. Sublime Text, likely like other editors, has a [Rubocop plugin](https://github.com/pderichs/sublime_rubocop) that runs checks on files as you edit them. The codebase also has a test suite.

* The codebase is not perfect, at the time of writing, but it is expected that you do not introduce new code style violations
* The rspec test suite must pass
* To the extent that it is possible, verify your changes. In the best case, by adding new tests to the test suite. At the very least, by running the server or console and checking it manually
* If you are introducing new strings to the user interface, they must be using localization methods
[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)

If your code has syntax errors that won't let it run, it's a good sign that the pull request isn't ready for submission yet.
## Pull requests

## Front-end application
Please use clean, concise titles for your pull requests. We use commit squashing, so the final commit in the master branch will carry the title of the pull request.

It is expected that you have a working development environment set up (see back-end application section). This project includes an ESLint configuration file, with which you can lint your changes.
The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged. Splitting tasks into multiple smaller pull requests is often preferable.

* Avoid grave ESLint violations
* Verify that your changes work
* If you are introducing new strings, they must be using localization methods
**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:

If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
- Unit and integration tests (rspec, jest)
- Code style rules (rubocop, eslint)
- Normalization of locale files (i18n-tasks)

## Translate
## Documentation

You can contribute to translating Mastodon via Weblate at [weblate.joinmastodon.org](https://weblate.joinmastodon.org/).
[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)
The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/docs](https://source.joinmastodon.org/mastodon/docs).

</blockquote>

+ 4
- 4
Gemfile View File

@@ -16,8 +16,8 @@ gem 'pghero', '~> 2.2'
gem 'dotenv-rails', '~> 2.5'

gem 'aws-sdk-s3', '~> 1.21', require: false
gem 'fog-core', '~> 2.1'
gem 'fog-openstack', '~> 1.0', require: false
gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'paperclip', '~> 6.0'
gem 'paperclip-av-transcoder', '~> 0.6'
gem 'streamio-ffmpeg', '~> 3.0'
@@ -64,7 +64,7 @@ gem 'oj', '~> 3.6'
gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.10'
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
gem 'pundit', '~> 1.1'
gem 'pundit', '~> 2.0'
gem 'premailer-rails'
gem 'rack-attack', '~> 5.4'
gem 'rack-cors', '~> 1.0', require: 'rack/cors'
@@ -82,7 +82,7 @@ gem 'simple-navigation', '~> 4.0'
gem 'simple_form', '~> 4.0'
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'stoplight', '~> 2.1.3'
gem 'strong_migrations', '~> 0.2'
gem 'strong_migrations', '~> 0.3'
gem 'tty-command', '~> 0.8', require: false
gem 'tty-prompt', '~> 0.17', require: false
gem 'twitter-text', '~> 1.14'

+ 15
- 15
Gemfile.lock View File

@@ -182,7 +182,7 @@ GEM
docile (1.3.0)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.0.0)
doorkeeper (5.0.1)
railties (>= 4.2)
dotenv (2.5.0)
dotenv-rails (2.5.0)
@@ -211,7 +211,7 @@ GEM
fast_blank (1.0.0)
fastimage (2.1.4)
ffi (1.9.25)
fog-core (2.1.2)
fog-core (2.1.0)
builder
excon (~> 0.58)
formatador (~> 0.2)
@@ -219,8 +219,8 @@ GEM
fog-json (1.2.0)
fog-core
multi_json (~> 1.10)
fog-openstack (1.0.3)
fog-core (~> 2.1)
fog-openstack (0.3.7)
fog-core (>= 1.45, <= 2.1.0)
fog-json (>= 1.0)
ipaddress (>= 0.8)
formatador (0.2.5)
@@ -271,7 +271,7 @@ GEM
httplog (1.1.1)
rack (>= 1.0)
rainbow (>= 2.0.0)
i18n (1.1.0)
i18n (1.1.1)
concurrent-ruby (~> 1.0)
i18n-tasks (0.9.25)
activesupport (>= 4.0.2)
@@ -360,7 +360,7 @@ GEM
concurrent-ruby (~> 1.0.0)
sidekiq (>= 3.5.0)
statsd-ruby (~> 1.2.0)
oj (3.6.11)
oj (3.6.12)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
@@ -417,7 +417,7 @@ GEM
pry (>= 0.10.4)
public_suffix (3.0.3)
puma (3.12.0)
pundit (1.1.0)
pundit (2.0.0)
activesupport (>= 3.0.0)
raabro (1.1.6)
rack (2.0.5)
@@ -587,7 +587,7 @@ GEM
stoplight (2.1.3)
streamio-ffmpeg (3.0.2)
multi_json (~> 1.8)
strong_migrations (0.2.3)
strong_migrations (0.3.1)
activerecord (>= 3.2.0)
temple (0.8.0)
terminal-table (1.8.0)
@@ -618,7 +618,7 @@ GEM
unf (~> 0.1.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
tzinfo-data (1.2018.5)
tzinfo-data (1.2018.6)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
@@ -680,8 +680,8 @@ DEPENDENCIES
faker (~> 1.9)
fast_blank (~> 1.0)
fastimage
fog-core (~> 2.1)
fog-openstack (~> 1.0)
fog-core (<= 2.1.0)
fog-openstack (~> 0.3)
fuubar (~> 2.3)
goldfinger (~> 2.1)
hamlit-rails (~> 0.2)
@@ -727,7 +727,7 @@ DEPENDENCIES
pry-byebug (~> 3.6)
pry-rails (~> 0.3)
puma (~> 3.12)
pundit (~> 1.1)
pundit (~> 2.0)
rack-attack (~> 5.4)
rack-cors (~> 1.0)
rails (~> 5.2.1)
@@ -755,7 +755,7 @@ DEPENDENCIES
stackprof
stoplight (~> 2.1.3)
streamio-ffmpeg (~> 3.0)
strong_migrations (~> 0.2)
strong_migrations (~> 0.3)
thor (~> 0.20)
tty-command (~> 0.8)
tty-prompt (~> 0.17)
@@ -766,7 +766,7 @@ DEPENDENCIES
webpush

RUBY VERSION
ruby 2.5.0p0
ruby 2.5.3p105

BUNDLED WITH
1.16.5
1.16.6

+ 1
- 1
app/controllers/admin/domain_blocks_controller.rb View File

@@ -46,7 +46,7 @@ module Admin
end

def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
end

def retroactive_unblock?

+ 9
- 0
app/controllers/admin/reports_controller.rb View File

@@ -44,6 +44,14 @@ module Admin
when 'resolve'
@report.resolve!(current_account)
log_action :resolve, @report
when 'disable'
@report.resolve!(current_account)
@report.target_account.user.disable!

log_action :resolve, @report
log_action :disable, @report.target_account.user

resolve_all_target_account_reports
when 'silence'
@report.resolve!(current_account)
@report.target_account.update!(silenced: true)
@@ -55,6 +63,7 @@ module Admin
else
raise ActiveRecord::RecordNotFound
end

@report.reload
end


+ 18
- 2
app/controllers/api/v1/conversations_controller.rb View File

@@ -3,9 +3,11 @@
class Api::V1::ConversationsController < Api::BaseController
LIMIT = 20

before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
before_action :require_user!
after_action :insert_pagination_headers
before_action :set_conversation, except: :index
after_action :insert_pagination_headers, only: :index

respond_to :json

@@ -14,8 +16,22 @@ class Api::V1::ConversationsController < Api::BaseController
render json: @conversations, each_serializer: REST::ConversationSerializer
end

def read
@conversation.update!(unread: false)
render json: @conversation, serializer: REST::ConversationSerializer
end

def destroy
@conversation.destroy!
render_empty
end

private

def set_conversation
@conversation = AccountConversation.where(account: current_account).find(params[:id])
end

def paginated_conversations
AccountConversation.where(account: current_account)
.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))

+ 0
- 1
app/controllers/api/v1/reports_controller.rb View File

@@ -1,7 +1,6 @@
# frozen_string_literal: true

class Api::V1::ReportsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create]
before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
before_action :require_user!


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

@@ -84,7 +84,7 @@ module ApplicationHelper
end

def cdn_host
ENV['CDN_HOST'].presence
Rails.configuration.action_controller.asset_host
end

def cdn_host?
@@ -92,10 +92,10 @@ module ApplicationHelper
end

def storage_host
ENV['S3_ALIAS_HOST'].presence || ENV['S3_CLOUDFRONT_HOST'].presence
"https://#{ENV['S3_ALIAS_HOST'].presence || ENV['S3_CLOUDFRONT_HOST']}"
end

def storage_host?
storage_host.present?
ENV['S3_ALIAS_HOST'].present? || ENV['S3_CLOUDFRONT_HOST'].present?
end
end

+ 8
- 13
app/javascript/core/admin.js View File

@@ -2,18 +2,6 @@

import { delegate } from 'rails-ujs';

function handleDeleteStatus(event) {
const [data] = event.detail;
const element = document.querySelector(`[data-id="${data.id}"]`);
if (element) {
element.parentNode.removeChild(element);
}
}

[].forEach.call(document.querySelectorAll('.trash-button'), (content) => {
content.addEventListener('ajax:success', handleDeleteStatus);
});

const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';

delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
@@ -24,6 +12,7 @@ delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {

delegate(document, batchCheckboxClassName, 'change', () => {
const checkAllElement = document.querySelector('#batch_checkbox_all');

if (checkAllElement) {
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
@@ -43,8 +32,14 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => {
});

delegate(document, '#domain_block_severity', 'change', ({ target }) => {
const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports');

if (rejectMediaDiv) {
rejectMediaDiv.style.display = (target.value === 'suspend') ? 'none' : 'block';
}

if (rejectReportsDiv) {
rejectReportsDiv.style.display = (target.value === 'suspend') ? 'none' : 'block';
}
});

+ 15
- 14
app/javascript/mastodon/actions/compose.js View File

@@ -56,7 +56,7 @@ export function changeCompose(text) {
};
};

export function replyCompose(status, router) {
export function replyCompose(status, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_REPLY,
@@ -64,7 +64,7 @@ export function replyCompose(status, router) {
});

if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
routerHistory.push('/statuses/new');
}
};
};
@@ -81,7 +81,7 @@ export function resetCompose() {
};
};

export function mentionCompose(account, router) {
export function mentionCompose(account, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_MENTION,
@@ -89,12 +89,12 @@ export function mentionCompose(account, router) {
});

if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
routerHistory.push('/statuses/new');
}
};
};

export function directCompose(account, router) {
export function directCompose(account, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_DIRECT,
@@ -102,12 +102,12 @@ export function directCompose(account, router) {
});

if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
routerHistory.push('/statuses/new');
}
};
};

export function submitCompose() {
export function submitCompose(routerHistory) {
return function (dispatch, getState) {
const status = getState().getIn(['compose', 'text'], '');
const media = getState().getIn(['compose', 'media_attachments']);
@@ -133,21 +133,22 @@ export function submitCompose() {
dispatch(insertIntoTagHistory(response.data.tags, status));
dispatch(submitComposeSuccess({ ...response.data }));

// To make the app more responsive, immediately get the status into the columns
// To make the app more responsive, immediately push the status
// into the columns

const insertIfOnline = (timelineId) => {
const insertIfOnline = timelineId => {
if (getState().getIn(['timelines', timelineId, 'items', 0]) !== null) {
dispatch(updateTimeline(timelineId, { ...response.data }));
}
};

insertIfOnline('home');

if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0) {
routerHistory.push('/timelines/direct');
} else if (response.data.visibility !== 'direct') {
insertIfOnline('home');
} else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
insertIfOnline('community');
insertIfOnline('public');
} else if (response.data.visibility === 'direct') {
insertIfOnline('direct');
}
}).catch(function (error) {
dispatch(submitComposeFail(error));

+ 22
- 0
app/javascript/mastodon/actions/conversations.js View File

@@ -5,11 +5,33 @@ import {
importFetchedStatus,
} from './importer';

export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';

export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';

export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';

export const mountConversations = () => ({
type: CONVERSATIONS_MOUNT,
});

export const unmountConversations = () => ({
type: CONVERSATIONS_UNMOUNT,
});

export const markConversationRead = conversationId => (dispatch, getState) => {
dispatch({
type: CONVERSATIONS_READ,
id: conversationId,
});

api(getState).post(`/api/v1/conversations/${conversationId}/read`);
};

export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
dispatch(expandConversationsRequest());


+ 96
- 0
app/javascript/mastodon/components/avatar_composite.js View File

@@ -0,0 +1,96 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from '../initial_state';

export default class AvatarComposite extends React.PureComponent {

static propTypes = {
accounts: ImmutablePropTypes.list.isRequired,
animate: PropTypes.bool,
size: PropTypes.number.isRequired,
};

static defaultProps = {
animate: autoPlayGif,
};

renderItem (account, size, index) {
const { animate } = this.props;

let width = 50;
let height = 100;
let top = 'auto';
let left = 'auto';
let bottom = 'auto';
let right = 'auto';

if (size === 1) {
width = 100;
}

if (size === 4 || (size === 3 && index > 0)) {
height = 50;
}

if (size === 2) {
if (index === 0) {
right = '2px';
} else {
left = '2px';
}
} else if (size === 3) {
if (index === 0) {
right = '2px';
} else if (index > 0) {
left = '2px';
}

if (index === 1) {
bottom = '2px';
} else if (index > 1) {
top = '2px';
}
} else if (size === 4) {
if (index === 0 || index === 2) {
right = '2px';
}

if (index === 1 || index === 3) {
left = '2px';
}

if (index < 2) {
bottom = '2px';
} else {
top = '2px';
}
}

const style = {
left: left,
top: top,
right: right,
bottom: bottom,
width: `${width}%`,
height: `${height}%`,
backgroundSize: 'cover',
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
};

return (
<div key={account.get('id')} style={style} />
);
}

render() {
const { accounts, size } = this.props;

return (
<div className='account__avatar-composite' style={{ width: `${size}px`, height: `${size}px` }}>
{accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))}
</div>
);
}

}

+ 12
- 8
app/javascript/mastodon/components/display_name.js View File

@@ -1,25 +1,29 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';

export default class DisplayName extends React.PureComponent {

static propTypes = {
account: ImmutablePropTypes.map.isRequired,
withAcct: PropTypes.bool,
};

static defaultProps = {
withAcct: true,
others: ImmutablePropTypes.list,
};

render () {
const { account, withAcct } = this.props;
const { account, others } = this.props;
const displayNameHtml = { __html: account.get('display_name_html') };

let suffix;

if (others && others.size > 1) {
suffix = `+${others.size}`;
} else {
suffix = <span className='display-name__account'>@{account.get('acct')}</span>;
}

return (
<span className='display-name'>
<bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi> {withAcct && <span className='display-name__account'>@{account.get('acct')}</span>}
<bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi>
<span>{suffix}</span>
</span>
);
}

+ 17
- 6
app/javascript/mastodon/components/status.js View File

@@ -3,6 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import Avatar from './avatar';
import AvatarOverlay from './avatar_overlay';
import AvatarComposite from './avatar_composite';
import RelativeTimestamp from './relative_timestamp';
import DisplayName from './display_name';
import StatusContent from './status_content';
@@ -45,6 +46,8 @@ class Status extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map,
account: ImmutablePropTypes.map,
otherAccounts: ImmutablePropTypes.list,
onClick: PropTypes.func,
onReply: PropTypes.func,
onFavourite: PropTypes.func,
onReblog: PropTypes.func,
@@ -60,6 +63,7 @@ class Status extends ImmutablePureComponent {
onToggleHidden: PropTypes.func,
muted: PropTypes.bool,
hidden: PropTypes.bool,
unread: PropTypes.bool,
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
};
@@ -74,6 +78,11 @@ class Status extends ImmutablePureComponent {
]

handleClick = () => {
if (this.props.onClick) {
this.props.onClick();
return;
}

if (!this.context.router) {
return;
}
@@ -158,7 +167,7 @@ class Status extends ImmutablePureComponent {
let media = null;
let statusAvatar, prepend, rebloggedByText;

const { intl, hidden, featured } = this.props;
const { intl, hidden, featured, otherAccounts, unread } = this.props;

let { status, account, ...other } = this.props;

@@ -249,9 +258,11 @@ class Status extends ImmutablePureComponent {
}
}

if (account === undefined || account === null) {
if (otherAccounts) {
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />;
} else if (account === undefined || account === null) {
statusAvatar = <Avatar account={status.get('account')} size={48} />;
}else{
} else {
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
}

@@ -269,10 +280,10 @@ class Status extends ImmutablePureComponent {

return (
<HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}>
{prepend}

<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}>
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
<div className='status__info'>
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>

@@ -281,7 +292,7 @@ class Status extends ImmutablePureComponent {
{statusAvatar}
</div>

<DisplayName account={status.get('account')} />
<DisplayName account={status.get('account')} others={otherAccounts} />
</a>
</div>


+ 5
- 1
app/javascript/mastodon/features/compose/components/compose_form.js View File

@@ -31,6 +31,10 @@ const messages = defineMessages({
export default @injectIntl
class ComposeForm extends ImmutablePureComponent {

static contextTypes = {
router: PropTypes.object,
};

static propTypes = {
intl: PropTypes.object.isRequired,
text: PropTypes.string.isRequired,
@@ -85,7 +89,7 @@ class ComposeForm extends ImmutablePureComponent {
return;
}

this.props.onSubmit();
this.props.onSubmit(this.context.router.history);
}

onSuggestionsClearRequested = () => {

+ 1
- 1
app/javascript/mastodon/features/compose/components/privacy_dropdown.js View File

@@ -164,7 +164,7 @@ class PrivacyDropdown extends React.PureComponent {

state = {
open: false,
placement: null,
placement: 'bottom',
};

handleToggle = ({ target }) => {

+ 5
- 1
app/javascript/mastodon/features/compose/components/upload.js View File

@@ -14,6 +14,10 @@ const messages = defineMessages({
export default @injectIntl
class Upload extends ImmutablePureComponent {

static contextTypes = {
router: PropTypes.object,
};

static propTypes = {
media: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
@@ -37,7 +41,7 @@ class Upload extends ImmutablePureComponent {

handleSubmit = () => {
this.handleInputBlur();
this.props.onSubmit();
this.props.onSubmit(this.context.router.history);
}

handleUndoClick = () => {

+ 2
- 2
app/javascript/mastodon/features/compose/containers/compose_form_container.js View File

@@ -33,8 +33,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(changeCompose(text));
},

onSubmit () {
dispatch(submitCompose());
onSubmit (router) {
dispatch(submitCompose(router));
},

onClearSuggestions () {

+ 2
- 2
app/javascript/mastodon/features/compose/containers/upload_container.js View File

@@ -22,8 +22,8 @@ const mapDispatchToProps = dispatch => ({
dispatch(openModal('FOCAL_POINT', { id }));
},

onSubmit () {
dispatch(submitCompose());
onSubmit (router) {
dispatch(submitCompose(router));
},

});

+ 2
- 1
app/javascript/mastodon/features/compose/index.js View File

@@ -13,6 +13,7 @@ import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container';
import { changeComposing } from '../../actions/compose';
import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
import { mascot } from '../../initial_state';

const messages = defineMessages({
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@@ -107,7 +108,7 @@ class Compose extends React.PureComponent {
<ComposeFormContainer />
{multiColumn && (
<div className='drawer__inner__mastodon'>
<img alt='' draggable='false' src={elephantUIPlane} />
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
</div>
)}
</div>}

+ 20
- 42
app/javascript/mastodon/features/direct_timeline/components/conversation.js View File

@@ -2,12 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import StatusContent from '../../../components/status_content';
import RelativeTimestamp from '../../../components/relative_timestamp';
import DisplayName from '../../../components/display_name';
import Avatar from '../../../components/avatar';
import AttachmentList from '../../../components/attachment_list';
import { HotKeys } from 'react-hotkeys';
import StatusContainer from '../../../containers/status_container';

export default class Conversation extends ImmutablePureComponent {

@@ -18,9 +13,11 @@ export default class Conversation extends ImmutablePureComponent {
static propTypes = {
conversationId: PropTypes.string.isRequired,
accounts: ImmutablePropTypes.list.isRequired,
lastStatus: ImmutablePropTypes.map.isRequired,
lastStatusId: PropTypes.string,
unread:PropTypes.bool.isRequired,
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
markRead: PropTypes.func.isRequired,
};

handleClick = () => {
@@ -28,8 +25,13 @@ export default class Conversation extends ImmutablePureComponent {
return;
}

const { lastStatus } = this.props;
this.context.router.history.push(`/statuses/${lastStatus.get('id')}`);
const { lastStatusId, unread, markRead } = this.props;

if (unread) {
markRead();
}

this.context.router.history.push(`/statuses/${lastStatusId}`);
}

handleHotkeyMoveUp = () => {
@@ -41,44 +43,20 @@ export default class Conversation extends ImmutablePureComponent {
}

render () {
const { accounts, lastStatus, lastAccount } = this.props;
const { accounts, lastStatusId, unread } = this.props;

if (lastStatus === null) {
if (lastStatusId === null) {
return null;
}

const handlers = {
moveDown: this.handleHotkeyMoveDown,
moveUp: this.handleHotkeyMoveUp,
open: this.handleClick,
};

let media;

if (lastStatus.get('media_attachments').size > 0) {
media = <AttachmentList compact media={lastStatus.get('media_attachments')} />;
}

return (
<HotKeys handlers={handlers}>
<div className='conversation focusable' tabIndex='0' onClick={this.handleClick} role='button'>
<div className='conversation__header'>
<div className='conversation__avatars'>
<div>{accounts.map(account => <Avatar key={account.get('id')} size={36} account={account} />)}</div>
</div>

<div className='conversation__time'>
<RelativeTimestamp timestamp={lastStatus.get('created_at')} />
<br />
<DisplayName account={lastAccount} withAcct={false} />
</div>
</div>

<StatusContent status={lastStatus} onClick={this.handleClick} />

{media}
</div>
</HotKeys>
<StatusContainer
id={lastStatusId}
unread={unread}
otherAccounts={accounts}
onMoveUp={this.handleHotkeyMoveUp}
onMoveDown={this.handleHotkeyMoveDown}
/>
);
}


+ 8
- 4
app/javascript/mastodon/features/direct_timeline/containers/conversation_container.js View File

@@ -1,15 +1,19 @@
import { connect } from 'react-redux';
import Conversation from '../components/conversation';
import { markConversationRead } from '../../../actions/conversations';

const mapStateToProps = (state, { conversationId }) => {
const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
const lastStatus = state.getIn(['statuses', conversation.get('last_status')], null);

return {
accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
lastStatus,
lastAccount: lastStatus === null ? null : state.getIn(['accounts', lastStatus.get('account')], null),
unread: conversation.get('unread'),
lastStatusId: conversation.get('last_status', null),
};
};

export default connect(mapStateToProps)(Conversation);
const mapDispatchToProps = (dispatch, { conversationId }) => ({
markRead: () => dispatch(markConversationRead(conversationId)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Conversation);

+ 4
- 1
app/javascript/mastodon/features/direct_timeline/index.js View File

@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandConversations } from '../../actions/conversations';
import { mountConversations, unmountConversations, expandConversations } from '../../actions/conversations';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connectDirectStream } from '../../actions/streaming';
@@ -48,11 +48,14 @@ class DirectTimeline extends React.PureComponent {
componentDidMount () {
const { dispatch } = this.props;

dispatch(mountConversations());
dispatch(expandConversations());
this.disconnect = dispatch(connectDirectStream());
}

componentWillUnmount () {
this.props.dispatch(unmountConversations());

if (this.disconnect) {
this.disconnect();
this.disconnect = null;

+ 1
- 1
app/javascript/mastodon/features/status/index.js View File

@@ -181,7 +181,7 @@ class Status extends ImmutablePureComponent {
if (status.get('reblogged')) {
this.props.dispatch(unreblog(status));
} else {
if (e.shiftKey || !boostModal) {
if ((e && e.shiftKey) || !boostModal) {
this.handleModalReblog(status);
} else {
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));

+ 1
- 1
app/javascript/mastodon/features/ui/index.js View File

@@ -460,7 +460,7 @@ class UI extends React.PureComponent {
};

return (
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
<TabsBar />


+ 1
- 0
app/javascript/mastodon/initial_state.js View File

@@ -15,5 +15,6 @@ export const searchEnabled = getMeta('search_enabled');
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
export const invitesEnabled = getMeta('invites_enabled');
export const version = getMeta('version');
export const mascot = getMeta('mascot');

export default initialState;

+ 10
- 6
app/javascript/mastodon/locales/ar.json View File

@@ -15,7 +15,7 @@
"account.follows.empty": "هذا المستخدِم لا يتبع أحدًا بعد.",
"account.follows_you": "يتابعك",
"account.hide_reblogs": "إخفاء ترقيات @{name}",
"account.link_verified_on": "Ownership of this link was checked on {date}",
"account.link_verified_on": "تم التحقق مِن مالك هذا الرابط بتاريخ {date}",
"account.media": "وسائط",
"account.mention": "أُذكُر @{name}",
"account.moved_to": "{name} إنتقل إلى :",
@@ -91,8 +91,11 @@
"confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
"confirmations.redraft.confirm": "إزالة و إعادة الصياغة",
"confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته ؟ سوف تفقد جميع الإعجابات و الترقيات أما الردود المتصلة به فستُصبِح يتيمة.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "إلغاء المتابعة",
"confirmations.unfollow.message": "متأكد من أنك تريد إلغاء متابعة {name} ؟",
"conversation.last_message": "Last message:",
"embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
"embed.preview": "هكذا ما سوف يبدو عليه :",
"emoji_button.activity": "الأنشطة",
@@ -113,9 +116,9 @@
"empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
"empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
"empty_column.domain_blocks": "ليس هناك نطاقات مخفية بعد.",
"empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
"empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
"empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
"empty_column.favourited_statuses": "ليس لديك أية تبويقات مفضلة بعد. عندما ستقوم بالإعجاب بواحد، سيظهر هنا.",
"empty_column.favourites": "لم يقم أي أحد بالإعجاب بهذا التبويق بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.",
"empty_column.follow_requests": "ليس عندك أي طلب للمتابعة بعد. سوف تظهر طلباتك هنا إن قمت بتلقي البعض منها.",
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
"empty_column.home": "إنّ الخيط الزمني لصفحتك الرئيسية فارغ. قم بزيارة {public} أو استخدم حقل البحث لكي تكتشف مستخدمين آخرين.",
"empty_column.home.public_timeline": "الخيط العام",
@@ -163,7 +166,7 @@
"keyboard_shortcuts.reply": "للردّ",
"keyboard_shortcuts.requests": "لفتح قائمة طلبات المتابعة",
"keyboard_shortcuts.search": "للتركيز على البحث",
"keyboard_shortcuts.start": "to open \"get started\" column",
"keyboard_shortcuts.start": "لفتح عمود \"هيا نبدأ\"",
"keyboard_shortcuts.toggle_hidden": "لعرض أو إخفاء النص مِن وراء التحذير",
"keyboard_shortcuts.toot": "لتحرير تبويق جديد",
"keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث",
@@ -280,7 +283,7 @@
"status.cancel_reblog_private": "إلغاء الترقية",
"status.cannot_reblog": "تعذرت ترقية هذا المنشور",
"status.delete": "إحذف",
"status.detailed_status": "Detailed conversation view",
"status.detailed_status": "تفاصيل المحادثة",
"status.direct": "رسالة خاصة إلى @{name}",
"status.embed": "إدماج",
"status.favourite": "أضف إلى المفضلة",
@@ -294,6 +297,7 @@
"status.open": "وسع هذه المشاركة",
"status.pin": "تدبيس على الملف الشخصي",
"status.pinned": "تبويق مثبَّت",
"status.read_more": "Read more",
"status.reblog": "رَقِّي",
"status.reblog_private": "القيام بالترقية إلى الجمهور الأصلي",
"status.reblogged_by": "رقّاه {name}",

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

@@ -91,8 +91,11 @@
"confirmations.mute.message": "¿De xuru que quies silenciar a {name}?",
"confirmations.redraft.confirm": "Delete & redraft",
"confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
"conversation.last_message": "Last message:",
"embed.instructions": "Embed this status on your website by copying the code below.",
"embed.preview": "Here is what it will look like:",
"emoji_button.activity": "Actividá",
@@ -294,6 +297,7 @@
"status.open": "Espander esti estáu",
"status.pin": "Pin on profile",
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblogged_by": "{name} boosted",

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

@@ -91,8 +91,11 @@
"confirmations.mute.message": "Are you sure you want to mute {name}?",
"confirmations.redraft.confirm": "Delete & redraft",
"confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
"conversation.last_message": "Last message:",
"embed.instructions": "Embed this status on your website by copying the code below.",
"embed.preview": "Here is what it will look like:",
"emoji_button.activity": "Activity",
@@ -294,6 +297,7 @@
"status.open": "Expand this status",
"status.pin": "Pin on profile",
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Споделяне",
"status.reblog_private": "Boost to original audience",
"status.reblogged_by": "{name} сподели",

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

@@ -91,8 +91,11 @@
"confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
"confirmations.redraft.confirm": "Esborrar i refer",
"confirmations.redraft.message": "Estàs segur que vols esborrar aquesta publicació i tornar a redactar-la? Perderàs totes els impulsos i favorits, i les respostes a la publicació original es quedaran orfes.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Deixa de seguir",
"confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
"conversation.last_message": "Last message:",
"embed.instructions": "Incrusta aquest estat al lloc web copiant el codi a continuació.",
"embed.preview": "Aquí tenim quin aspecte tindrá:",
"emoji_button.activity": "Activitat",
@@ -294,6 +297,7 @@
"status.open": "Ampliar aquest estat",
"status.pin": "Fixat en el perfil",
"status.pinned": "Toot fixat",
"status.read_more": "Read more",
"status.reblog": "Impuls",
"status.reblog_private": "Impulsar a l'audiència original",
"status.reblogged_by": "{name} ha retootejat",

+ 4
- 0
app/javascript/mastodon/locales/co.json View File

@@ -91,8 +91,11 @@
"confirmations.mute.message": "Site sicuru·a che vulete piattà @{name}?",
"confirmations.redraft.confirm": "Sguassà è riscrive",
"confirmations.redraft.message": "Site sicuru·a chè vulete sguassà stu statutu è riscrivelu? I favuriti è spartere saranu persi, è e risposte diventeranu orfane.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Disabbunassi",
"confirmations.unfollow.message": "Site sicuru·a ch'ùn vulete più siguità @{name}?",
"conversation.last_message": "Last message:",
"embed.instructions": "Integrà stu statutu à u vostru situ cù u codice quì sottu.",
"embed.preview": "Assumiglierà à qualcosa cusì:",
"emoji_button.activity": "Attività",
@@ -294,6 +297,7 @@
"status.open": "Apre stu statutu",
"status.pin": "Puntarulà à u prufile",
"status.pinned": "Statutu puntarulatu",
"status.read_more": "Read more",
"status.reblog": "Sparte",
"status.reblog_private": "Sparte à l'audienza uriginale",
"status.reblogged_by": "{name} hà spartutu",

+ 6
- 2
app/javascript/mastodon/locales/cs.json View File

@@ -8,14 +8,14 @@
"account.domain_blocked": "Doména skryta",
"account.edit_profile": "Upravit profil",
"account.endorse": "Představit na profilu",
"account.follow": "Sleduj",
"account.follow": "Sledovat",
"account.followers": "Sledovatelé",
"account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
"account.follows": "Sleduje",
"account.follows.empty": "Tento uživatel ještě nikoho nesleduje.",
"account.follows_you": "Sleduje vás",
"account.hide_reblogs": "Skrýt boosty od uživatele @{name}",
"account.link_verified_on": "Ownership of this link was checked on {date}",
"account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}",
"account.media": "Média",
"account.mention": "Zmínit uživatele @{name}",
"account.moved_to": "{name} se přesunul/a na:",
@@ -91,8 +91,11 @@
"confirmations.mute.message": "Jste si jistý/á, že chcete ignorovat uživatele {name}?",
"confirmations.redraft.confirm": "Vymazat a přepsat",
"confirmations.redraft.message": "Jste si jistý/á, že chcete vymazat a přepsat tento příspěvek? Oblíbení a boosty budou ztraceny a odpovědi na původní příspěvek budou opuštěny.",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Přestat sledovat",
"confirmations.unfollow.message": "jste si jistý/á, že chcete přestat sledovat uživatele {name}?",
"conversation.last_message": "Last message:",
"embed.instructions": "Pro přidání příspěvku na vaši webovou stránku zkopírujte níže uvedený kód.",
"embed.preview": "Takhle to bude vypadat:",
"emoji_button.activity": "Aktivita",
@@ -294,6 +297,7 @@
"status.open": "Rozbalit tento příspěvek",
"status.pin": "Připnout na profil",
"status.pinned": "Připnutý toot",
"status.read_more": "Read more",
"status.reblog": "Boostnout",
"status.reblog_private": "Boostnout původnímu publiku",
"status.reblogged_by": "{name} boostnul/a",

+ 338
- 333
app/javascript/mastodon/locales/cy.json View File

@@ -1,335 +1,340 @@
{
"account.badges.bot": "Bot",
"account.block": "Blociwch @{name}",
"account.block_domain": "Cuddiwch bopeth rhag {domain}",
"account.blocked": "Blociwyd",
"account.direct": "Neges breifat @{name}",
"account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
"account.domain_blocked": "Parth wedi ei guddio",
"account.edit_profile": "Golygu proffil",
"account.endorse": "Arddangos ar fy mhroffil",
"account.follow": "Dilyn",
"account.followers": "Dilynwyr",
"account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
"account.follows": "Yn dilyn",
"account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
"account.follows_you": "Yn eich dilyn chi",
"account.hide_reblogs": "Cuddio bwstiau o @{name}",
"account.media": "Cyfryngau",
"account.mention": "Crybwyll @{name}",
"account.moved_to": "Mae @{name} wedi symud i:",
"account.mute": "Tawelu @{name}",
"account.mute_notifications": "Cuddio hysbysiadau o @{name}",
"account.muted": "Distewyd",
"account.posts": "Tŵtiau",
"account.posts_with_replies": "Tŵtiau ac atebion",
"account.report": "Adroddwch @{name}",
"account.requested": "Aros am gymeradwyaeth. Cliciwch er mwyn canslo cais dilyn",
"account.share": "Rhannwch broffil @{name}",
"account.show_reblogs": "Dangoswch bwstiau o @{name}",
"account.unblock": "Dadflociwch @{name}",
"account.unblock_domain": "Dadguddiwch {domain}",
"account.unendorse": "Peidwch a'i arddangos ar fy mhroffil",
"account.unfollow": "Daddilynwch",
"account.unmute": "Dad-dawelu @{name}",
"account.unmute_notifications": "Dad-dawelu hysbysiadau o @{name}",
"account.view_full_profile": "Gweld proffil llawn",
"alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
"alert.unexpected.title": "Wps!",
"boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
"bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
"bundle_column_error.retry": "Ceisiwch eto",
"bundle_column_error.title": "Gwall rhwydwaith",
"bundle_modal_error.close": "Cau",
"bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
"bundle_modal_error.retry": "Ceiswich eto",
"column.blocks": "Defnyddwyr a flociwyd",
"column.community": "Llinell amser lleol",
"column.direct": "Negeseuon preifat",
"column.domain_blocks": "Parthau cuddiedig",
"column.favourites": "Ffefrynnau",
"column.follow_requests": "Ceisiadau dilyn",
"column.home": "Hafan",
"column.lists": "Rhestrau",
"column.mutes": "Defnyddwyr a ddistewyd",
"column.notifications": "Hysbysiadau",
"column.pins": "Tŵtiau wedi eu pinio",
"column.public": "",
"column_back_button.label": "Nôl",
"column_header.hide_settings": "Cuddiwch dewisiadau",
"column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
"column_header.moveRight_settings": "Symudwch y golofn i'r dde",
"column_header.pin": "Piniwch",
"column_header.show_settings": "Dangos gosodiadau",
"column_header.unpin": "Dadbiniwch",
"column_subheading.settings": "Gosodiadau",
"community.column_settings.media_only": "Cyfryngau yn unig",
"compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
"compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
"compose_form.hashtag_warning": "Ni fydd y tŵt hwn wedi ei restru o dan unrhyw hashnod gan ei fod heb ei restru. Dim ond tŵtiau cyhoeddus gellid chwilota amdanynt drwy hashnod.",
"compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
"compose_form.lock_disclaimer.lock": "wedi ei gloi",
"compose_form.placeholder": "Be syd ar eich meddwl?",
"compose_form.publish": "Tŵt",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "",
"compose_form.sensitive.unmarked": "",
"compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
"compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
"compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
"confirmation_modal.cancel": "Canslo",
"confirmations.block.confirm": "Blociwch",
"confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
"confirmations.delete.confirm": "Dileu",
"confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
"confirmations.delete_list.confirm": "Dileu",
"confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
"confirmations.domain_block.confirm": "",
"confirmations.domain_block.message": "",
"confirmations.mute.confirm": "Tawelu",
"confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?",
"confirmations.redraft.confirm": "Dilëwch & ailddrafftio",
"confirmations.redraft.message": "Ydych chi'n siwr eich bod eisiau dileu y statws hwn a'i ailddrafftio? Bydd ffefrynnau a bwstiau'n cael ei colli, a bydd ymatebion i'r statws gwreiddiol yn cael eu hamddifadu.",
"confirmations.unfollow.confirm": "Dad-ddilynwch",
"confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
"embed.instructions": "Mewnblannwch y statws hwn ar eich gwefan drwy gopïo'r côd isod.",
"embed.preview": "Dyma sut olwg fydd arno:",
"emoji_button.activity": "Gweithgarwch",
"emoji_button.custom": "",
"emoji_button.flags": "Baneri",
"emoji_button.food": "Bwyd a Diod",
"emoji_button.label": "Mewnosodwch emoji",
"emoji_button.nature": "Natur",
"emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "Gwrthrychau",
"emoji_button.people": "Pobl",
"emoji_button.recent": "Defnyddir yn aml",
"emoji_button.search": "Chwilio...",
"emoji_button.search_results": "Canlyniadau chwilio",
"emoji_button.symbols": "Symbolau",
"emoji_button.travel": "Teithio & Llefydd",
"empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
"empty_column.community": "",
"empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
"empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
"empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
"empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
"empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan dderbyniwch chi un, bydd yn ymddangos yma.",
"empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
"empty_column.home": "",
"empty_column.home.public_timeline": "y ffrwd cyhoeddus",
"empty_column.list": "Nid oes dim yn y rhestr yma eto. Pan y bydd aelodau'r rhestr yn cyhoeddi statws newydd, mi fydd yn ymddangos yma.",
"empty_column.lists": "Nid oes gennych unrhyw restrau eto. Pan grëwch chi un, mi fydd yn ymddangos yma.",
"empty_column.mutes": "Nid ydych wedi tawelu unrhyw ddefnyddwyr eto.",
"empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ac eraill i ddechrau'r sgwrs.",
"empty_column.public": "Does dim byd yma! Ysgrifennwch rhywbeth yn gyhoeddus, neu dilynwch ddefnyddwyr o INSTANCES eraill i'w lenwi",
"follow_request.authorize": "Caniatau",
"follow_request.reject": "Gwrthod",
"getting_started.developers": "Datblygwyr",
"getting_started.documentation": "Dogfennaeth",
"getting_started.find_friends": "Canfod ffrindiau o Twitter",
"getting_started.heading": "Dechrau",
"getting_started.invite": "Gwahoddwch bobl",
"getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.",
"getting_started.security": "Diogelwch",
"getting_started.terms": "Telerau Gwasanaeth",
"home.column_settings.basic": "Syml",
"home.column_settings.show_reblogs": "",
"home.column_settings.show_replies": "Dangoswch ymatebion",
"keyboard_shortcuts.back": "",
"keyboard_shortcuts.blocked": "i agor rhestr defnyddwyr a flociwyd",
"keyboard_shortcuts.boost": "",
"keyboard_shortcuts.column": "",
"keyboard_shortcuts.compose": "",
"keyboard_shortcuts.description": "Disgrifiad",
"keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
"keyboard_shortcuts.down": "i symud lawr yn y rhestr",
"keyboard_shortcuts.enter": "i agor statws",
"keyboard_shortcuts.favourite": "i hoffi",
"keyboard_shortcuts.favourites": "i agor rhestr hoffi",
"keyboard_shortcuts.federated": "",
"keyboard_shortcuts.heading": "",
"keyboard_shortcuts.home": "i agor ffrwd cartref",
"keyboard_shortcuts.hotkey": "Hotkey",
"keyboard_shortcuts.legend": "",
"keyboard_shortcuts.local": "i agor ffrwd lleol",
"keyboard_shortcuts.mention": "i grybwyll yr awdur",
"keyboard_shortcuts.muted": "i agor rhestr defnyddwyr a dawelwyd",
"keyboard_shortcuts.my_profile": "i agor eich proffil",
"keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
"keyboard_shortcuts.pinned": "",
"keyboard_shortcuts.profile": "i agor proffil yr awdur",
"keyboard_shortcuts.reply": "i ateb",
"keyboard_shortcuts.requests": "i agor rhestr ceisiadau dilyn",
"keyboard_shortcuts.search": "",
"keyboard_shortcuts.start": "",
"keyboard_shortcuts.toggle_hidden": "",
"keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
"keyboard_shortcuts.unfocus": "",
"keyboard_shortcuts.up": "i symud yn uwch yn y rhestr",
"lightbox.close": "Cau",
"lightbox.next": "Nesaf",
"lightbox.previous": "",
"lists.account.add": "Ychwanegwch at restr",
"lists.account.remove": "",
"lists.delete": "Dileu rhestr",
"lists.edit": "Golygwch restr",
"lists.new.create": "Ychwanegwch restr",
"lists.new.title_placeholder": "Teitl rhestr newydd",
"lists.search": "",
"lists.subheading": "Eich rhestrau",
"loading_indicator.label": "Llwytho...",
"media_gallery.toggle_visible": "",
"missing_indicator.label": "Heb ei ganfod",
"missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
"mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
"navigation_bar.apps": "Apiau symudol",
"navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
"navigation_bar.community_timeline": "",
"navigation_bar.compose": "Cyfansoddwch dŵt newydd",
"navigation_bar.direct": "Negeseuon preifat",
"navigation_bar.discover": "Darganfyddwch",
"navigation_bar.domain_blocks": "Parthau cuddiedig",
"navigation_bar.edit_profile": "Golygu proffil",
"navigation_bar.favourites": "Ffefrynnau",
"navigation_bar.filters": "Geiriau a dawelwyd",
"navigation_bar.follow_requests": "Ceisiadau dilyn",
"navigation_bar.info": "",
"navigation_bar.keyboard_shortcuts": "",
"navigation_bar.lists": "Rhestrau",
"navigation_bar.logout": "Allgofnodi",
"navigation_bar.mutes": "Defnyddwyr a dawelwyd",
"navigation_bar.personal": "Personol",
"navigation_bar.pins": "Tŵtiau wedi eu pinio",
"navigation_bar.preferences": "Dewisiadau",
"navigation_bar.public_timeline": "",
"navigation_bar.security": "Diogelwch",
"notification.favourite": "hoffodd {name} eich statws",
"notification.follow": "dilynodd {name} chi",
"notification.mention": "Soniodd {name} amdanoch chi",
"notification.reblog": "",
"notifications.clear": "Clirio hysbysiadau",
"notifications.clear_confirmation": "",
"notifications.column_settings.alert": "",
"notifications.column_settings.favourite": "Ffefrynnau:",
"notifications.column_settings.follow": "Dilynwyr newydd:",
"notifications.column_settings.mention": "",
"notifications.column_settings.push": "Hysbysiadau push",
"notifications.column_settings.reblog": "",