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.
Thibaut Girka 6 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:
13 13
           ALLOW_NOPAM: true
14 14
           CONTINUOUS_INTEGRATION: true
15 15
           DISABLE_SIMPLECOV: true
16
+          PAM_ENABLED: true	
17
+          PAM_DEFAULT_SERVICE: pam_test	
18
+          PAM_CONTROLLED_SERVICE: pam_test_controlled
16 19
     working_directory: ~/projects/mastodon/
17 20
 
18 21
   - &attach_workspace

+ 0
- 4
.env.test View File

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

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

@@ -1,11 +1,17 @@
1 1
 ---
2 2
 name: Feature Request
3
-about: Suggest an idea for this project
3
+about: I have a suggestion
4 4
 
5 5
 ---
6 6
 
7
-[Issue text goes here].
7
+<!-- Please use a concise and distinct title for the issue -->
8 8
 
9
-* * * *
9
+<!-- Consider: Could it be implemented as a 3rd party app using the REST API instead? -->
10 10
 
11
-- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.
11
+### Pitch
12
+
13
+<!-- Describe your idea for a feature. Make sure it has not already been suggested/implemented/turned down before -->
14
+
15
+### Motivation
16
+
17
+<!-- 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 @@
1
+---
2
+name: Support
3
+about: Ask for help with your deployment
4
+
5
+---
6
+
7
+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:
8
+
9
+- https://discourse.joinmastodon.org
10
+- #mastodon on irc.freenode.net

+ 1
- 1
.ruby-version View File

@@ -1 +1 @@
1
-2.5.1
1
+2.5.3

+ 52
- 31
AUTHORS.md View File

@@ -5,44 +5,46 @@ and provided thanks to the work of the following contributors:
5 5
 * [ykzts](https://github.com/ykzts)
6 6
 * [akihikodaki](https://github.com/akihikodaki)
7 7
 * [mjankowski](https://github.com/mjankowski)
8
-* [unarist](https://github.com/unarist)
9 8
 * [ThibG](https://github.com/ThibG)
9
+* [unarist](https://github.com/unarist)
10 10
 * [m4sk1n](https://github.com/m4sk1n)
11 11
 * [yiskah](https://github.com/yiskah)
12 12
 * [nolanlawson](https://github.com/nolanlawson)
13 13
 * [sorin-davidoi](https://github.com/sorin-davidoi)
14 14
 * [abcang](https://github.com/abcang)
15 15
 * [lynlynlynx](https://github.com/lynlynlynx)
16
+* [dependabot[bot]](https://github.com/apps/dependabot)
16 17
 * [alpaca-tc](https://github.com/alpaca-tc)
17 18
 * [nclm](https://github.com/nclm)
18 19
 * [ineffyble](https://github.com/ineffyble)
19 20
 * [renatolond](https://github.com/renatolond)
20 21
 * [jeroenpraat](https://github.com/jeroenpraat)
22
+* [mayaeh](https://github.com/mayaeh)
21 23
 * [blackle](https://github.com/blackle)
22 24
 * [Quent-in](https://github.com/Quent-in)
23 25
 * [JantsoP](https://github.com/JantsoP)
24 26
 * [nullkal](https://github.com/nullkal)
25 27
 * [yookoala](https://github.com/yookoala)
26
-* [mayaeh](https://github.com/mayaeh)
27 28
 * [ysksn](https://github.com/ysksn)
28 29
 * [shuheiktgw](https://github.com/shuheiktgw)
29 30
 * [ashfurrow](https://github.com/ashfurrow)
31
+* [mabkenar](https://github.com/mabkenar)
30 32
 * [zunda](https://github.com/zunda)
31
-* [eramdam](https://github.com/eramdam)
32 33
 * [Kjwon15](https://github.com/Kjwon15)
34
+* [eramdam](https://github.com/eramdam)
33 35
 * [masarakki](https://github.com/masarakki)
34 36
 * [ticky](https://github.com/ticky)
37
+* [takayamaki](https://github.com/takayamaki)
35 38
 * [Quenty31](https://github.com/Quenty31)
36 39
 * [danhunsaker](https://github.com/danhunsaker)
37 40
 * [ThisIsMissEm](https://github.com/ThisIsMissEm)
38 41
 * [hcmiya](https://github.com/hcmiya)
39 42
 * [stephenburgess8](https://github.com/stephenburgess8)
40 43
 * [Wonderfall](https://github.com/Wonderfall)
41
-* [takayamaki](https://github.com/takayamaki)
42 44
 * [matteoaquila](https://github.com/matteoaquila)
43 45
 * [rkarabut](https://github.com/rkarabut)
44
-* [Artoria2e5](https://github.com/Artoria2e5)
45 46
 * [yukimochi](https://github.com/yukimochi)
47
+* [Artoria2e5](https://github.com/Artoria2e5)
46 48
 * [marrus-sh](https://github.com/marrus-sh)
47 49
 * [krainboltgreene](https://github.com/krainboltgreene)
48 50
 * [patf](https://github.com/patf)
@@ -56,7 +58,7 @@ and provided thanks to the work of the following contributors:
56 58
 * [MasterGroosha](https://github.com/MasterGroosha)
57 59
 * [JeanGauthier](https://github.com/JeanGauthier)
58 60
 * [kschaper](https://github.com/kschaper)
59
-* [mabkenar](https://github.com/mabkenar)
61
+* [MaciekBaron](https://github.com/MaciekBaron)
60 62
 * [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
61 63
 * [beatrix-bitrot](https://github.com/beatrix-bitrot)
62 64
 * [adbelle](https://github.com/adbelle)
@@ -64,9 +66,9 @@ and provided thanks to the work of the following contributors:
64 66
 * [MightyPork](https://github.com/MightyPork)
65 67
 * [yhirano55](https://github.com/yhirano55)
66 68
 * [camponez](https://github.com/camponez)
67
-* [MaciekBaron](https://github.com/MaciekBaron)
68 69
 * [SerCom-KC](https://github.com/SerCom-KC)
69 70
 * [aschmitz](https://github.com/aschmitz)
71
+* [devkral](https://github.com/devkral)
70 72
 * [fpiesche](https://github.com/fpiesche)
71 73
 * [gandaro](https://github.com/gandaro)
72 74
 * [johnsudaar](https://github.com/johnsudaar)
@@ -75,8 +77,6 @@ and provided thanks to the work of the following contributors:
75 77
 * [lindwurm](https://github.com/lindwurm)
76 78
 * [victorhck](mailto:victorhck@geeko.site)
77 79
 * [voidsatisfaction](https://github.com/voidsatisfaction)
78
-* [valentin2105](https://github.com/valentin2105)
79
-* [devkral](https://github.com/devkral)
80 80
 * [hikari-no-yume](https://github.com/hikari-no-yume)
81 81
 * [angristan](https://github.com/angristan)
82 82
 * [seefood](https://github.com/seefood)
@@ -93,6 +93,7 @@ and provided thanks to the work of the following contributors:
93 93
 * [tsuwatch](https://github.com/tsuwatch)
94 94
 * [victorhck](https://github.com/victorhck)
95 95
 * [puckipedia](https://github.com/puckipedia)
96
+* [fvh-P](https://github.com/fvh-P)
96 97
 * [contraexemplo](https://github.com/contraexemplo)
97 98
 * [hugogameiro](https://github.com/hugogameiro)
98 99
 * [kazu9su](https://github.com/kazu9su)
@@ -102,11 +103,11 @@ and provided thanks to the work of the following contributors:
102 103
 * [Neetshin](mailto:neetshin@neetsh.in)
103 104
 * [rainyday](https://github.com/rainyday)
104 105
 * [ProgVal](https://github.com/ProgVal)
106
+* [valentin2105](https://github.com/valentin2105)
105 107
 * [yuntan](https://github.com/yuntan)
106 108
 * [goofy-bz](mailto:goofy@babelzilla.org)
107 109
 * [kadiix](https://github.com/kadiix)
108 110
 * [kodacs](https://github.com/kodacs)
109
-* [fvh-P](https://github.com/fvh-P)
110 111
 * [rtucker](https://github.com/rtucker)
111 112
 * [KScl](https://github.com/KScl)
112 113
 * [sterdev](https://github.com/sterdev)
@@ -116,6 +117,7 @@ and provided thanks to the work of the following contributors:
116 117
 * [cpytel](https://github.com/cpytel)
117 118
 * [northerner](https://github.com/northerner)
118 119
 * [fhemberger](https://github.com/fhemberger)
120
+* [greysteil](https://github.com/greysteil)
119 121
 * [hnrysmth](https://github.com/hnrysmth)
120 122
 * [d6rkaiz](https://github.com/d6rkaiz)
121 123
 * [JMendyk](https://github.com/JMendyk)
@@ -125,12 +127,14 @@ and provided thanks to the work of the following contributors:
125 127
 * [reneklacan](https://github.com/reneklacan)
126 128
 * [ekiru](https://github.com/ekiru)
127 129
 * [tcitworld](https://github.com/tcitworld)
130
+* [ashleyhull-versent](https://github.com/ashleyhull-versent)
128 131
 * [geta6](https://github.com/geta6)
129 132
 * [happycoloredbanana](https://github.com/happycoloredbanana)
130 133
 * [leopku](https://github.com/leopku)
131 134
 * [SansPseudoFix](https://github.com/SansPseudoFix)
132 135
 * [tomfhowe](https://github.com/tomfhowe)
133 136
 * [noraworld](https://github.com/noraworld)
137
+* [theboss](https://github.com/theboss)
134 138
 * [178inaba](https://github.com/178inaba)
135 139
 * [alyssais](https://github.com/alyssais)
136 140
 * [kodnaplakal](https://github.com/kodnaplakal)
@@ -140,6 +144,7 @@ and provided thanks to the work of the following contributors:
140 144
 * [halkeye](https://github.com/halkeye)
141 145
 * [hinaloe](https://github.com/hinaloe)
142 146
 * [treby](https://github.com/treby)
147
+* [Reverite](https://github.com/Reverite)
143 148
 * [jpdevries](https://github.com/jpdevries)
144 149
 * [00x9d](https://github.com/00x9d)
145 150
 * [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
@@ -147,15 +152,16 @@ and provided thanks to the work of the following contributors:
147 152
 * [nevillepark](https://github.com/nevillepark)
148 153
 * [ornithocoder](https://github.com/ornithocoder)
149 154
 * [pierreozoux](https://github.com/pierreozoux)
155
+* [qguv](https://github.com/qguv)
150 156
 * [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
151 157
 * [harukasan](https://github.com/harukasan)
152 158
 * [stamak](https://github.com/stamak)
153
-* [theboss](https://github.com/theboss)
154 159
 * [Technowix](mailto:technowix@users.noreply.github.com)
155 160
 * [Eychics](https://github.com/Eychics)
156 161
 * [Thor Harald Johansen](mailto:thj@thj.no)
157 162
 * [0x70b1a5](https://github.com/0x70b1a5)
158 163
 * [gled-rs](https://github.com/gled-rs)
164
+* [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
159 165
 * [R0ckweb](https://github.com/R0ckweb)
160 166
 * [caasi](https://github.com/caasi)
161 167
 * [esetomo](https://github.com/esetomo)
@@ -168,6 +174,7 @@ and provided thanks to the work of the following contributors:
168 174
 * [vahnj](https://github.com/vahnj)
169 175
 * [ikuradon](https://github.com/ikuradon)
170 176
 * [AndreLewin](https://github.com/AndreLewin)
177
+* [rinsuki](https://github.com/rinsuki)
171 178
 * [redtachyons](https://github.com/redtachyons)
172 179
 * [thurloat](https://github.com/thurloat)
173 180
 * [aaribaud](https://github.com/aaribaud)
@@ -188,14 +195,12 @@ and provided thanks to the work of the following contributors:
188 195
 * [Fjoerfoks](https://github.com/Fjoerfoks)
189 196
 * [fmauNeko](https://github.com/fmauNeko)
190 197
 * [gloaec](https://github.com/gloaec)
191
-* [greysteil](https://github.com/greysteil)
192 198
 * [unstabler](https://github.com/unstabler)
193 199
 * [potato4d](https://github.com/potato4d)
194 200
 * [h-izumi](https://github.com/h-izumi)
195 201
 * [ErikXXon](https://github.com/ErikXXon)
196 202
 * [ian-kelling](https://github.com/ian-kelling)
197 203
 * [immae](https://github.com/immae)
198
-* [Reverite](https://github.com/Reverite)
199 204
 * [foozmeat](https://github.com/foozmeat)
200 205
 * [jasonrhodes](https://github.com/jasonrhodes)
201 206
 * [Jason Snell](mailto:jason@newrelic.com)
@@ -232,6 +237,8 @@ and provided thanks to the work of the following contributors:
232 237
 * [zacanger](https://github.com/zacanger)
233 238
 * [amazedkoumei](https://github.com/amazedkoumei)
234 239
 * [anon5r](https://github.com/anon5r)
240
+* [aus-social](https://github.com/aus-social)
241
+* [imbsky](https://github.com/imbsky)
235 242
 * [bsky](mailto:me@imbsky.net)
236 243
 * [chr-1x](https://github.com/chr-1x)
237 244
 * [codl](https://github.com/codl)
@@ -249,12 +256,12 @@ and provided thanks to the work of the following contributors:
249 256
 * [oliverkeeble](https://github.com/oliverkeeble)
250 257
 * [pinfort](https://github.com/pinfort)
251 258
 * [rbaumert](https://github.com/rbaumert)
259
+* [rhoio](https://github.com/rhoio)
252 260
 * [trwnh](https://github.com/trwnh)
253 261
 * [usagi-f](https://github.com/usagi-f)
254 262
 * [vidarlee](https://github.com/vidarlee)
255 263
 * [vjackson725](https://github.com/vjackson725)
256 264
 * [wxcafe](https://github.com/wxcafe)
257
-* [rinsuki](https://github.com/rinsuki)
258 265
 * [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
259 266
 * [cygnan](https://github.com/cygnan)
260 267
 * [Awea](https://github.com/Awea)
@@ -282,6 +289,7 @@ and provided thanks to the work of the following contributors:
282 289
 * [ameliavoncat](https://github.com/ameliavoncat)
283 290
 * [ilpianista](https://github.com/ilpianista)
284 291
 * [Andreas Drop](mailto:andy@remline.de)
292
+* [andi1984](https://github.com/andi1984)
285 293
 * [schas002](https://github.com/schas002)
286 294
 * [abackstrom](https://github.com/abackstrom)
287 295
 * [jumbosushi](https://github.com/jumbosushi)
@@ -303,8 +311,9 @@ and provided thanks to the work of the following contributors:
303 311
 * [chriswk](https://github.com/chriswk)
304 312
 * [csu](https://github.com/csu)
305 313
 * [kklleemm](https://github.com/kklleemm)
314
+* [colindean](https://github.com/colindean)
306 315
 * [dachinat](https://github.com/dachinat)
307
-* [monsterpit-daggertooth](https://github.com/monsterpit-daggertooth)
316
+* [multiple-creatures](https://github.com/multiple-creatures)
308 317
 * [watilde](https://github.com/watilde)
309 318
 * [daprice](https://github.com/daprice)
310 319
 * [dar5hak](https://github.com/dar5hak)
@@ -328,14 +337,17 @@ and provided thanks to the work of the following contributors:
328 337
 * [espenronnevik](https://github.com/espenronnevik)
329 338
 * [Finariel](https://github.com/Finariel)
330 339
 * [siuying](https://github.com/siuying)
340
+* [GenbuHase](https://github.com/GenbuHase)
331 341
 * [hattori6789](https://github.com/hattori6789)
332 342
 * [algernon](https://github.com/algernon)
333 343
 * [Fastbyte01](https://github.com/Fastbyte01)
344
+* [Gomasy](https://github.com/Gomasy)
334 345
 * [myfreeweb](https://github.com/myfreeweb)
335 346
 * [gfaivre](https://github.com/gfaivre)
336 347
 * [Fiaxhs](https://github.com/Fiaxhs)
337 348
 * [reedcourty](https://github.com/reedcourty)
338 349
 * [anneau](https://github.com/anneau)
350
+* [lanodan](https://github.com/lanodan)
339 351
 * [Harmon758](https://github.com/Harmon758)
340 352
 * [HellPie](https://github.com/HellPie)
341 353
 * [Habu-Kagumba](https://github.com/Habu-Kagumba)
@@ -360,6 +372,7 @@ and provided thanks to the work of the following contributors:
360 372
 * [jguerder](https://github.com/jguerder)
361 373
 * [Jehops](https://github.com/Jehops)
362 374
 * [joshuap](https://github.com/joshuap)
375
+* [YuleZ](https://github.com/YuleZ)
363 376
 * [Tiwy57](https://github.com/Tiwy57)
364 377
 * [xuv](https://github.com/xuv)
365 378
 * [Jnsll](https://github.com/Jnsll)
@@ -388,6 +401,7 @@ and provided thanks to the work of the following contributors:
388 401
 * [otsune](https://github.com/otsune)
389 402
 * [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
390 403
 * [matt-auckland](https://github.com/matt-auckland)
404
+* [webroo](https://github.com/webroo)
391 405
 * [matthiasbeyer](https://github.com/matthiasbeyer)
392 406
 * [mattjmattj](https://github.com/mattjmattj)
393 407
 * [mtparet](https://github.com/mtparet)
@@ -400,6 +414,7 @@ and provided thanks to the work of the following contributors:
400 414
 * [mike-burns](https://github.com/mike-burns)
401 415
 * [verymilan](https://github.com/verymilan)
402 416
 * [milmazz](https://github.com/milmazz)
417
+* [premist](https://github.com/premist)
403 418
 * [Mnkai](https://github.com/Mnkai)
404 419
 * [mitchhentges](https://github.com/mitchhentges)
405 420
 * [moritzheiber](https://github.com/moritzheiber)
@@ -413,7 +428,7 @@ and provided thanks to the work of the following contributors:
413 428
 * [vonneudeck](https://github.com/vonneudeck)
414 429
 * [Ninetailed](https://github.com/Ninetailed)
415 430
 * [k24](https://github.com/k24)
416
-* [Noiob](mailto:noiob@users.noreply.github.com)
431
+* [noiob](https://github.com/noiob)
417 432
 * [kwaio](https://github.com/kwaio)
418 433
 * [norayr](https://github.com/norayr)
419 434
 * [joyeusenoelle](https://github.com/joyeusenoelle)
@@ -425,7 +440,6 @@ and provided thanks to the work of the following contributors:
425 440
 * [Pangoraw](https://github.com/Pangoraw)
426 441
 * [peterkeen](https://github.com/peterkeen)
427 442
 * [pgate](https://github.com/pgate)
428
-* [qguv](https://github.com/qguv)
429 443
 * [remram44](https://github.com/remram44)
430 444
 * [retokromer](https://github.com/retokromer)
431 445
 * [rfwatson](https://github.com/rfwatson)
@@ -436,6 +450,7 @@ and provided thanks to the work of the following contributors:
436 450
 * [staticsafe](https://github.com/staticsafe)
437 451
 * [snwh](https://github.com/snwh)
438 452
 * [sts10](https://github.com/sts10)
453
+* [sascha-sl](https://github.com/sascha-sl)
439 454
 * [skoji](https://github.com/skoji)
440 455
 * [ScienJus](https://github.com/ScienJus)
441 456
 * [larkinscott](https://github.com/larkinscott)
@@ -450,20 +465,20 @@ and provided thanks to the work of the following contributors:
450 465
 * [Sina Mashek](mailto:sina@mashek.xyz)
451 466
 * [sossii](https://github.com/sossii)
452 467
 * [SpankyWorks](https://github.com/SpankyWorks)
453
-* [StefOfficiel](https://github.com/StefOfficiel)
454
-* [svetlik](https://github.com/svetlik)
455
-* [dereckson](https://github.com/dereckson)
456
-* [phaedryx](https://github.com/phaedryx)
457
-* [takp](https://github.com/takp)
458
-* [tkusano](https://github.com/tkusano)
459
-* [TakesxiSximada](https://github.com/TakesxiSximada)
460
-* [TheInventrix](https://github.com/TheInventrix)
461
-* [shug0](https://github.com/shug0)
462
-* [Fortyseven](https://github.com/Fortyseven)
463
-* [tobypinder](https://github.com/tobypinder)
464
-* [tomosm](https://github.com/tomosm)
465
-* [TomoyaShibata](https://github.com/TomoyaShibata)
466
-* [treyssatvincent](https://github.com/treyssatvincent)
468
+* [StefOfficiel](mailto:pichard.stephane@free.fr)
469
+* [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
470
+* [Sébastien Santoro](mailto:dereckson@espace-win.org)
471
+* [Tad Thorley](mailto:phaedryx@users.noreply.github.com)
472
+* [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
473
+* [Takayuki KUSANO](mailto:github@tkusano.jp)
474
+* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
475
+* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
476
+* [Thomas Alberola](mailto:thomas@needacoffee.fr)
477
+* [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
478
+* [Toby Pinder](mailto:gigitrix@gmail.com)
479
+* [Tomonori Murakami](mailto:crosslife777@gmail.com)
480
+* [TomoyaShibata](mailto:wind.of.hometown@gmail.com)
481
+* [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
467 482
 * [Udo Kramer](mailto:optik@fluffel.io)
468 483
 * [Una](mailto:una@unascribed.com)
469 484
 * [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
@@ -489,6 +504,7 @@ and provided thanks to the work of the following contributors:
489 504
 * [benklop](mailto:benklop@gmail.com)
490 505
 * [bsky](mailto:git@imbsky.net)
491 506
 * [caesarologia](mailto:lopesgemelli.1@gmail.com)
507
+* [cbayerlein](mailto:c.bayerlein@gmail.com)
492 508
 * [chrolis](mailto:chrolis@users.noreply.github.com)
493 509
 * [cormo](mailto:cormorant2+github@gmail.com)
494 510
 * [d0p1](mailto:dopi-sama@hush.com)
@@ -500,19 +516,23 @@ and provided thanks to the work of the following contributors:
500 516
 * [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
501 517
 * [haosbvnker](mailto:github@chaosbunker.com)
502 518
 * [isati](mailto:phil@juchnowi.cz)
519
+* [jacob](mailto:jacobherringtondeveloper@gmail.com)
503 520
 * [jenn kaplan](mailto:me@jkap.io)
504 521
 * [jirayudech](mailto:jirayudech@gmail.com)
522
+* [jooops](mailto:joops@autistici.org)
505 523
 * [jukper](mailto:jukkaperanto@gmail.com)
506 524
 * [jumoru](mailto:jumoru@mailbox.org)
507 525
 * [karlyeurl](mailto:karl.yeurl@gmail.com)
508 526
 * [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
509 527
 * [kuro5hin](mailto:rusty@kuro5hin.org)
528
+* [luzpaz](mailto:luzpaz@users.noreply.github.com)
510 529
 * [maxypy](mailto:maxime@mpigou.fr)
511 530
 * [mhe](mailto:mail@marcus-herrmann.com)
512 531
 * [mimikun](mailto:dzdzble_effort_311@outlook.jp)
513 532
 * [mshrtkch](mailto:mshrtkch@users.noreply.github.com)
514 533
 * [muan](mailto:muan@github.com)
515 534
 * [neetshin](mailto:neetshin@neetsh.in)
535
+* [nightpool](mailto:nightpool@users.noreply.github.com)
516 536
 * [rch850](mailto:rich850@gmail.com)
517 537
 * [roikale](mailto:roikale@users.noreply.github.com)
518 538
 * [rysiekpl](mailto:rysiek@hackerspace.pl)
@@ -524,6 +544,7 @@ and provided thanks to the work of the following contributors:
524 544
 * [tackeyy](mailto:mailto.takita.yusuke@gmail.com)
525 545
 * [tateisu](mailto:tateisu@gmail.com)
526 546
 * [tmyt](mailto:shigure@refy.net)
547
+* [trevDev()](mailto:trev@trevdev.ca)
527 548
 * [utam0k](mailto:k0ma@utam0k.jp)
528 549
 * [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
529 550
 * [walfie](mailto:walfington@gmail.com)

+ 107
- 0
CHANGELOG.md View File

@@ -0,0 +1,107 @@
1
+Changelog
2
+=========
3
+
4
+All notable changes to this project will be documented in this file.
5
+
6
+## [Unreleased]
7
+### Added
8
+
9
+- Add link ownership verification (#8703)
10
+- Add conversations API (#8832)
11
+- Add limit for the number of people that can be followed from one account (#8807)
12
+- Add admin setting to customize mascot (#8766)
13
+- Add support for more granular ActivityPub audiences from other software, i.e. circles (#8950)
14
+- Add option to block all reports from a domain (#8830)
15
+- Add user preference to always expand toots marked with content warnings (#8762)
16
+- Add user preference to always hide all media (#8569)
17
+- Add `force_login` param to OAuth authorize page (#8655)
18
+- Add `tootctl accounts backup` (#8642, #8811)
19
+- Add `tootctl accounts create` (#8642, #8811)
20
+- Add `tootctl accounts cull` (#8642, #8811)
21
+- Add `tootctl accounts delete` (#8642, #8811)
22
+- Add `tootctl accounts modify` (#8642, #8811)
23
+- Add `tootctl accounts refresh` (#8642, #8811)
24
+- Add `tootctl feeds build` (#8642, #8811)
25
+- Add `tootctl feeds clear` (#8642, #8811)
26
+- Add `tootctl settings registrations open` (#8642, #8811)
27
+- Add `tootctl settings registrations close` (#8642, #8811)
28
+- Add `min_id` param to REST API to support backwards pagination (#8736)
29
+- Add a confirmation dialog when hitting reply and the compose box isn't empty (#8893)
30
+- Add PostgreSQL disk space growth tracking in PGHero (#8906)
31
+- Add button for disabling local account to report quick actions bar (#9024)
32
+- Add Czech language (#8594)
33
+- Add `Clear-Site-Data` header when logging out (#8627)
34
+- Add `same-site` (`lax`) attribute to cookies (#8626)
35
+- Add support for styled scrollbars in Firefox Nightly (#8653)
36
+- Add highlight to the active tab in web UI profiles (#8673)
37
+- Add auto-focus for comment textarea in report modal (#8689)
38
+- Add auto-focus for emoji picker's search field (#8688)
39
+- Add nginx and systemd templates to `dist/` directory (#8770)
40
+- Add support for `/.well-known/change-password` (#8828)
41
+- Add option to override FFMPEG binary path (#8855)
42
+- Add `dns-prefetch` tag when using different host for assets or uploads (#8942)
43
+- Add `description` meta tag (#8941)
44
+- Add `Content-Security-Policy` header (#8957)
45
+- Add cache for the instance info API (#8765)
46
+
47
+### Changed
48
+
49
+- Change forms design (#8703)
50
+- Change reports overview to group by target account (#8674)
51
+- Change web UI to show "read more" link on overly long in-stream statuses (#8205)
52
+- Change design of direct messages column (#8832, #9022)
53
+- Change home timelines to exclude DMs (#8940)
54
+- Change list timelines to exclude all replies (#8683)
55
+- Change admin accounts UI default sort to most recent (#8813)
56
+- Change documentation URL in the UI (#8898)
57
+- Change style of success and failure messages (#8973)
58
+- Change DM filtering to always allow DMs from staff (#8993)
59
+- Change recommended Ruby version to 2.5.3 (#9003)
60
+
61
+### Deprecated
62
+
63
+- `GET /api/v1/timelines/direct` → `GET /api/v1/conversations` (#8832)
64
+- `POST /api/v1/notifications/dismiss` → `POST /api/v1/notifications/:id/dismiss` (#8905)
65
+
66
+### Removed
67
+
68
+- Remove "on this device" label in column push settings (#8704)
69
+- Remove rake tasks in favour of tootctl commands (#8675)
70
+
71
+### Fixed
72
+
73
+- Fix remote statuses using instance's default locale if no language given (#8861)
74
+- Fix streaming API not exiting when port or socket is unavailable (#9023)
75
+- Fix network calls being performed in database transaction in ActivityPub handler (#8951)
76
+- Fix dropdown arrow position (#8637)
77
+- Fix first element of dropdowns being focused even if not using keyboard (#8679)
78
+- Fix tootctl requiring `bundle exec` invocation (#8619)
79
+- Fix public pages not using animation preference for avatars (#8614)
80
+- Fix OEmbed/OpenGraph cards not understanding relative URLs (#8669)
81
+- Fix some dark emojis not having a white outline (#8597)
82
+- Fix media description not being displayed in various media modals (#8678)
83
+- Fix generated URLs of desktop notifications missing base URL (#8758)
84
+- Fix RTL styles (#8764, #8767, #8823, #8897, #9005, #9007, #9018, #9021)
85
+- Fix crash in streaming API when tag param missing (#8955)
86
+- Fix hotkeys not working when no element is focused (#8998)
87
+- Fix some hotkeys not working on detailed status view (#9006)
88
+
89
+## [2.5.2] - 2018-10-12
90
+### Security
91
+
92
+- Fix XSS vulnerability (#8959)
93
+
94
+## [2.5.1] - 2018-10-07
95
+### Fixed
96
+
97
+- Fix database migrations for PostgreSQL below 9.5 (#8903)
98
+- Fix class autoloading issue in ActivityPub Create handler (#8820)
99
+- Fix cache statistics not being sent via statsd when statsd enabled (#8831)
100
+- Bump puma from 3.11.4 to 3.12.0 (#8883)
101
+
102
+### Security
103
+
104
+- Fix some local images not having their EXIF metadata stripped on upload (#8714)
105
+- Fix being able to enable a disabled relay via ActivityPub Accept handler (#8864)
106
+- Bump nokogiri from 1.8.4 to 1.8.5 (#8881)
107
+- 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
32 32
 <blockquote>
33 33
 
34 34
 CONTRIBUTING
35
-============
35
+=======
36
+Contributing
36 37
 
37
-There are three ways in which you can contribute to this repository:
38
+Thank you for considering contributing to Mastodon 🐘 
38 39
 
39
-1. By improving the documentation
40
-2. By working on the back-end application
41
-3. By working on the front-end application
40
+You can contribute in the following ways:
42 41
 
43
-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).
42
+- Finding and reporting bugs
43
+- Translating the Mastodon interface into various languages
44
+- Contributing code to Mastodon by fixing bugs or implementing features
45
+- Improving the documentation
44 46
 
45
-Below are the guidelines for working on pull requests:
47
+## Bug reports
46 48
 
47
-## General
49
+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.
48 50
 
49
-- 2 spaces indentation
51
+## Translations
50 52
 
51
-## Documentation
52
-
53
-- No spelling mistakes
54
-- No orthographic mistakes
55
-- No Markdown syntax errors
56
-
57
-## Requirements
58
-
59
-- Ruby
60
-- Node.js
61
-- PostgreSQL
62
-- Redis
63
-- Nginx (optional)
64
-
65
-## Back-end application
53
+You can submit translations via [Weblate](https://weblate.joinmastodon.org/). They are periodically merged into the codebase.
66 54
 
67
-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.
68
-
69
-* The codebase is not perfect, at the time of writing, but it is expected that you do not introduce new code style violations
70
-* The rspec test suite must pass
71
-* 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
72
-* If you are introducing new strings to the user interface, they must be using localization methods
55
+[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)
73 56
 
74
-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.
57
+## Pull requests
75 58
 
76
-## Front-end application
59
+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.
77 60
 
78
-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.
61
+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.
79 62
 
80
-* Avoid grave ESLint violations
81
-* Verify that your changes work
82
-* If you are introducing new strings, they must be using localization methods
63
+**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind:
83 64
 
84
-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.
65
+- Unit and integration tests (rspec, jest)
66
+- Code style rules (rubocop, eslint)
67
+- Normalization of locale files (i18n-tasks)
85 68
 
86
-## Translate
69
+## Documentation
87 70
 
88
-You can contribute to translating Mastodon via Weblate at [weblate.joinmastodon.org](https://weblate.joinmastodon.org/).
89
-[![Mastodon translation statistics by language](https://weblate.joinmastodon.org/widgets/mastodon/-/multi-auto.svg)](https://weblate.joinmastodon.org/)
71
+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).
90 72
 
91 73
 </blockquote>

+ 4
- 4
Gemfile View File

@@ -16,8 +16,8 @@ gem 'pghero', '~> 2.2'
16 16
 gem 'dotenv-rails', '~> 2.5'
17 17
 
18 18
 gem 'aws-sdk-s3', '~> 1.21', require: false
19
-gem 'fog-core', '~> 2.1'
20
-gem 'fog-openstack', '~> 1.0', require: false
19
+gem 'fog-core', '<= 2.1.0'
20
+gem 'fog-openstack', '~> 0.3', require: false
21 21
 gem 'paperclip', '~> 6.0'
22 22
 gem 'paperclip-av-transcoder', '~> 0.6'
23 23
 gem 'streamio-ffmpeg', '~> 3.0'
@@ -64,7 +64,7 @@ gem 'oj', '~> 3.6'
64 64
 gem 'ostatus2', '~> 2.0'
65 65
 gem 'ox', '~> 2.10'
66 66
 gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
67
-gem 'pundit', '~> 1.1'
67
+gem 'pundit', '~> 2.0'
68 68
 gem 'premailer-rails'
69 69
 gem 'rack-attack', '~> 5.4'
70 70
 gem 'rack-cors', '~> 1.0', require: 'rack/cors'
@@ -82,7 +82,7 @@ gem 'simple-navigation', '~> 4.0'
82 82
 gem 'simple_form', '~> 4.0'
83 83
 gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
84 84
 gem 'stoplight', '~> 2.1.3'
85
-gem 'strong_migrations', '~> 0.2'
85
+gem 'strong_migrations', '~> 0.3'
86 86
 gem 'tty-command', '~> 0.8', require: false
87 87
 gem 'tty-prompt', '~> 0.17', require: false
88 88
 gem 'twitter-text', '~> 1.14'

+ 15
- 15
Gemfile.lock View File

@@ -182,7 +182,7 @@ GEM
182 182
     docile (1.3.0)
183 183
     domain_name (0.5.20180417)
184 184
       unf (>= 0.0.5, < 1.0.0)
185
-    doorkeeper (5.0.0)
185
+    doorkeeper (5.0.1)
186 186
       railties (>= 4.2)
187 187
     dotenv (2.5.0)
188 188
     dotenv-rails (2.5.0)
@@ -211,7 +211,7 @@ GEM
211 211
     fast_blank (1.0.0)
212 212
     fastimage (2.1.4)
213 213
     ffi (1.9.25)
214
-    fog-core (2.1.2)
214
+    fog-core (2.1.0)
215 215
       builder
216 216
       excon (~> 0.58)
217 217
       formatador (~> 0.2)
@@ -219,8 +219,8 @@ GEM
219 219
     fog-json (1.2.0)
220 220
       fog-core
221 221
       multi_json (~> 1.10)
222
-    fog-openstack (1.0.3)
223
-      fog-core (~> 2.1)
222
+    fog-openstack (0.3.7)
223
+      fog-core (>= 1.45, <= 2.1.0)
224 224
       fog-json (>= 1.0)
225 225
       ipaddress (>= 0.8)
226 226
     formatador (0.2.5)
@@ -271,7 +271,7 @@ GEM
271 271
     httplog (1.1.1)
272 272
       rack (>= 1.0)
273 273
       rainbow (>= 2.0.0)
274
-    i18n (1.1.0)
274
+    i18n (1.1.1)
275 275
       concurrent-ruby (~> 1.0)
276 276
     i18n-tasks (0.9.25)
277 277
       activesupport (>= 4.0.2)
@@ -360,7 +360,7 @@ GEM
360 360
       concurrent-ruby (~> 1.0.0)
361 361
       sidekiq (>= 3.5.0)
362 362
       statsd-ruby (~> 1.2.0)
363
-    oj (3.6.11)
363
+    oj (3.6.12)
364 364
     omniauth (1.8.1)
365 365
       hashie (>= 3.4.6, < 3.6.0)
366 366
       rack (>= 1.6.2, < 3)
@@ -417,7 +417,7 @@ GEM
417 417
       pry (>= 0.10.4)
418 418
     public_suffix (3.0.3)
419 419
     puma (3.12.0)
420
-    pundit (1.1.0)
420
+    pundit (2.0.0)
421 421
       activesupport (>= 3.0.0)
422 422
     raabro (1.1.6)
423 423
     rack (2.0.5)
@@ -587,7 +587,7 @@ GEM
587 587
     stoplight (2.1.3)
588 588
     streamio-ffmpeg (3.0.2)
589 589
       multi_json (~> 1.8)
590
-    strong_migrations (0.2.3)
590
+    strong_migrations (0.3.1)
591 591
       activerecord (>= 3.2.0)
592 592
     temple (0.8.0)
593 593
     terminal-table (1.8.0)
@@ -618,7 +618,7 @@ GEM
618 618
       unf (~> 0.1.0)
619 619
     tzinfo (1.2.5)
620 620
       thread_safe (~> 0.1)
621
-    tzinfo-data (1.2018.5)
621
+    tzinfo-data (1.2018.6)
622 622
       tzinfo (>= 1.0.0)
623 623
     unf (0.1.4)
624 624
       unf_ext
@@ -680,8 +680,8 @@ DEPENDENCIES
680 680
   faker (~> 1.9)
681 681
   fast_blank (~> 1.0)
682 682
   fastimage
683
-  fog-core (~> 2.1)
684
-  fog-openstack (~> 1.0)
683
+  fog-core (<= 2.1.0)
684
+  fog-openstack (~> 0.3)
685 685
   fuubar (~> 2.3)
686 686
   goldfinger (~> 2.1)
687 687
   hamlit-rails (~> 0.2)
@@ -727,7 +727,7 @@ DEPENDENCIES
727 727
   pry-byebug (~> 3.6)
728 728
   pry-rails (~> 0.3)
729 729
   puma (~> 3.12)
730
-  pundit (~> 1.1)
730
+  pundit (~> 2.0)
731 731
   rack-attack (~> 5.4)
732 732
   rack-cors (~> 1.0)
733 733
   rails (~> 5.2.1)
@@ -755,7 +755,7 @@ DEPENDENCIES
755 755
   stackprof
756 756
   stoplight (~> 2.1.3)
757 757
   streamio-ffmpeg (~> 3.0)
758
-  strong_migrations (~> 0.2)
758
+  strong_migrations (~> 0.3)
759 759
   thor (~> 0.20)
760 760
   tty-command (~> 0.8)
761 761
   tty-prompt (~> 0.17)
@@ -766,7 +766,7 @@ DEPENDENCIES
766 766
   webpush
767 767
 
768 768
 RUBY VERSION
769
-   ruby 2.5.0p0
769
+   ruby 2.5.3p105
770 770
 
771 771
 BUNDLED WITH
772
-   1.16.5
772
+   1.16.6

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

@@ -46,7 +46,7 @@ module Admin
46 46
     end
47 47
 
48 48
     def resource_params
49
-      params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
49
+      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :retroactive)
50 50
     end
51 51
 
52 52
     def retroactive_unblock?

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

@@ -44,6 +44,14 @@ module Admin
44 44
       when 'resolve'
45 45
         @report.resolve!(current_account)
46 46
         log_action :resolve, @report
47
+      when 'disable'
48
+        @report.resolve!(current_account)
49
+        @report.target_account.user.disable!
50
+
51
+        log_action :resolve, @report
52
+        log_action :disable, @report.target_account.user
53
+
54
+        resolve_all_target_account_reports
47 55
       when 'silence'
48 56
         @report.resolve!(current_account)
49 57
         @report.target_account.update!(silenced: true)
@@ -55,6 +63,7 @@ module Admin
55 63
       else
56 64
         raise ActiveRecord::RecordNotFound
57 65
       end
66
+
58 67
       @report.reload
59 68
     end
60 69
 

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

@@ -3,9 +3,11 @@
3 3
 class Api::V1::ConversationsController < Api::BaseController
4 4
   LIMIT = 20
5 5
 
6
-  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
6
+  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :index
7
+  before_action -> { doorkeeper_authorize! :write, :'write:conversations' }, except: :index
7 8
   before_action :require_user!
8
-  after_action :insert_pagination_headers
9
+  before_action :set_conversation, except: :index
10
+  after_action :insert_pagination_headers, only: :index
9 11
 
10 12
   respond_to :json
11 13
 
@@ -14,8 +16,22 @@ class Api::V1::ConversationsController < Api::BaseController
14 16
     render json: @conversations, each_serializer: REST::ConversationSerializer
15 17
   end
16 18
 
19
+  def read
20
+    @conversation.update!(unread: false)
21
+    render json: @conversation, serializer: REST::ConversationSerializer
22
+  end
23
+
24
+  def destroy
25
+    @conversation.destroy!
26
+    render_empty
27
+  end
28
+
17 29
   private
18 30
 
31
+  def set_conversation
32
+    @conversation = AccountConversation.where(account: current_account).find(params[:id])
33
+  end
34
+
19 35
   def paginated_conversations
20 36
     AccountConversation.where(account: current_account)
21 37
                        .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 @@
1 1
 # frozen_string_literal: true
2 2
 
3 3
 class Api::V1::ReportsController < Api::BaseController
4
-  before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create]
5 4
   before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
6 5
   before_action :require_user!
7 6
 

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

@@ -84,7 +84,7 @@ module ApplicationHelper
84 84
   end
85 85
 
86 86
   def cdn_host
87
-    ENV['CDN_HOST'].presence
87
+    Rails.configuration.action_controller.asset_host
88 88
   end
89 89
 
90 90
   def cdn_host?
@@ -92,10 +92,10 @@ module ApplicationHelper
92 92
   end
93 93
 
94 94
   def storage_host
95
-    ENV['S3_ALIAS_HOST'].presence || ENV['S3_CLOUDFRONT_HOST'].presence
95
+    "https://#{ENV['S3_ALIAS_HOST'].presence || ENV['S3_CLOUDFRONT_HOST']}"
96 96
   end
97 97
 
98 98
   def storage_host?
99
-    storage_host.present?
99
+    ENV['S3_ALIAS_HOST'].present? || ENV['S3_CLOUDFRONT_HOST'].present?
100 100
   end
101 101
 end

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

@@ -2,18 +2,6 @@
2 2
 
3 3
 import { delegate } from 'rails-ujs';
4 4
 
5
-function handleDeleteStatus(event) {
6
-  const [data] = event.detail;
7
-  const element = document.querySelector(`[data-id="${data.id}"]`);
8
-  if (element) {
9
-    element.parentNode.removeChild(element);
10
-  }
11
-}
12
-
13
-[].forEach.call(document.querySelectorAll('.trash-button'), (content) => {
14
-  content.addEventListener('ajax:success', handleDeleteStatus);
15
-});
16
-
17 5
 const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
18 6
 
19 7
 delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
@@ -24,6 +12,7 @@ delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
24 12
 
25 13
 delegate(document, batchCheckboxClassName, 'change', () => {
26 14
   const checkAllElement = document.querySelector('#batch_checkbox_all');
15
+
27 16
   if (checkAllElement) {
28 17
     checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
29 18
     checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
@@ -43,8 +32,14 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => {
43 32
 });
44 33
 
45 34
 delegate(document, '#domain_block_severity', 'change', ({ target }) => {
46
-  const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
35
+  const rejectMediaDiv   = document.querySelector('.input.with_label.domain_block_reject_media');
36
+  const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports');
37
+
47 38
   if (rejectMediaDiv) {
48 39
     rejectMediaDiv.style.display = (target.value === 'suspend') ? 'none' : 'block';
49 40
   }
41
+
42
+  if (rejectReportsDiv) {
43
+    rejectReportsDiv.style.display = (target.value === 'suspend') ? 'none' : 'block';
44
+  }
50 45
 });

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

@@ -56,7 +56,7 @@ export function changeCompose(text) {
56 56
   };
57 57
 };
58 58
 
59
-export function replyCompose(status, router) {
59
+export function replyCompose(status, routerHistory) {
60 60
   return (dispatch, getState) => {
61 61
     dispatch({
62 62
       type: COMPOSE_REPLY,
@@ -64,7 +64,7 @@ export function replyCompose(status, router) {
64 64
     });
65 65
 
66 66
     if (!getState().getIn(['compose', 'mounted'])) {
67
-      router.push('/statuses/new');
67
+      routerHistory.push('/statuses/new');
68 68
     }
69 69
   };
70 70
 };
@@ -81,7 +81,7 @@ export function resetCompose() {
81 81
   };
82 82
 };
83 83
 
84
-export function mentionCompose(account, router) {
84
+export function mentionCompose(account, routerHistory) {
85 85
   return (dispatch, getState) => {
86 86
     dispatch({
87 87
       type: COMPOSE_MENTION,
@@ -89,12 +89,12 @@ export function mentionCompose(account, router) {
89 89
     });
90 90
 
91 91
     if (!getState().getIn(['compose', 'mounted'])) {
92
-      router.push('/statuses/new');
92
+      routerHistory.push('/statuses/new');
93 93
     }
94 94
   };
95 95
 };
96 96
 
97
-export function directCompose(account, router) {
97
+export function directCompose(account, routerHistory) {
98 98
   return (dispatch, getState) => {
99 99
     dispatch({
100 100
       type: COMPOSE_DIRECT,
@@ -102,12 +102,12 @@ export function directCompose(account, router) {
102 102
     });
103 103
 
104 104
     if (!getState().getIn(['compose', 'mounted'])) {
105
-      router.push('/statuses/new');
105
+      routerHistory.push('/statuses/new');
106 106
     }
107 107
   };
108 108
 };
109 109
 
110
-export function submitCompose() {
110
+export function submitCompose(routerHistory) {
111 111
   return function (dispatch, getState) {
112 112
     const status = getState().getIn(['compose', 'text'], '');
113 113
     const media  = getState().getIn(['compose', 'media_attachments']);
@@ -133,21 +133,22 @@ export function submitCompose() {
133 133
       dispatch(insertIntoTagHistory(response.data.tags, status));
134 134
       dispatch(submitComposeSuccess({ ...response.data }));
135 135
 
136
-      // To make the app more responsive, immediately get the status into the columns
136
+      // To make the app more responsive, immediately push the status
137
+      // into the columns
137 138
 
138
-      const insertIfOnline = (timelineId) => {
139
+      const insertIfOnline = timelineId => {
139 140
         if (getState().getIn(['timelines', timelineId, 'items', 0]) !== null) {
140 141
           dispatch(updateTimeline(timelineId, { ...response.data }));
141 142
         }
142 143
       };
143 144
 
144
-      insertIfOnline('home');
145
-
146
-      if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
145
+      if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0) {
146
+        routerHistory.push('/timelines/direct');
147
+      } else if (response.data.visibility !== 'direct') {
148
+        insertIfOnline('home');
149
+      } else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
147 150
         insertIfOnline('community');
148 151
         insertIfOnline('public');
149
-      } else if (response.data.visibility === 'direct') {
150
-        insertIfOnline('direct');
151 152
       }
152 153
     }).catch(function (error) {
153 154
       dispatch(submitComposeFail(error));

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

@@ -5,11 +5,33 @@ import {
5 5
   importFetchedStatus,
6 6
 } from './importer';
7 7
 
8
+export const CONVERSATIONS_MOUNT   = 'CONVERSATIONS_MOUNT';
9
+export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
10
+
8 11
 export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
9 12
 export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
10 13
 export const CONVERSATIONS_FETCH_FAIL    = 'CONVERSATIONS_FETCH_FAIL';
11 14
 export const CONVERSATIONS_UPDATE        = 'CONVERSATIONS_UPDATE';
12 15
 
16
+export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
17
+
18
+export const mountConversations = () => ({
19
+  type: CONVERSATIONS_MOUNT,
20
+});
21
+
22
+export const unmountConversations = () => ({
23
+  type: CONVERSATIONS_UNMOUNT,
24
+});
25
+
26
+export const markConversationRead = conversationId => (dispatch, getState) => {
27
+  dispatch({
28
+    type: CONVERSATIONS_READ,
29
+    id: conversationId,
30
+  });
31
+
32
+  api(getState).post(`/api/v1/conversations/${conversationId}/read`);
33
+};
34
+
13 35
 export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
14 36
   dispatch(expandConversationsRequest());
15 37
 

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

@@ -0,0 +1,96 @@
1
+import React from 'react';
2
+import PropTypes from 'prop-types';
3
+import ImmutablePropTypes from 'react-immutable-proptypes';
4
+import { autoPlayGif } from '../initial_state';
5
+
6
+export default class AvatarComposite extends React.PureComponent {
7
+
8
+  static propTypes = {
9
+    accounts: ImmutablePropTypes.list.isRequired,
10
+    animate: PropTypes.bool,
11
+    size: PropTypes.number.isRequired,
12
+  };
13
+
14
+  static defaultProps = {
15
+    animate: autoPlayGif,
16
+  };
17
+
18
+  renderItem (account, size, index) {
19
+    const { animate } = this.props;
20
+
21
+    let width  = 50;
22
+    let height = 100;
23
+    let top    = 'auto';
24
+    let left   = 'auto';
25
+    let bottom = 'auto';
26
+    let right  = 'auto';
27
+
28
+    if (size === 1) {
29
+      width = 100;
30
+    }
31
+
32
+    if (size === 4 || (size === 3 && index > 0)) {
33
+      height = 50;
34
+    }
35
+
36
+    if (size === 2) {
37
+      if (index === 0) {
38
+        right = '2px';
39
+      } else {
40
+        left = '2px';
41
+      }
42
+    } else if (size === 3) {
43
+      if (index === 0) {
44
+        right = '2px';
45
+      } else if (index > 0) {
46
+        left = '2px';
47
+      }
48
+
49
+      if (index === 1) {
50
+        bottom = '2px';
51
+      } else if (index > 1) {
52
+        top = '2px';
53
+      }
54
+    } else if (size === 4) {
55
+      if (index === 0 || index === 2) {
56
+        right = '2px';
57
+      }
58
+
59
+      if (index === 1 || index === 3) {
60
+        left = '2px';
61
+      }
62
+
63
+      if (index < 2) {
64
+        bottom = '2px';
65
+      } else {
66
+        top = '2px';
67
+      }
68
+    }
69
+
70
+    const style = {
71
+      left: left,
72
+      top: top,
73
+      right: right,
74
+      bottom: bottom,
75
+      width: `${width}%`,
76
+      height: `${height}%`,
77
+      backgroundSize: 'cover',
78
+      backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
79
+    };
80
+
81
+    return (
82
+      <div key={account.get('id')} style={style} />
83
+    );
84
+  }
85
+
86
+  render() {
87
+    const { accounts, size } = this.props;
88
+
89
+    return (
90
+      <div className='account__avatar-composite' style={{ width: `${size}px`, height: `${size}px` }}>
91
+        {accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))}
92
+      </div>
93
+    );
94
+  }
95
+
96
+}

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

@@ -1,25 +1,29 @@
1 1
 import React from 'react';
2 2
 import ImmutablePropTypes from 'react-immutable-proptypes';
3
-import PropTypes from 'prop-types';
4 3
 
5 4
 export default class DisplayName extends React.PureComponent {
6 5
 
7 6
   static propTypes = {
8 7
     account: ImmutablePropTypes.map.isRequired,
9
-    withAcct: PropTypes.bool,
10
-  };
11
-
12
-  static defaultProps = {
13
-    withAcct: true,
8
+    others: ImmutablePropTypes.list,
14 9
   };
15 10
 
16 11
   render () {
17
-    const { account, withAcct } = this.props;
12
+    const { account, others } = this.props;
18 13
     const displayNameHtml = { __html: account.get('display_name_html') };
19 14
 
15
+    let suffix;
16
+
17
+    if (others && others.size > 1) {
18
+      suffix = `+${others.size}`;
19
+    } else {
20
+      suffix = <span className='display-name__account'>@{account.get('acct')}</span>;
21
+    }
22
+
20 23
     return (
21 24
       <span className='display-name'>
22
-        <bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi> {withAcct && <span className='display-name__account'>@{account.get('acct')}</span>}
25
+        <bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi>
26
+        <span>{suffix}</span>
23 27
       </span>
24 28
     );
25 29
   }

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

@@ -3,6 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
3 3
 import PropTypes from 'prop-types';
4 4
 import Avatar from './avatar';
5 5
 import AvatarOverlay from './avatar_overlay';
6
+import AvatarComposite from './avatar_composite';
6 7
 import RelativeTimestamp from './relative_timestamp';
7 8
 import DisplayName from './display_name';
8 9
 import StatusContent from './status_content';
@@ -45,6 +46,8 @@ class Status extends ImmutablePureComponent {
45 46
   static propTypes = {
46 47
     status: ImmutablePropTypes.map,
47 48
     account: ImmutablePropTypes.map,
49
+    otherAccounts: ImmutablePropTypes.list,
50
+    onClick: PropTypes.func,
48 51
     onReply: PropTypes.func,
49 52
     onFavourite: PropTypes.func,
50 53
     onReblog: PropTypes.func,
@@ -60,6 +63,7 @@ class Status extends ImmutablePureComponent {
60 63
     onToggleHidden: PropTypes.func,
61 64
     muted: PropTypes.bool,
62 65
     hidden: PropTypes.bool,
66
+    unread: PropTypes.bool,
63 67
     onMoveUp: PropTypes.func,
64 68
     onMoveDown: PropTypes.func,
65 69
   };
@@ -74,6 +78,11 @@ class Status extends ImmutablePureComponent {
74 78
   ]
75 79
 
76 80
   handleClick = () => {
81
+    if (this.props.onClick) {
82
+      this.props.onClick();
83
+      return;
84
+    }
85
+
77 86
     if (!this.context.router) {
78 87
       return;
79 88
     }
@@ -158,7 +167,7 @@ class Status extends ImmutablePureComponent {
158 167
     let media = null;
159 168
     let statusAvatar, prepend, rebloggedByText;
160 169
 
161
-    const { intl, hidden, featured } = this.props;
170
+    const { intl, hidden, featured, otherAccounts, unread } = this.props;
162 171
 
163 172
     let { status, account, ...other } = this.props;
164 173
 
@@ -249,9 +258,11 @@ class Status extends ImmutablePureComponent {
249 258
       }
250 259
     }
251 260
 
252
-    if (account === undefined || account === null) {
261
+    if (otherAccounts) {
262
+      statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />;
263
+    } else if (account === undefined || account === null) {
253 264
       statusAvatar = <Avatar account={status.get('account')} size={48} />;
254
-    }else{
265
+    } else {
255 266
       statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
256 267
     }
257 268
 
@@ -269,10 +280,10 @@ class Status extends ImmutablePureComponent {
269 280
 
270 281
     return (
271 282
       <HotKeys handlers={handlers}>
272
-        <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'))}>
283
+        <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'))}>
273 284
           {prepend}
274 285
 
275
-          <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')}>
286
+          <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')}>
276 287
             <div className='status__info'>
277 288
               <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
278 289
 
@@ -281,7 +292,7 @@ class Status extends ImmutablePureComponent {
281 292
                   {statusAvatar}
282 293
                 </div>
283 294
 
284
-                <DisplayName account={status.get('account')} />
295
+                <DisplayName account={status.get('account')} others={otherAccounts} />
285 296
               </a>
286 297
             </div>
287 298
 

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

@@ -31,6 +31,10 @@ const messages = defineMessages({
31 31
 export default @injectIntl
32 32
 class ComposeForm extends ImmutablePureComponent {
33 33
 
34
+  static contextTypes = {
35
+    router: PropTypes.object,
36
+  };
37
+
34 38
   static propTypes = {
35 39
     intl: PropTypes.object.isRequired,
36 40
     text: PropTypes.string.isRequired,
@@ -85,7 +89,7 @@ class ComposeForm extends ImmutablePureComponent {
85 89
       return;
86 90
     }
87 91
 
88
-    this.props.onSubmit();
92
+    this.props.onSubmit(this.context.router.history);
89 93
   }
90 94
 
91 95
   onSuggestionsClearRequested = () => {

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

@@ -164,7 +164,7 @@ class PrivacyDropdown extends React.PureComponent {
164 164
 
165 165
   state = {
166 166
     open: false,
167
-    placement: null,
167
+    placement: 'bottom',
168 168
   };
169 169
 
170 170
   handleToggle = ({ target }) => {

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

@@ -14,6 +14,10 @@ const messages = defineMessages({
14 14
 export default @injectIntl
15 15
 class Upload extends ImmutablePureComponent {
16 16
 
17
+  static contextTypes = {
18
+    router: PropTypes.object,
19
+  };
20
+
17 21
   static propTypes = {
18 22
     media: ImmutablePropTypes.map.isRequired,
19 23
     intl: PropTypes.object.isRequired,
@@ -37,7 +41,7 @@ class Upload extends ImmutablePureComponent {
37 41
 
38 42
   handleSubmit = () => {
39 43
     this.handleInputBlur();
40
-    this.props.onSubmit();
44
+    this.props.onSubmit(this.context.router.history);
41 45
   }
42 46
 
43 47
   handleUndoClick = () => {

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

@@ -33,8 +33,8 @@ const mapDispatchToProps = (dispatch) => ({
33 33
     dispatch(changeCompose(text));
34 34
   },
35 35
 
36
-  onSubmit () {
37
-    dispatch(submitCompose());
36
+  onSubmit (router) {
37
+    dispatch(submitCompose(router));
38 38
   },
39 39
 
40 40
   onClearSuggestions () {

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

@@ -22,8 +22,8 @@ const mapDispatchToProps = dispatch => ({
22 22
     dispatch(openModal('FOCAL_POINT', { id }));
23 23
   },
24 24
 
25
-  onSubmit () {
26
-    dispatch(submitCompose());
25
+  onSubmit (router) {
26
+    dispatch(submitCompose(router));
27 27
   },
28 28
 
29 29
 });

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

@@ -13,6 +13,7 @@ import spring from 'react-motion/lib/spring';
13 13
 import SearchResultsContainer from './containers/search_results_container';
14 14
 import { changeComposing } from '../../actions/compose';
15 15
 import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
16
+import { mascot } from '../../initial_state';
16 17
 
17 18
 const messages = defineMessages({
18 19
   start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@@ -107,7 +108,7 @@ class Compose extends React.PureComponent {
107 108
             <ComposeFormContainer />
108 109
             {multiColumn && (
109 110
               <div className='drawer__inner__mastodon'>
110
-                <img alt='' draggable='false' src={elephantUIPlane} />
111
+                <img alt='' draggable='false' src={mascot || elephantUIPlane} />
111 112
               </div>
112 113
             )}
113 114
           </div>}

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

@@ -2,12 +2,7 @@ import React from 'react';
2 2
 import PropTypes from 'prop-types';
3 3
 import ImmutablePropTypes from 'react-immutable-proptypes';
4 4
 import ImmutablePureComponent from 'react-immutable-pure-component';
5
-import StatusContent from '../../../components/status_content';
6
-import RelativeTimestamp from '../../../components/relative_timestamp';
7
-import DisplayName from '../../../components/display_name';
8
-import Avatar from '../../../components/avatar';
9
-import AttachmentList from '../../../components/attachment_list';
10
-import { HotKeys } from 'react-hotkeys';
5
+import StatusContainer from '../../../containers/status_container';
11 6
 
12 7
 export default class Conversation extends ImmutablePureComponent {
13 8
 
@@ -18,9 +13,11 @@ export default class Conversation extends ImmutablePureComponent {
18 13
   static propTypes = {
19 14
     conversationId: PropTypes.string.isRequired,
20 15
     accounts: ImmutablePropTypes.list.isRequired,
21
-    lastStatus: ImmutablePropTypes.map.isRequired,
16
+    lastStatusId: PropTypes.string,
17
+    unread:PropTypes.bool.isRequired,
22 18
     onMoveUp: PropTypes.func,
23 19
     onMoveDown: PropTypes.func,
20
+    markRead: PropTypes.func.isRequired,
24 21
   };
25 22
 
26 23
   handleClick = () => {
@@ -28,8 +25,13 @@ export default class Conversation extends ImmutablePureComponent {
28 25
       return;
29 26
     }
30 27
 
31
-    const { lastStatus } = this.props;
32
-    this.context.router.history.push(`/statuses/${lastStatus.get('id')}`);
28
+    const { lastStatusId, unread, markRead } = this.props;
29
+
30
+    if (unread) {
31
+      markRead();
32
+    }
33
+
34
+    this.context.router.history.push(`/statuses/${lastStatusId}`);
33 35
   }
34 36
 
35 37
   handleHotkeyMoveUp = () => {
@@ -41,44 +43,20 @@ export default class Conversation extends ImmutablePureComponent {
41 43
   }
42 44
 
43 45
   render () {
44
-    const { accounts, lastStatus, lastAccount } = this.props;
46
+    const { accounts, lastStatusId, unread } = this.props;
45 47
 
46
-    if (lastStatus === null) {
48
+    if (lastStatusId === null) {
47 49
       return null;
48 50
     }
49 51
 
50
-    const handlers = {
51
-      moveDown: this.handleHotkeyMoveDown,
52
-      moveUp: this.handleHotkeyMoveUp,
53
-      open: this.handleClick,
54
-    };
55
-
56
-    let media;
57
-
58
-    if (lastStatus.get('media_attachments').size > 0) {
59
-      media = <AttachmentList compact media={lastStatus.get('media_attachments')} />;
60
-    }
61
-
62 52
     return (
63
-      <HotKeys handlers={handlers}>
64
-        <div className='conversation focusable' tabIndex='0' onClick={this.handleClick} role='button'>
65
-          <div className='conversation__header'>
66
-            <div className='conversation__avatars'>
67
-              <div>{accounts.map(account => <Avatar key={account.get('id')} size={36} account={account} />)}</div>
68
-            </div>
69
-
70
-            <div className='conversation__time'>
71
-              <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
72
-              <br />
73
-              <DisplayName account={lastAccount} withAcct={false} />
74
-            </div>
75
-          </div>
76
-
77
-          <StatusContent status={lastStatus} onClick={this.handleClick} />
78
-
79
-          {media}
80
-        </div>
81
-      </HotKeys>
53
+      <StatusContainer
54
+        id={lastStatusId}
55
+        unread={unread}
56
+        otherAccounts={accounts}
57
+        onMoveUp={this.handleHotkeyMoveUp}
58
+        onMoveDown={this.handleHotkeyMoveDown}
59
+      />
82 60
     );
83 61
   }
84 62
 

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

@@ -1,15 +1,19 @@
1 1
 import { connect } from 'react-redux';
2 2
 import Conversation from '../components/conversation';
3
+import { markConversationRead } from '../../../actions/conversations';
3 4
 
4 5
 const mapStateToProps = (state, { conversationId }) => {
5 6
   const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
6
-  const lastStatus   = state.getIn(['statuses', conversation.get('last_status')], null);
7 7
 
8 8
   return {
9 9
     accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
10
-    lastStatus,
11
-    lastAccount: lastStatus === null ? null : state.getIn(['accounts', lastStatus.get('account')], null),
10
+    unread: conversation.get('unread'),
11
+    lastStatusId: conversation.get('last_status', null),
12 12
   };
13 13
 };
14 14
 
15
-export default connect(mapStateToProps)(Conversation);
15
+const mapDispatchToProps = (dispatch, { conversationId }) => ({
16
+  markRead: () => dispatch(markConversationRead(conversationId)),
17
+});
18
+
19
+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';
3 3
 import PropTypes from 'prop-types';
4 4
 import Column from '../../components/column';
5 5
 import ColumnHeader from '../../components/column_header';
6
-import { expandConversations } from '../../actions/conversations';
6
+import { mountConversations, unmountConversations, expandConversations } from '../../actions/conversations';
7 7
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
8 8
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
9 9
 import { connectDirectStream } from '../../actions/streaming';
@@ -48,11 +48,14 @@ class DirectTimeline extends React.PureComponent {
48 48
   componentDidMount () {
49 49
     const { dispatch } = this.props;
50 50
 
51
+    dispatch(mountConversations());
51 52
     dispatch(expandConversations());
52 53
     this.disconnect = dispatch(connectDirectStream());
53 54
   }
54 55
 
55 56
   componentWillUnmount () {
57
+    this.props.dispatch(unmountConversations());
58
+
56 59
     if (this.disconnect) {
57 60
       this.disconnect();
58 61
       this.disconnect = null;

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

@@ -181,7 +181,7 @@ class Status extends ImmutablePureComponent {
181 181
     if (status.get('reblogged')) {
182 182
       this.props.dispatch(unreblog(status));
183 183
     } else {
184
-      if (e.shiftKey || !boostModal) {
184
+      if ((e && e.shiftKey) || !boostModal) {
185 185
         this.handleModalReblog(status);
186 186
       } else {
187 187
         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 {
460 460
     };
461 461
 
462 462
     return (
463
-      <HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef}>
463
+      <HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
464 464
         <div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
465 465
           <TabsBar />
466 466
 

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

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

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

@@ -15,7 +15,7 @@
15 15
   "account.follows.empty": "هذا المستخدِم لا يتبع أحدًا بعد.",
16 16
   "account.follows_you": "يتابعك",
17 17
   "account.hide_reblogs": "إخفاء ترقيات @{name}",
18
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
18
+  "account.link_verified_on": "تم التحقق مِن مالك هذا الرابط بتاريخ {date}",
19 19
   "account.media": "وسائط",
20 20
   "account.mention": "أُذكُر @{name}",
21 21
   "account.moved_to": "{name} إنتقل إلى :",
@@ -91,8 +91,11 @@
91 91
   "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
92 92
   "confirmations.redraft.confirm": "إزالة و إعادة الصياغة",
93 93
   "confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته ؟ سوف تفقد جميع الإعجابات و الترقيات أما الردود المتصلة به فستُصبِح يتيمة.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
94 96
   "confirmations.unfollow.confirm": "إلغاء المتابعة",
95 97
   "confirmations.unfollow.message": "متأكد من أنك تريد إلغاء متابعة {name} ؟",
98
+  "conversation.last_message": "Last message:",
96 99
   "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
97 100
   "embed.preview": "هكذا ما سوف يبدو عليه :",
98 101
   "emoji_button.activity": "الأنشطة",
@@ -113,9 +116,9 @@
113 116
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
114 117
   "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
115 118
   "empty_column.domain_blocks": "ليس هناك نطاقات مخفية بعد.",
116
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
117
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
118
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
119
+  "empty_column.favourited_statuses": "ليس لديك أية تبويقات مفضلة بعد. عندما ستقوم بالإعجاب بواحد، سيظهر هنا.",
120
+  "empty_column.favourites": "لم يقم أي أحد بالإعجاب بهذا التبويق بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.",
121
+  "empty_column.follow_requests": "ليس عندك أي طلب للمتابعة بعد. سوف تظهر طلباتك هنا إن قمت بتلقي البعض منها.",
119 122
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
120 123
   "empty_column.home": "إنّ الخيط الزمني لصفحتك الرئيسية فارغ. قم بزيارة {public} أو استخدم حقل البحث لكي تكتشف مستخدمين آخرين.",
121 124
   "empty_column.home.public_timeline": "الخيط العام",
@@ -163,7 +166,7 @@
163 166
   "keyboard_shortcuts.reply": "للردّ",
164 167
   "keyboard_shortcuts.requests": "لفتح قائمة طلبات المتابعة",
165 168
   "keyboard_shortcuts.search": "للتركيز على البحث",
166
-  "keyboard_shortcuts.start": "to open \"get started\" column",
169
+  "keyboard_shortcuts.start": "لفتح عمود \"هيا نبدأ\"",
167 170
   "keyboard_shortcuts.toggle_hidden": "لعرض أو إخفاء النص مِن وراء التحذير",
168 171
   "keyboard_shortcuts.toot": "لتحرير تبويق جديد",
169 172
   "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث",
@@ -280,7 +283,7 @@
280 283
   "status.cancel_reblog_private": "إلغاء الترقية",
281 284
   "status.cannot_reblog": "تعذرت ترقية هذا المنشور",
282 285
   "status.delete": "إحذف",
283
-  "status.detailed_status": "Detailed conversation view",
286
+  "status.detailed_status": "تفاصيل المحادثة",
284 287
   "status.direct": "رسالة خاصة إلى @{name}",
285 288
   "status.embed": "إدماج",
286 289
   "status.favourite": "أضف إلى المفضلة",
@@ -294,6 +297,7 @@
294 297
   "status.open": "وسع هذه المشاركة",
295 298
   "status.pin": "تدبيس على الملف الشخصي",
296 299
   "status.pinned": "تبويق مثبَّت",
300
+  "status.read_more": "Read more",
297 301
   "status.reblog": "رَقِّي",
298 302
   "status.reblog_private": "القيام بالترقية إلى الجمهور الأصلي",
299 303
   "status.reblogged_by": "رقّاه {name}",

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

@@ -91,8 +91,11 @@
91 91
   "confirmations.mute.message": "¿De xuru que quies silenciar a {name}?",
92 92
   "confirmations.redraft.confirm": "Delete & redraft",
93 93
   "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.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
94 96
   "confirmations.unfollow.confirm": "Unfollow",
95 97
   "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
98
+  "conversation.last_message": "Last message:",
96 99
   "embed.instructions": "Embed this status on your website by copying the code below.",
97 100
   "embed.preview": "Here is what it will look like:",
98 101
   "emoji_button.activity": "Actividá",
@@ -294,6 +297,7 @@
294 297
   "status.open": "Espander esti estáu",
295 298
   "status.pin": "Pin on profile",
296 299
   "status.pinned": "Pinned toot",
300
+  "status.read_more": "Read more",
297 301
   "status.reblog": "Boost",
298 302
   "status.reblog_private": "Boost to original audience",
299 303
   "status.reblogged_by": "{name} boosted",

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

@@ -91,8 +91,11 @@
91 91
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
92 92
   "confirmations.redraft.confirm": "Delete & redraft",
93 93
   "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.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
94 96
   "confirmations.unfollow.confirm": "Unfollow",
95 97
   "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
98
+  "conversation.last_message": "Last message:",
96 99
   "embed.instructions": "Embed this status on your website by copying the code below.",
97 100
   "embed.preview": "Here is what it will look like:",
98 101
   "emoji_button.activity": "Activity",
@@ -294,6 +297,7 @@
294 297
   "status.open": "Expand this status",
295 298
   "status.pin": "Pin on profile",
296 299
   "status.pinned": "Pinned toot",
300
+  "status.read_more": "Read more",
297 301
   "status.reblog": "Споделяне",
298 302
   "status.reblog_private": "Boost to original audience",
299 303
   "status.reblogged_by": "{name} сподели",

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

@@ -91,8 +91,11 @@
91 91
   "confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
92 92
   "confirmations.redraft.confirm": "Esborrar i refer",
93 93
   "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.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
94 96
   "confirmations.unfollow.confirm": "Deixa de seguir",
95 97
   "confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
98
+  "conversation.last_message": "Last message:",
96 99
   "embed.instructions": "Incrusta aquest estat al lloc web copiant el codi a continuació.",
97 100
   "embed.preview": "Aquí tenim quin aspecte tindrá:",
98 101
   "emoji_button.activity": "Activitat",
@@ -294,6 +297,7 @@
294 297
   "status.open": "Ampliar aquest estat",
295 298
   "status.pin": "Fixat en el perfil",
296 299
   "status.pinned": "Toot fixat",
300
+  "status.read_more": "Read more",
297 301
   "status.reblog": "Impuls",
298 302
   "status.reblog_private": "Impulsar a l'audiència original",
299 303
   "status.reblogged_by": "{name} ha retootejat",

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

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

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

@@ -8,14 +8,14 @@
8 8
   "account.domain_blocked": "Doména skryta",
9 9
   "account.edit_profile": "Upravit profil",
10 10
   "account.endorse": "Představit na profilu",
11
-  "account.follow": "Sleduj",
11
+  "account.follow": "Sledovat",
12 12
   "account.followers": "Sledovatelé",
13 13
   "account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
14 14
   "account.follows": "Sleduje",
15 15
   "account.follows.empty": "Tento uživatel ještě nikoho nesleduje.",
16 16
   "account.follows_you": "Sleduje vás",
17 17
   "account.hide_reblogs": "Skrýt boosty od uživatele @{name}",
18
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
18
+  "account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}",
19 19
   "account.media": "Média",
20 20
   "account.mention": "Zmínit uživatele @{name}",
21 21
   "account.moved_to": "{name} se přesunul/a na:",
@@ -91,8 +91,11 @@
91 91
   "confirmations.mute.message": "Jste si jistý/á, že chcete ignorovat uživatele {name}?",
92 92
   "confirmations.redraft.confirm": "Vymazat a přepsat",
93 93
   "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.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
94 96
   "confirmations.unfollow.confirm": "Přestat sledovat",
95 97
   "confirmations.unfollow.message": "jste si jistý/á, že chcete přestat sledovat uživatele {name}?",
98
+  "conversation.last_message": "Last message:",
96 99
   "embed.instructions": "Pro přidání příspěvku na vaši webovou stránku zkopírujte níže uvedený kód.",
97 100
   "embed.preview": "Takhle to bude vypadat:",
98 101
   "emoji_button.activity": "Aktivita",
@@ -294,6 +297,7 @@
294 297
   "status.open": "Rozbalit tento příspěvek",
295 298
   "status.pin": "Připnout na profil",
296 299
   "status.pinned": "Připnutý toot",
300
+  "status.read_more": "Read more",
297 301
   "status.reblog": "Boostnout",
298 302
   "status.reblog_private": "Boostnout původnímu publiku",
299 303
   "status.reblogged_by": "{name} boostnul/a",

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

@@ -1,335 +1,340 @@
1 1
 {
2
-    "account.badges.bot": "Bot",
3
-    "account.block": "Blociwch @{name}",
4
-    "account.block_domain": "Cuddiwch bopeth rhag {domain}",
5
-    "account.blocked": "Blociwyd",
6
-    "account.direct": "Neges breifat @{name}",
7
-    "account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
8
-    "account.domain_blocked": "Parth wedi ei guddio",
9
-    "account.edit_profile": "Golygu proffil",
10
-    "account.endorse": "Arddangos ar fy mhroffil",
11
-    "account.follow": "Dilyn",
12
-    "account.followers": "Dilynwyr",
13
-    "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
14
-    "account.follows": "Yn dilyn",
15
-    "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
16
-    "account.follows_you": "Yn eich dilyn chi",
17
-    "account.hide_reblogs": "Cuddio bwstiau o @{name}",
18
-    "account.media": "Cyfryngau",
19
-    "account.mention": "Crybwyll @{name}",
20
-    "account.moved_to": "Mae @{name} wedi symud i:",
21
-    "account.mute": "Tawelu @{name}",
22
-    "account.mute_notifications": "Cuddio hysbysiadau o @{name}",
23
-    "account.muted": "Distewyd",
24
-    "account.posts": "Tŵtiau",
25
-    "account.posts_with_replies": "Tŵtiau ac atebion",
26
-    "account.report": "Adroddwch @{name}",
27
-    "account.requested": "Aros am gymeradwyaeth. Cliciwch er mwyn canslo cais dilyn",
28
-    "account.share": "Rhannwch broffil @{name}",
29
-    "account.show_reblogs": "Dangoswch bwstiau o @{name}",
30
-    "account.unblock": "Dadflociwch @{name}",
31
-    "account.unblock_domain": "Dadguddiwch {domain}",
32
-    "account.unendorse": "Peidwch a'i arddangos ar fy mhroffil",
33
-    "account.unfollow": "Daddilynwch",
34
-    "account.unmute": "Dad-dawelu @{name}",
35
-    "account.unmute_notifications": "Dad-dawelu hysbysiadau o @{name}",
36
-    "account.view_full_profile": "Gweld proffil llawn",
37
-    "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
38
-    "alert.unexpected.title": "Wps!",
39
-    "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
40
-    "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
41
-    "bundle_column_error.retry": "Ceisiwch eto",
42
-    "bundle_column_error.title": "Gwall rhwydwaith",
43
-    "bundle_modal_error.close": "Cau",
44
-    "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
45
-    "bundle_modal_error.retry": "Ceiswich eto",
46
-    "column.blocks": "Defnyddwyr a flociwyd",
47
-    "column.community": "Llinell amser lleol",
48
-    "column.direct": "Negeseuon preifat",
49
-    "column.domain_blocks": "Parthau cuddiedig",
50
-    "column.favourites": "Ffefrynnau",
51
-    "column.follow_requests": "Ceisiadau dilyn",
52
-    "column.home": "Hafan",
53
-    "column.lists": "Rhestrau",
54
-    "column.mutes": "Defnyddwyr a ddistewyd",
55
-    "column.notifications": "Hysbysiadau",
56
-    "column.pins": "Tŵtiau wedi eu pinio",
57
-    "column.public": "",
58
-    "column_back_button.label": "Nôl",
59
-    "column_header.hide_settings": "Cuddiwch dewisiadau",
60
-    "column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
61
-    "column_header.moveRight_settings": "Symudwch y golofn i'r dde",
62
-    "column_header.pin": "Piniwch",
63
-    "column_header.show_settings": "Dangos gosodiadau",
64
-    "column_header.unpin": "Dadbiniwch",
65
-    "column_subheading.settings": "Gosodiadau",
66
-    "community.column_settings.media_only": "Cyfryngau yn unig",
67
-    "compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
68
-    "compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
69
-    "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.",
70
-    "compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
71
-    "compose_form.lock_disclaimer.lock": "wedi ei gloi",
72
-    "compose_form.placeholder": "Be syd ar eich meddwl?",
73
-    "compose_form.publish": "Tŵt",
74
-    "compose_form.publish_loud": "{publish}!",
75
-    "compose_form.sensitive.marked": "",
76
-    "compose_form.sensitive.unmarked": "",
77
-    "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
78
-    "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
79
-    "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
80
-    "confirmation_modal.cancel": "Canslo",
81
-    "confirmations.block.confirm": "Blociwch",
82
-    "confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
83
-    "confirmations.delete.confirm": "Dileu",
84
-    "confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
85
-    "confirmations.delete_list.confirm": "Dileu",
86
-    "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
87
-    "confirmations.domain_block.confirm": "",
88
-    "confirmations.domain_block.message": "",
89
-    "confirmations.mute.confirm": "Tawelu",
90
-    "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?",
91
-    "confirmations.redraft.confirm": "Dilëwch & ailddrafftio",
92
-    "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.",
93
-    "confirmations.unfollow.confirm": "Dad-ddilynwch",
94
-    "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
95
-    "embed.instructions": "Mewnblannwch y statws hwn ar eich gwefan drwy gopïo'r côd isod.",
96
-    "embed.preview": "Dyma sut olwg fydd arno:",
97
-    "emoji_button.activity": "Gweithgarwch",
98
-    "emoji_button.custom": "",
99
-    "emoji_button.flags": "Baneri",
100
-    "emoji_button.food": "Bwyd a Diod",
101
-    "emoji_button.label": "Mewnosodwch emoji",
102
-    "emoji_button.nature": "Natur",
103
-    "emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
104
-    "emoji_button.objects": "Gwrthrychau",
105
-    "emoji_button.people": "Pobl",
106
-    "emoji_button.recent": "Defnyddir yn aml",
107
-    "emoji_button.search": "Chwilio...",
108
-    "emoji_button.search_results": "Canlyniadau chwilio",
109
-    "emoji_button.symbols": "Symbolau",
110
-    "emoji_button.travel": "Teithio & Llefydd",
111
-    "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
112
-    "empty_column.community": "",
113
-    "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
114
-    "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
115
-    "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
116
-    "empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
117
-    "empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan dderbyniwch chi un, bydd yn ymddangos yma.",
118
-    "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
119
-    "empty_column.home": "",
120
-    "empty_column.home.public_timeline": "y ffrwd cyhoeddus",
121
-    "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.",
122
-    "empty_column.lists": "Nid oes gennych unrhyw restrau eto. Pan grëwch chi un, mi fydd yn ymddangos yma.",
123
-    "empty_column.mutes": "Nid ydych wedi tawelu unrhyw ddefnyddwyr eto.",
124
-    "empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ac eraill i ddechrau'r sgwrs.",
125
-    "empty_column.public": "Does dim byd yma! Ysgrifennwch rhywbeth yn gyhoeddus, neu dilynwch ddefnyddwyr o INSTANCES eraill i'w lenwi",
126
-    "follow_request.authorize": "Caniatau",
127
-    "follow_request.reject": "Gwrthod",
128
-    "getting_started.developers": "Datblygwyr",
129
-    "getting_started.documentation": "Dogfennaeth",
130
-    "getting_started.find_friends": "Canfod ffrindiau o Twitter",
131
-    "getting_started.heading": "Dechrau",
132
-    "getting_started.invite": "Gwahoddwch bobl",
133
-    "getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.",
134
-    "getting_started.security": "Diogelwch",
135
-    "getting_started.terms": "Telerau Gwasanaeth",
136
-    "home.column_settings.basic": "Syml",
137
-    "home.column_settings.show_reblogs": "",
138
-    "home.column_settings.show_replies": "Dangoswch ymatebion",
139
-    "keyboard_shortcuts.back": "",
140
-    "keyboard_shortcuts.blocked": "i agor rhestr defnyddwyr a flociwyd",
141
-    "keyboard_shortcuts.boost": "",
142
-    "keyboard_shortcuts.column": "",
143
-    "keyboard_shortcuts.compose": "",
144
-    "keyboard_shortcuts.description": "Disgrifiad",
145
-    "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
146
-    "keyboard_shortcuts.down": "i symud lawr yn y rhestr",
147
-    "keyboard_shortcuts.enter": "i agor statws",
148
-    "keyboard_shortcuts.favourite": "i hoffi",
149
-    "keyboard_shortcuts.favourites": "i agor rhestr hoffi",
150
-    "keyboard_shortcuts.federated": "",
151
-    "keyboard_shortcuts.heading": "",
152
-    "keyboard_shortcuts.home": "i agor ffrwd cartref",
153
-    "keyboard_shortcuts.hotkey": "Hotkey",
154
-    "keyboard_shortcuts.legend": "",
155
-    "keyboard_shortcuts.local": "i agor ffrwd lleol",
156
-    "keyboard_shortcuts.mention": "i grybwyll yr awdur",
157
-    "keyboard_shortcuts.muted": "i agor rhestr defnyddwyr a dawelwyd",
158
-    "keyboard_shortcuts.my_profile": "i agor eich proffil",
159
-    "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
160
-    "keyboard_shortcuts.pinned": "",
161
-    "keyboard_shortcuts.profile": "i agor proffil yr awdur",
162
-    "keyboard_shortcuts.reply": "i ateb",
163
-    "keyboard_shortcuts.requests": "i agor rhestr ceisiadau dilyn",
164
-    "keyboard_shortcuts.search": "",
165
-    "keyboard_shortcuts.start": "",
166
-    "keyboard_shortcuts.toggle_hidden": "",
167
-    "keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
168
-    "keyboard_shortcuts.unfocus": "",
169
-    "keyboard_shortcuts.up": "i symud yn uwch yn y rhestr",
170
-    "lightbox.close": "Cau",
171
-    "lightbox.next": "Nesaf",
172
-    "lightbox.previous": "",
173
-    "lists.account.add": "Ychwanegwch at restr",
174
-    "lists.account.remove": "",
175
-    "lists.delete": "Dileu rhestr",
176
-    "lists.edit": "Golygwch restr",
177
-    "lists.new.create": "Ychwanegwch restr",
178
-    "lists.new.title_placeholder": "Teitl rhestr newydd",
179
-    "lists.search": "",
180
-    "lists.subheading": "Eich rhestrau",
181
-    "loading_indicator.label": "Llwytho...",
182
-    "media_gallery.toggle_visible": "",
183
-    "missing_indicator.label": "Heb ei ganfod",
184
-    "missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
185
-    "mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
186
-    "navigation_bar.apps": "Apiau symudol",
187
-    "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
188
-    "navigation_bar.community_timeline": "",
189
-    "navigation_bar.compose": "Cyfansoddwch dŵt newydd",
190
-    "navigation_bar.direct": "Negeseuon preifat",
191
-    "navigation_bar.discover": "Darganfyddwch",
192
-    "navigation_bar.domain_blocks": "Parthau cuddiedig",
193
-    "navigation_bar.edit_profile": "Golygu proffil",
194
-    "navigation_bar.favourites": "Ffefrynnau",
195
-    "navigation_bar.filters": "Geiriau a dawelwyd",
196
-    "navigation_bar.follow_requests": "Ceisiadau dilyn",
197
-    "navigation_bar.info": "",
198
-    "navigation_bar.keyboard_shortcuts": "",
199
-    "navigation_bar.lists": "Rhestrau",
200
-    "navigation_bar.logout": "Allgofnodi",
201
-    "navigation_bar.mutes": "Defnyddwyr a dawelwyd",
202
-    "navigation_bar.personal": "Personol",
203
-    "navigation_bar.pins": "Tŵtiau wedi eu pinio",
204
-    "navigation_bar.preferences": "Dewisiadau",
205
-    "navigation_bar.public_timeline": "",
206
-    "navigation_bar.security": "Diogelwch",
207
-    "notification.favourite": "hoffodd {name} eich statws",
208
-    "notification.follow": "dilynodd {name} chi",
209
-    "notification.mention": "Soniodd {name} amdanoch chi",
210
-    "notification.reblog": "",
211
-    "notifications.clear": "Clirio hysbysiadau",
212
-    "notifications.clear_confirmation": "",
213
-    "notifications.column_settings.alert": "",
214
-    "notifications.column_settings.favourite": "Ffefrynnau:",
215
-    "notifications.column_settings.follow": "Dilynwyr newydd:",
216
-    "notifications.column_settings.mention": "",
217
-    "notifications.column_settings.push": "Hysbysiadau push",
218
-    "notifications.column_settings.reblog": "",
219
-    "notifications.column_settings.show": "",
220
-    "notifications.column_settings.sound": "Chwarae sain",
221
-    "notifications.group": "{count} o hysbysiadau",
222
-    "onboarding.done": "Wedi'i wneud",
223
-    "onboarding.next": "Nesaf",
224
-    "onboarding.page_five.public_timelines": "",
225
-    "onboarding.page_four.home": "Mae'r ffrwd gartref yn dangos twtiau o bobl yr ydych yn dilyn.",
226
-    "onboarding.page_four.notifications": "",
227
-    "onboarding.page_one.federation": "",
228
-    "onboarding.page_one.full_handle": "",
229
-    "onboarding.page_one.handle_hint": "",
230
-    "onboarding.page_one.welcome": "Croeso i Mastodon!",
231
-    "onboarding.page_six.admin": "",
232
-    "onboarding.page_six.almost_done": "Bron a gorffen...",
233
-    "onboarding.page_six.appetoot": "Bon Apetŵt!",
234
-    "onboarding.page_six.apps_available": "Mae yna {apps} ar gael i iOS, Android a platfformau eraill.",
235
-    "onboarding.page_six.github": "Mae Mastodon yn feddalwedd côd agored rhad ac am ddim. Mae modd adrodd bygiau, gwneud ceisiadau am nodweddion penodol, neu gyfrannu i'r côd ar {github}.",
236
-    "onboarding.page_six.guidelines": "canllawiau cymunedol",
237
-    "onboarding.page_six.read_guidelines": "Darllenwch {guidelines} y {domain} os gwelwch yn dda!",
238
-    "onboarding.page_six.various_app": "apiau symudol",
239
-    "onboarding.page_three.profile": "",
240
-    "onboarding.page_three.search": "",
241
-    "onboarding.page_two.compose": "",
242
-    "onboarding.skip": "Sgipiwch",
243
-    "privacy.change": "",
244
-    "privacy.direct.long": "",
245
-    "privacy.direct.short": "Uniongyrchol",
246
-    "privacy.private.long": "Cyhoeddi i ddilynwyr yn unig",
247
-    "privacy.private.short": "Dilynwyr-yn-unig",
248
-    "privacy.public.long": "Cyhoeddi i ffrydiau cyhoeddus",
249
-    "privacy.public.short": "Cyhoeddus",
250
-    "privacy.unlisted.long": "Peidio a cyhoeddi i ffrydiau cyhoeddus",
251
-    "privacy.unlisted.short": "Heb ei restru",
252
-    "regeneration_indicator.label": "Llwytho…",
253
-    "regeneration_indicator.sublabel": "Mae eich ffrwd cartref yn cael ei baratoi!",
254
-    "relative_time.days": "{number}d",
255
-    "relative_time.hours": "{number}h",
256
-    "relative_time.just_now": "nawr",
257
-    "relative_time.minutes": "{number}m",
258
-    "relative_time.seconds": "{number}s",
259
-    "reply_indicator.cancel": "Canslo",
260
-    "report.forward": "",
261
-    "report.forward_hint": "",
262
-    "report.hint": "",
263
-    "report.placeholder": "Sylwadau ychwanegol",
264
-    "report.submit": "Cyflwyno",
265
-    "report.target": "",
266
-    "search.placeholder": "Chwilio",
267
-    "search_popout.search_format": "Fformat chwilio uwch",
268
-    "search_popout.tips.full_text": "",
269
-    "search_popout.tips.hashtag": "hashnod",
270
-    "search_popout.tips.status": "statws",
271
-    "search_popout.tips.text": "",
272
-    "search_popout.tips.user": "defnyddiwr",
273
-    "search_results.accounts": "Pobl",
274
-    "search_results.hashtags": "Hanshnodau",
275
-    "search_results.statuses": "Twtiau",
276
-    "search_results.total": "",
277
-    "standalone.public_title": "Golwg tu fewn...",
278
-    "status.block": "Blociwch @{name}",
279
-    "status.cancel_reblog_private": "",
280
-    "status.cannot_reblog": "",
281
-    "status.delete": "Dileu",
282
-    "status.detailed_status": "",
283
-    "status.direct": "Neges breifat @{name}",
284
-    "status.embed": "Plannu",
285
-    "status.favourite": "",
286
-    "status.filtered": "",
287
-    "status.load_more": "Llwythwch mwy",
288
-    "status.media_hidden": "",
289
-    "status.mention": "",
290
-    "status.more": "Mwy",
291
-    "status.mute": "Tawelu @{name}",
292
-    "status.mute_conversation": "",
293
-    "status.open": "",
294
-    "status.pin": "",
295
-    "status.pinned": "",
296
-    "status.reblog": "",
297
-    "status.reblog_private": "",
298
-    "status.reblogged_by": "",
299
-    "status.reblogs.empty": "",
300
-    "status.redraft": "Dilëwh & ailddrafftio",
301
-    "status.reply": "Ateb",
302
-    "status.replyAll": "Ateb i edefyn",
303
-    "status.report": "",
304
-    "status.sensitive_toggle": "",
305
-    "status.sensitive_warning": "Cynnwys sensitif",
306
-    "status.share": "Rhannwch",
307
-    "status.show_less": "Dangoswch lai",
308
-    "status.show_less_all": "Dangoswch lai i bawb",
309
-    "status.show_more": "Dangoswch fwy",
310
-    "status.show_more_all": "",
311
-    "status.unmute_conversation": "Dad-dawelu sgwrs",
312
-    "status.unpin": "",
313
-    "tabs_bar.federated_timeline": "",
314
-    "tabs_bar.home": "Hafan",
315
-    "tabs_bar.local_timeline": "Lleol",
316
-    "tabs_bar.notifications": "Hysbysiadau",
317
-    "tabs_bar.search": "Chwilio",
318
-    "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} yn siarad",
319
-    "ui.beforeunload": "Mi fyddwch yn colli eich drafft os gadewch Mastodon.",
320
-    "upload_area.title": "Llusgwch & gollwing i uwchlwytho",
321
-    "upload_button.label": "Ychwanegwch gyfryngau (JPEG, PNG, GIF, WebM, MP4, MOV)",
322
-    "upload_form.description": "",
323
-    "upload_form.focus": "",
324
-    "upload_form.undo": "Dileu",
325
-    "upload_progress.label": "Uwchlwytho...",
326
-    "video.close": "Cau fideo",
327
-    "video.exit_fullscreen": "Gadael sgrîn llawn",
328
-    "video.expand": "Ymestyn fideo",
329
-    "video.fullscreen": "Sgrîn llawn",
330
-    "video.hide": "Cuddio fideo",
331
-    "video.mute": "Tawelu sain",
332
-    "video.pause": "Oedi",
333
-    "video.play": "Chwarae",
334
-    "video.unmute": "Dad-dawelu sain"
2
+  "account.badges.bot": "Bot",
3
+  "account.block": "Blociwch @{name}",
4
+  "account.block_domain": "Cuddiwch bopeth rhag {domain}",
5
+  "account.blocked": "Blociwyd",
6
+  "account.direct": "Neges breifat @{name}",
7
+  "account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
8
+  "account.domain_blocked": "Parth wedi ei guddio",
9
+  "account.edit_profile": "Golygu proffil",
10
+  "account.endorse": "Arddangos ar fy mhroffil",
11
+  "account.follow": "Dilyn",
12
+  "account.followers": "Dilynwyr",
13
+  "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
14
+  "account.follows": "Yn dilyn",
15
+  "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
16
+  "account.follows_you": "Yn eich dilyn chi",
17
+  "account.hide_reblogs": "Cuddio bwstiau o @{name}",
18
+  "account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}",
19
+  "account.media": "Cyfryngau",
20
+  "account.mention": "Crybwyll @{name}",
21
+  "account.moved_to": "Mae @{name} wedi symud i:",
22
+  "account.mute": "Tawelu @{name}",
23
+  "account.mute_notifications": "Cuddio hysbysiadau o @{name}",
24
+  "account.muted": "Distewyd",
25
+  "account.posts": "Tŵtiau",
26
+  "account.posts_with_replies": "Tŵtiau ac atebion",
27
+  "account.report": "Adroddwch @{name}",
28
+  "account.requested": "Aros am gymeradwyaeth. Cliciwch er mwyn canslo cais dilyn",
29
+  "account.share": "Rhannwch broffil @{name}",
30
+  "account.show_reblogs": "Dangoswch bwstiau o @{name}",
31
+  "account.unblock": "Dadflociwch @{name}",
32
+  "account.unblock_domain": "Dadguddiwch {domain}",
33
+  "account.unendorse": "Peidwch a'i arddangos ar fy mhroffil",
34
+  "account.unfollow": "Daddilynwch",
35
+  "account.unmute": "Dad-dawelu @{name}",
36
+  "account.unmute_notifications": "Dad-dawelu hysbysiadau o @{name}",
37
+  "account.view_full_profile": "Gweld proffil llawn",
38
+  "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
39
+  "alert.unexpected.title": "Wps!",
40
+  "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
41
+  "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
42
+  "bundle_column_error.retry": "Ceisiwch eto",
43
+  "bundle_column_error.title": "Gwall rhwydwaith",
44
+  "bundle_modal_error.close": "Cau",
45
+  "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
46
+  "bundle_modal_error.retry": "Ceiswich eto",
47
+  "column.blocks": "Defnyddwyr a flociwyd",
48
+  "column.community": "Llinell amser lleol",
49
+  "column.direct": "Negeseuon preifat",
50
+  "column.domain_blocks": "Parthau cuddiedig",
51
+  "column.favourites": "Ffefrynnau",
52
+  "column.follow_requests": "Ceisiadau dilyn",
53
+  "column.home": "Hafan",
54
+  "column.lists": "Rhestrau",
55
+  "column.mutes": "Defnyddwyr a ddistewyd",
56
+  "column.notifications": "Hysbysiadau",
57
+  "column.pins": "Tŵtiau wedi eu pinio",
58
+  "column.public": "Ffrwd y ffederasiwn",
59
+  "column_back_button.label": "Nôl",
60
+  "column_header.hide_settings": "Cuddiwch dewisiadau",
61
+  "column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
62
+  "column_header.moveRight_settings": "Symudwch y golofn i'r dde",
63
+  "column_header.pin": "Piniwch",
64
+  "column_header.show_settings": "Dangos gosodiadau",
65
+  "column_header.unpin": "Dadbiniwch",
66
+  "column_subheading.settings": "Gosodiadau",
67
+  "community.column_settings.media_only": "Cyfryngau yn unig",
68
+  "compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
69
+  "compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
70
+  "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.",
71
+  "compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
72
+  "compose_form.lock_disclaimer.lock": "wedi ei gloi",
73
+  "compose_form.placeholder": "Be syd ar eich meddwl?",
74
+  "compose_form.publish": "Tŵt",
75
+  "compose_form.publish_loud": "{publish}!",
76
+  "compose_form.sensitive.marked": "Media is marked as sensitive",
77
+  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
78
+  "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
79
+  "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
80
+  "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
81
+  "confirmation_modal.cancel": "Canslo",
82
+  "confirmations.block.confirm": "Blociwch",
83
+  "confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
84
+  "confirmations.delete.confirm": "Dileu",
85
+  "confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
86
+  "confirmations.delete_list.confirm": "Dileu",
87
+  "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
88
+  "confirmations.domain_block.confirm": "Cuddio parth cyfan",
89
+  "confirmations.domain_block.message": "A ydych yn hollol, hollol sicr eich bod am flocio y {domain} cyfan? Yn y nifer helaeth o achosion mae blocio neu tawelu ambell gyfrif yn ddigonol ac yn well. Ni fyddwch yn gweld cynnwyr o'r parth hwnnw mewn unrhyw ffrydiau cyhoeddus na chwaith eich hysbysiadau. Bydd hyn yn cael gwared o'ch dilynwyr o'r parth hwnnw.",
90
+  "confirmations.mute.confirm": "Tawelu",
91
+  "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?",
92
+  "confirmations.redraft.confirm": "Dilëwch & ailddrafftio",
93
+  "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.",
94
+  "confirmations.reply.confirm": "Reply",
95
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
96
+  "confirmations.unfollow.confirm": "Dad-ddilynwch",
97
+  "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
98
+  "conversation.last_message": "Last message:",
99
+  "embed.instructions": "Mewnblannwch y statws hwn ar eich gwefan drwy gopïo'r côd isod.",
100
+  "embed.preview": "Dyma sut olwg fydd arno:",
101
+  "emoji_button.activity": "Gweithgarwch",
102
+  "emoji_button.custom": "Custom",
103
+  "emoji_button.flags": "Baneri",
104
+  "emoji_button.food": "Bwyd a Diod",
105
+  "emoji_button.label": "Mewnosodwch emoji",
106
+  "emoji_button.nature": "Natur",
107
+  "emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
108
+  "emoji_button.objects": "Gwrthrychau",
109
+  "emoji_button.people": "Pobl",
110
+  "emoji_button.recent": "Defnyddir yn aml",
111
+  "emoji_button.search": "Chwilio...",
112
+  "emoji_button.search_results": "Canlyniadau chwilio",
113
+  "emoji_button.symbols": "Symbolau",
114
+  "emoji_button.travel": "Teithio & Llefydd",
115
+  "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
116
+  "empty_column.community": "Mae'r ffrwd lleol yn wag. Ysgrifenwch rhywbeth yn gyhoeddus i gael dechrau arni!",
117
+  "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
118
+  "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
119
+  "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
120
+  "empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
121
+  "empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan dderbyniwch chi un, bydd yn ymddangos yma.",
122
+  "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
123
+  "empty_column.home": "Mae eich ffrwd gartref yn wag! Ymwelwch a {public} neu defnyddiwch y chwilotwr i ddechrau arni ac i gwrdd a defnyddwyr eraill.",
124
+  "empty_column.home.public_timeline": "y ffrwd cyhoeddus",
125
+  "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.",
126
+  "empty_column.lists": "Nid oes gennych unrhyw restrau eto. Pan grëwch chi un, mi fydd yn ymddangos yma.",
127
+  "empty_column.mutes": "Nid ydych wedi tawelu unrhyw ddefnyddwyr eto.",
128
+  "empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ac eraill i ddechrau'r sgwrs.",
129
+  "empty_column.public": "Does dim byd yma! Ysgrifennwch rhywbeth yn gyhoeddus, neu dilynwch ddefnyddwyr o INSTANCES eraill i'w lenwi",
130
+  "follow_request.authorize": "Caniatau",
131
+  "follow_request.reject": "Gwrthod",
132
+  "getting_started.developers": "Datblygwyr",
133
+  "getting_started.documentation": "Dogfennaeth",
134
+  "getting_started.find_friends": "Canfod ffrindiau o Twitter",
135
+  "getting_started.heading": "Dechrau",
136
+  "getting_started.invite": "Gwahoddwch bobl",
137
+  "getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.",
138
+  "getting_started.security": "Diogelwch",
139
+  "getting_started.terms": "Telerau Gwasanaeth",
140
+  "home.column_settings.basic": "Syml",
141
+  "home.column_settings.show_reblogs": "Show boosts",
142
+  "home.column_settings.show_replies": "Dangoswch ymatebion",
143
+  "keyboard_shortcuts.back": "i lywio nôl",
144
+  "keyboard_shortcuts.blocked": "i agor rhestr defnyddwyr a flociwyd",
145
+  "keyboard_shortcuts.boost": "to boost",
146
+  "keyboard_shortcuts.column": "to focus a status in one of the columns",
147
+  "keyboard_shortcuts.compose": "to focus the compose textarea",
148
+  "keyboard_shortcuts.description": "Disgrifiad",
149
+  "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
150
+  "keyboard_shortcuts.down": "i symud lawr yn y rhestr",
151
+  "keyboard_shortcuts.enter": "i agor statws",
152
+  "keyboard_shortcuts.favourite": "i hoffi",
153
+  "keyboard_shortcuts.favourites": "i agor rhestr hoffi",
154
+  "keyboard_shortcuts.federated": "i agor ffrwd y ffederasiwn",
155
+  "keyboard_shortcuts.heading": "Llwybrau byr allweddell",
156
+  "keyboard_shortcuts.home": "i agor ffrwd cartref",
157
+  "keyboard_shortcuts.hotkey": "Hotkey",
158
+  "keyboard_shortcuts.legend": "i ddangos yr arwr yma",
159
+  "keyboard_shortcuts.local": "i agor ffrwd lleol",
160
+  "keyboard_shortcuts.mention": "i grybwyll yr awdur",
161
+  "keyboard_shortcuts.muted": "i agor rhestr defnyddwyr a dawelwyd",
162
+  "keyboard_shortcuts.my_profile": "i agor eich proffil",
163
+  "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
164
+  "keyboard_shortcuts.pinned": "i agor rhestr tŵtiau wedi'i pinio",
165
+  "keyboard_shortcuts.profile": "i agor proffil yr awdur",
166
+  "keyboard_shortcuts.reply": "i ateb",
167
+  "keyboard_shortcuts.requests": "i agor rhestr ceisiadau dilyn",
168
+  "keyboard_shortcuts.search": "i ffocysu chwilio",
169
+  "keyboard_shortcuts.start": "i agor colofn \"dechrau arni\"",
170
+  "keyboard_shortcuts.toggle_hidden": "i ddangos/cuddio testun tu ôl i CW",
171
+  "keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
172
+  "keyboard_shortcuts.unfocus": "i ddad-ffocysu ardal cyfansoddi testun/chwilio",
173
+  "keyboard_shortcuts.up": "i symud yn uwch yn y rhestr",
174
+  "lightbox.close": "Cau",
175
+  "lightbox.next": "Nesaf",
176
+  "lightbox.previous": "Blaenorol",
177
+  "lists.account.add": "Ychwanegwch at restr",
178
+  "lists.account.remove": "Remove from list",
179
+  "lists.delete": "Dileu rhestr",
180
+  "lists.edit": "Golygwch restr",
181
+  "lists.new.create": "Ychwanegwch restr",
182
+  "lists.new.title_placeholder": "Teitl rhestr newydd",
183
+  "lists.search": "Chwiliwch ymysg pobl yr ydych yn ei ddilyn",
184
+  "lists.subheading": "Eich rhestrau",
185
+  "loading_indicator.label": "Llwytho...",
186
+  "media_gallery.toggle_visible": "Toglo gwelededd",
187
+  "missing_indicator.label": "Heb ei ganfod",
188
+  "missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
189
+  "mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
190
+  "navigation_bar.apps": "Apiau symudol",
191
+  "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
192
+  "navigation_bar.community_timeline": "Ffrwd leol",
193
+  "navigation_bar.compose": "Cyfansoddwch dŵt newydd",
194
+  "navigation_bar.direct": "Negeseuon preifat",
195
+  "navigation_bar.discover": "Darganfyddwch",
196
+  "navigation_bar.domain_blocks": "Parthau cuddiedig",
197
+  "navigation_bar.edit_profile": "Golygu proffil",
198
+  "navigation_bar.favourites": "Ffefrynnau",
199
+  "navigation_bar.filters": "Geiriau a dawelwyd",
200
+  "navigation_bar.follow_requests": "Ceisiadau dilyn",
201
+  "navigation_bar.info": "Ynghylch yr achos hwn",
202
+  "navigation_bar.keyboard_shortcuts": "Bysellau brys",
203
+  "navigation_bar.lists": "Rhestrau",
204
+  "navigation_bar.logout": "Allgofnodi",
205
+  "navigation_bar.mutes": "Defnyddwyr a dawelwyd",
206
+  "navigation_bar.personal": "Personol",
207
+  "navigation_bar.pins": "Tŵtiau wedi eu pinio",
208
+  "navigation_bar.preferences": "Dewisiadau",
209
+  "navigation_bar.public_timeline": "Ffrwd y fferasiwn",
210
+  "navigation_bar.security": "Diogelwch",
211
+  "notification.favourite": "hoffodd {name} eich statws",
212
+  "notification.follow": "dilynodd {name} chi",
213
+  "notification.mention": "Soniodd {name} amdanoch chi",
214
+  "notification.reblog": "{name} boosted your status",
215
+  "notifications.clear": "Clirio hysbysiadau",
216
+  "notifications.clear_confirmation": "Ydych chi'n sicr eich bod am glirio'ch holl hysbysiadau am byth?",
217
+  "notifications.column_settings.alert": "Hysbysiadau bwrdd gwaith",
218
+  "notifications.column_settings.favourite": "Ffefrynnau:",
219
+  "notifications.column_settings.follow": "Dilynwyr newydd:",
220
+  "notifications.column_settings.mention": "Crybwylliadau:",
221
+  "notifications.column_settings.push": "Hysbysiadau push",
222
+  "notifications.column_settings.reblog": "Boosts:",
223
+  "notifications.column_settings.show": "Dangos yn y golofn",
224
+  "notifications.column_settings.sound": "Chwarae sain",
225
+  "notifications.group": "{count} o hysbysiadau",
226
+  "onboarding.done": "Wedi'i wneud",
227
+  "onboarding.next": "Nesaf",
228
+  "onboarding.page_five.public_timelines": "Mae'r ffrwd lleol yn dangos tŵtiau cyhoeddus o bawb ar y {domain}. Mae ffrwd y ffederasiwn yn dangos tŵtiau cyhoeddus o bawb y mae pobl ar y {domain} yn dilyn. Mae rhain yn Ffrydiau Cyhoeddus, ffordd wych o ddarganfod pobl newydd.",
229
+  "onboarding.page_four.home": "Mae'r ffrwd gartref yn dangos twtiau o bobl yr ydych yn dilyn.",
230
+  "onboarding.page_four.notifications": "Mae'r golofn hysbysiadau yn dangos pan mae rhywun yn ymwneud a chi.",
231
+  "onboarding.page_one.federation": "Mae mastodon yn rwydwaith o weinyddwyr anibynnol sy'n uno i greu un rhwydwaith gymdeithasol mwy. Yr ydym yn galw'r gweinyddwyr yma yn achosion.",
232
+  "onboarding.page_one.full_handle": "Your full handle",
233
+  "onboarding.page_one.handle_hint": "Dyma beth y bysech chi'n dweud wrth eich ffrindiau i chwilota amdano.",
234
+  "onboarding.page_one.welcome": "Croeso i Mastodon!",
235
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
236
+  "onboarding.page_six.almost_done": "Bron a gorffen...",