Compare commits

...

232 Commits

Author SHA1 Message Date
Thibaut Girka
83247d7617 Further optimizations 2019-07-01 12:53:31 +02:00
Thibaut Girka
3f4e8fa2c2 Fix multiple broken things 2019-06-30 18:05:37 +02:00
Thibaut Girka
34293ec05b [WiP] Try improving glitch-soc front-end perfs
This *does* break things, it's just to test things
2019-06-30 10:24:56 +02:00
ThibG
98c2d2aa46 Merge pull request #1146 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-28 22:35:54 +02:00
Eugen Rochko
662252c8f7 [Glitch] Add categories for custom emojis
Port front-end changes from e64e6a03dd to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-28 21:42:37 +02:00
ThibG
4d964398de [Glitch] Fix swiping columns on mobile sometimes failing
Port 072158ee97 to glitch-soc
2019-06-28 21:38:34 +02:00
Thibaut Girka
3922b518f7 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-28 21:36:50 +02:00
ThibG
4f5b221be2 Display FTS warning based on actual search term, not the one being typed (#11202)
Follow-up to #11112
2019-06-28 19:29:11 +02:00
ThibG
f7c0e326ab Merge pull request #1145 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-28 19:11:06 +02:00
ThibG
43698e08ca [Glitch] Add message telling FTS is disabled when no toot can be found because of this
Port ca8944728f to glitch-soc
2019-06-28 18:54:56 +02:00
Eugen Rochko
e64e6a03dd Add categories for custom emojis (#11196)
Fix #7940
2019-06-28 15:54:10 +02:00
ThibG
072158ee97 Fix swiping columns on mobile sometimes failing (#11200)
Fixes #9779
2019-06-28 13:52:15 +02:00
Thibaut Girka
c8ba75b963 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-28 12:11:45 +02:00
Thibaut Girka
6ad870a410 Change search components classes and styling to match upstream 2019-06-28 11:13:41 +02:00
ThibG
ca8944728f Add message telling FTS is disabled when no toot can be found because of this (#11112)
* Add message telling FTS is disabled when no toot can be found because of this

Fixes #11082

* Remove info icon and reword message
2019-06-27 21:12:26 +02:00
ThibG
9a90ec3b3b Fix account URI in UpdatePollSerializer (#11194)
* Fix account URI in UpdatePollSerializer

Fixes #11185

* Add specs
2019-06-27 19:41:55 +02:00
Thibaut Girka
ca17bae904 Use a redis-cached feed for the DM timeline 2019-06-27 16:44:12 +02:00
ThibG
2f95adc06f Merge pull request #1142 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-27 16:43:55 +02:00
Thibaut Girka
6ab7051b48 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-27 15:48:23 +02:00
mayaeh
a02f4b7cd4 Fix NameError (#11192) 2019-06-27 09:16:55 +02:00
ThibG
4175f13155 [Glitch] Add option to disable blurhash previews
Port 3086c645fd to glitch-soc
2019-06-26 23:19:26 +02:00
PatOnTheBack
383136d9bb [Glitch] Removed extra pipes from regex.
Port 5b20284f6f to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-26 23:19:26 +02:00
ThibG
5c3171e8ea [Glitch] Apply filters to poll options in WebUI
Port 47ef4a6c7a to glitch-soc
2019-06-26 23:19:26 +02:00
Thibaut Girka
aaec64a500 Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/controllers/settings/preferences_controller.rb
- app/lib/user_settings_decorator.rb
- app/models/user.rb
- config/locales/simple_form.en.yml
2019-06-26 23:19:22 +02:00
ThibG
3086c645fd Add option to disable blurhash previews (#11188)
* Add option to disable blurhash previews

* Update option text

* Change options order
2019-06-26 19:33:04 +02:00
ThibG
915c619394 Add support for Audio activities (#11189)
Fixes #11127
2019-06-26 19:32:36 +02:00
ThibG
32a4494926 Scroll to compose form rather than reply indicator on focus (#11182) 2019-06-26 14:28:36 +02:00
Thibaut Girka
9ef25877df Scroll to compose form rather than reply indicator on focus 2019-06-26 10:46:11 +02:00
PatOnTheBack
5b20284f6f Removed extra pipes from regex. (#11181) 2019-06-26 02:16:24 +02:00
ThibG
ed10ae2693 Merge pull request #1138 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-25 22:43:33 +02:00
dependabot-preview[bot]
07508b2045 Bump stringz from 1.0.0 to 2.0.0 (#11168)
Bumps [stringz](https://github.com/sallar/stringz) from 1.0.0 to 2.0.0.
- [Release notes](https://github.com/sallar/stringz/releases)
- [Changelog](https://github.com/sallar/stringz/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sallar/stringz/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 20:51:35 +02:00
Eugen Rochko
6836587117 Fix unnecessary SQL query performed on unauthenticated requests (#11179) 2019-06-25 20:18:15 +02:00
dependabot-preview[bot]
8d57795608 Bump eslint from 5.11.1 to 5.16.0 (#11165)
Bumps [eslint](https://github.com/eslint/eslint) from 5.11.1 to 5.16.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.11.1...v5.16.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:51:57 +09:00
dependabot-preview[bot]
8d56433327 Bump derailed_benchmarks from 1.3.5 to 1.3.6 (#11171)
Bumps [derailed_benchmarks](https://github.com/schneems/derailed_benchmarks) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/schneems/derailed_benchmarks/releases)
- [Changelog](https://github.com/schneems/derailed_benchmarks/blob/master/CHANGELOG.md)
- [Commits](https://github.com/schneems/derailed_benchmarks/compare/v1.3.5...v1.3.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:49:44 +09:00
dependabot-preview[bot]
1afb8cac2f Bump aws-sdk-s3 from 1.42.0 to 1.43.0 (#11172)
Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.42.0 to 1.43.0.
- [Release notes](https://github.com/aws/aws-sdk-ruby/releases)
- [Changelog](https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-ruby/compare/v1.42.0...v1.43.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:45:32 +09:00
dependabot-preview[bot]
d9ac5b79ae Bump parallel_tests from 2.29.0 to 2.29.1 (#11169)
Bumps [parallel_tests](https://github.com/grosser/parallel_tests) from 2.29.0 to 2.29.1.
- [Release notes](https://github.com/grosser/parallel_tests/releases)
- [Commits](https://github.com/grosser/parallel_tests/compare/v2.29.0...v2.29.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:39:35 +09:00
dependabot-preview[bot]
864bc0d97a Bump dotenv-rails from 2.7.2 to 2.7.4 (#11170)
Bumps [dotenv-rails](https://github.com/bkeepers/dotenv) from 2.7.2 to 2.7.4.
- [Release notes](https://github.com/bkeepers/dotenv/releases)
- [Changelog](https://github.com/bkeepers/dotenv/blob/master/Changelog.md)
- [Commits](https://github.com/bkeepers/dotenv/compare/v2.7.2...v2.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:37:26 +09:00
dependabot-preview[bot]
99ade565b4 Bump mini-css-extract-plugin from 0.5.0 to 0.7.0 (#11167)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v0.5.0...v0.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:36:57 +09:00
dependabot-preview[bot]
f1da937245 Bump webpack-dev-server from 3.5.1 to 3.7.2 (#11166)
Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 3.5.1 to 3.7.2.
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v3.5.1...v3.7.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:32:58 +09:00
dependabot-preview[bot]
e76d2c51c2 Bump babel-loader from 8.0.5 to 8.0.6 (#11164)
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.0.5 to 8.0.6.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.0.5...v8.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-25 22:24:49 +09:00
ThibG
47ef4a6c7a Apply filters to poll options (#11174)
* Apply filters to poll options in WebUI

Fixes #11128

* Apply filters to poll options server-side

* Add poll options to searchable text
2019-06-25 14:45:14 +02:00
Thibaut Girka
81bf43cfdd Change .env.production.sample to specify that MAX_VIDEO_SIZE also applies to audio files 2019-06-24 16:27:10 +02:00
Thibaut Girka
598cdc9542 Use a different icon for audio attachments 2019-06-24 16:16:16 +02:00
Eugen Rochko
d7eb580053 [Glitch] Add media description as title to links of unknown media attachments
Port front-end changes from 49ebda4d49 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-24 16:00:42 +02:00
Eugen Rochko
967456b6a9 [Glitch] Add audio uploads
Port front-end changes from f7f23b4a19 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-24 16:00:42 +02:00
Thibaut Girka
ddd875ad99 Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/models/media_attachment.rb
  Upstream added audio attachment support
- app/serializers/initial_state_serializer.rb
  Upstream added audio attachment support and how mimetypes are returned
- app/serializers/rest/instance_serializer.rb
  Upstream added a few fields
- config/application.rb
  Upstream added a different paperclip transcoder
2019-06-24 15:02:59 +02:00
Eugen Rochko
66ac1bd063 Fix unconverted PRs in the changelog (#11155) 2019-06-22 18:13:52 +02:00
Eugen Rochko
b5c772c3d4 Bump version to 2.9.2 (#11152) 2019-06-22 17:28:26 +02:00
Eugen Rochko
8fe7116cdf New Crowdin translations (#11144)
* New translations simple_form.en.yml (Japanese)
[ci skip]

* New translations en.json (Catalan)
[ci skip]

* New translations doorkeeper.en.yml (Catalan)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Galician)
[ci skip]

* New translations en.json (Arabic)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-22 17:26:09 +02:00
Eugen Rochko
b927bb3f07 Fix audio-only OGG and WebM files not being processed as such (#11151)
Also, because Chrome sends audio/mp3 instead of audio/mpeg as it's
supposed to, we need to whitelist that mime type as well
2019-06-22 16:54:06 +02:00
koyu
6eb5241099 Change camera icon to paperclip icon in upload form (#11149) 2019-06-22 15:29:25 +02:00
Eugen Rochko
d61d164685 Add short_description and approval_required to GET /api/v1/instance (#11146) 2019-06-22 12:08:16 +02:00
Eugen Rochko
aa9b37822b Fix audio not being downloaded from remote servers (#11145) 2019-06-22 02:50:36 +02:00
Eugen Rochko
84f945d64c Bump version to 2.9.1 (#11143) 2019-06-22 01:51:27 +02:00
Eugen Rochko
6e7e714bd9 New Crowdin translations (#11116)
* New translations devise.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations devise.en.yml (Chinese Traditional)
[ci skip]

* New translations activerecord.en.yml (Thai)
[ci skip]

* New translations activerecord.en.yml (Slovak)
[ci skip]

* New translations activerecord.en.yml (French)
[ci skip]

* New translations activerecord.en.yml (Hungarian)
[ci skip]

* New translations activerecord.en.yml (Hebrew)
[ci skip]

* New translations activerecord.en.yml (Greek)
[ci skip]

* New translations activerecord.en.yml (German)
[ci skip]

* New translations activerecord.en.yml (Georgian)
[ci skip]

* New translations activerecord.en.yml (Galician)
[ci skip]

* New translations activerecord.en.yml (Esperanto)
[ci skip]

* New translations activerecord.en.yml (Danish)
[ci skip]

* New translations activerecord.en.yml (Czech)
[ci skip]

* New translations activerecord.en.yml (Corsican)
[ci skip]

* New translations activerecord.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations activerecord.en.yml (Indonesian)
[ci skip]

* New translations activerecord.en.yml (Japanese)
[ci skip]

* New translations activerecord.en.yml (Swedish)
[ci skip]

* New translations activerecord.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations activerecord.en.yml (Spanish)
[ci skip]

* New translations activerecord.en.yml (Slovenian)
[ci skip]

* New translations devise.en.yml (Ido)
[ci skip]

* New translations activerecord.en.yml (Serbian (Latin))
[ci skip]

* New translations activerecord.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations activerecord.en.yml (Russian)
[ci skip]

* New translations activerecord.en.yml (Portuguese)
[ci skip]

* New translations activerecord.en.yml (Kazakh)
[ci skip]

* New translations activerecord.en.yml (Polish)
[ci skip]

* New translations activerecord.en.yml (Persian)
[ci skip]

* New translations activerecord.en.yml (Occitan)
[ci skip]

* New translations activerecord.en.yml (Norwegian)
[ci skip]

* New translations activerecord.en.yml (Korean)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Indonesian)
[ci skip]

* New translations doorkeeper.en.yml (Ido)
[ci skip]

* New translations doorkeeper.en.yml (Korean)
[ci skip]

* New translations doorkeeper.en.yml (Kazakh)
[ci skip]

* New translations doorkeeper.en.yml (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Indonesian)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations doorkeeper.en.yml (Norwegian)
[ci skip]

* New translations doorkeeper.en.yml (Hebrew)
[ci skip]

* New translations doorkeeper.en.yml (Greek)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations doorkeeper.en.yml (Georgian)
[ci skip]

* New translations doorkeeper.en.yml (Galician)
[ci skip]

* New translations doorkeeper.en.yml (French)
[ci skip]

* New translations doorkeeper.en.yml (Finnish)
[ci skip]

* New translations doorkeeper.en.yml (Occitan)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Ukrainian)
[ci skip]

* New translations doorkeeper.en.yml (Turkish)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Spanish)
[ci skip]

* New translations doorkeeper.en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Persian)
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations doorkeeper.en.yml (Russian)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese)
[ci skip]

* New translations doorkeeper.en.yml (Polish)
[ci skip]

* New translations doorkeeper.en.yml (Esperanto)
[ci skip]

* New translations doorkeeper.en.yml (Danish)
[ci skip]

* New translations devise.en.yml (Persian)
[ci skip]

* New translations devise.en.yml (Serbian (Latin))
[ci skip]

* New translations devise.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations devise.en.yml (Russian)
[ci skip]

* New translations devise.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations devise.en.yml (Portuguese)
[ci skip]

* New translations devise.en.yml (Polish)
[ci skip]

* New translations devise.en.yml (Occitan)
[ci skip]

* New translations devise.en.yml (Slovenian)
[ci skip]

* New translations devise.en.yml (Norwegian)
[ci skip]

* New translations activerecord.en.yml (Chinese Simplified)
[ci skip]

* New translations devise.en.yml (Korean)
[ci skip]

* New translations devise.en.yml (Kazakh)
[ci skip]

* New translations devise.en.yml (Japanese)
[ci skip]

* New translations devise.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Spanish)
[ci skip]

* New translations doorkeeper.en.yml (Czech)
[ci skip]

* New translations doorkeeper.en.yml (Croatian)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Simplified)
[ci skip]

* New translations doorkeeper.en.yml (Catalan)
[ci skip]

* New translations doorkeeper.en.yml (Bulgarian)
[ci skip]

* New translations doorkeeper.en.yml (Basque)
[ci skip]

* New translations devise.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Asturian)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations devise.en.yml (Ukrainian)
[ci skip]

* New translations devise.en.yml (Turkish)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations activerecord.en.yml (Catalan)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Bulgarian)
[ci skip]

* New translations en.yml (Bengali)
[ci skip]

* New translations en.yml (Basque)
[ci skip]

* New translations en.json (Ukrainian)
[ci skip]

* New translations en.json (Turkish)
[ci skip]

* New translations en.json (Telugu)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.json (Tamil)
[ci skip]

* New translations en.json (Swedish)
[ci skip]

* New translations en.json (Spanish)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.json (Serbian (Latin))
[ci skip]

* New translations en.json (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Catalan)
[ci skip]

* New translations en.yml (Chinese Traditional)
[ci skip]

* New translations en.json (Romanian)
[ci skip]

* New translations en.yml (Georgian)
[ci skip]

* New translations en.yml (Indonesian)
[ci skip]

* New translations en.yml (Ido)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hebrew)
[ci skip]

* New translations en.yml (Greek)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations en.yml (Galician)
[ci skip]

* New translations en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.yml (French)
[ci skip]

* New translations en.yml (Finnish)
[ci skip]

* New translations en.yml (Esperanto)
[ci skip]

* New translations en.yml (Danish)
[ci skip]

* New translations en.yml (Croatian)
[ci skip]

* New translations en.yml (Corsican)
[ci skip]

* New translations en.json (Portuguese, Brazilian)
[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (Danish)
[ci skip]

* New translations en.json (Croatian)
[ci skip]

* New translations en.json (Chinese Traditional)
[ci skip]

* New translations en.json (Catalan)
[ci skip]

* New translations en.json (Bulgarian)
[ci skip]

* New translations en.json (Bengali)
[ci skip]

* New translations devise.en.yml (Czech)
[ci skip]

* New translations simple_form.en.yml (Czech)
[ci skip]

* New translations en.json (Georgian)
[ci skip]

* New translations en.json (Portuguese)
[ci skip]

* New translations en.json (Occitan)
[ci skip]

* New translations en.json (Norwegian)
[ci skip]

* New translations en.json (Malay)
[ci skip]

* New translations en.json (Lithuanian)
[ci skip]

* New translations en.json (Latvian)
[ci skip]

* New translations en.json (Kazakh)
[ci skip]

* New translations en.json (Indonesian)
[ci skip]

* New translations en.json (Ido)
[ci skip]

* New translations en.json (Hebrew)
[ci skip]

* New translations en.yml (Kazakh)
[ci skip]

* New translations simple_form.en.yml (Occitan)
[ci skip]

* New translations simple_form.en.yml (Norwegian)
[ci skip]

* New translations simple_form.en.yml (Korean)
[ci skip]

* New translations simple_form.en.yml (Japanese)
[ci skip]

* New translations simple_form.en.yml (Polish)
[ci skip]

* New translations simple_form.en.yml (Indonesian)
[ci skip]

* New translations simple_form.en.yml (Ido)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hebrew)
[ci skip]

* New translations simple_form.en.yml (Greek)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Portuguese)
[ci skip]

* New translations simple_form.en.yml (Galician)
[ci skip]

* New translations activerecord.en.yml (Basque)
[ci skip]

* New translations en.yml (Czech)
[ci skip]

* New translations simple_form.en.yml (Ukrainian)
[ci skip]

* New translations simple_form.en.yml (Turkish)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Swedish)
[ci skip]

* New translations simple_form.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations simple_form.en.yml (Spanish)
[ci skip]

* New translations simple_form.en.yml (Slovenian)
[ci skip]

* New translations simple_form.en.yml (Slovak)
[ci skip]

* New translations simple_form.en.yml (Serbian (Latin))
[ci skip]

* New translations simple_form.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations simple_form.en.yml (Russian)
[ci skip]

* New translations simple_form.en.yml (Romanian)
[ci skip]

* New translations simple_form.en.yml (Georgian)
[ci skip]

* New translations simple_form.en.yml (French)
[ci skip]

* New translations en.yml (Korean)
[ci skip]

* New translations en.yml (Portuguese, Brazilian)
[ci skip]

* New translations en.yml (Slovenian)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Serbian (Latin))
[ci skip]

* New translations en.yml (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Russian)
[ci skip]

* New translations en.yml (Romanian)
[ci skip]

* New translations en.yml (Portuguese)
[ci skip]

* New translations en.yml (Swedish)
[ci skip]

* New translations en.yml (Polish)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations en.yml (Occitan)
[ci skip]

* New translations en.yml (Norwegian)
[ci skip]

* New translations en.yml (Malay)
[ci skip]

* New translations en.yml (Lithuanian)
[ci skip]

* New translations en.yml (Latvian)
[ci skip]

* New translations en.yml (Spanish)
[ci skip]

* New translations en.yml (Tamil)
[ci skip]

* New translations simple_form.en.yml (Finnish)
[ci skip]

* New translations simple_form.en.yml (Chinese Simplified)
[ci skip]

* New translations simple_form.en.yml (Esperanto)
[ci skip]

* New translations simple_form.en.yml (Danish)
[ci skip]

* New translations simple_form.en.yml (Croatian)
[ci skip]

* New translations simple_form.en.yml (Corsican)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional)
[ci skip]

* New translations simple_form.en.yml (Catalan)
[ci skip]

* New translations en.yml (Telugu)
[ci skip]

* New translations simple_form.en.yml (Bulgarian)
[ci skip]

* New translations simple_form.en.yml (Basque)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Ukrainian)
[ci skip]

* New translations en.yml (Turkish)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Japanese)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations activerecord.en.yml (Finnish)
[ci skip]

* New translations en.json (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Greek)
[ci skip]

* New translations doorkeeper.en.yml (Italian)
[ci skip]

* New translations doorkeeper.en.yml (Italian)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Polish)
[ci skip]

* New translations doorkeeper.en.yml (Czech)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations en.json (Italian)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Polish)
[ci skip]

* New translations en.json (Dutch)
[ci skip]

* New translations en.json (German)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-22 00:57:48 +02:00
Eugen Rochko
1b052c7b2d Remove expensive counters from federation page in admin UI (#11139) 2019-06-22 00:39:09 +02:00
Eugen Rochko
707ddf7808 Change domain blocks to automatically support subdomains (#11138)
* Change domain blocks to automatically support subdomains

If a more authoritative domain is blocked (example.com), then the
same block will be applied to a subdomain (foo.example.com)

* Match subdomains of existing accounts when blocking/unblocking domains

* Improve code style
2019-06-22 00:13:10 +02:00
Eugen Rochko
49ebda4d49 Change audio format from ogg to mp3 for wider compatibility (#11141)
* Change audio format from ogg to mp3 for wider compatibility

* Add media description as title to links of unknown media attachments
2019-06-21 22:59:44 +02:00
Thibaut Girka
38d2882447 Fix NavigationBar styling 2019-06-20 19:19:46 +02:00
Thibaut Girka
bb9459774d Add NavigationBar to getting started column in single-column mode
Fixes #1131
2019-06-20 19:19:46 +02:00
ThibG
f717d7a92d Merge pull request #1134 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-20 19:19:30 +02:00
Eugen Rochko
8f23726918 Fix converted media being saved with original extension and mime type (#11130) 2019-06-20 10:52:36 +02:00
Eugen Rochko
7696f77245 Add moderation API (#9387)
Fix #8580
Fix #7143
2019-06-20 02:52:34 +02:00
Acid Chicken (硫酸鶏)
33144e132d Fix layout of identity proofs settings (#11126) 2019-06-20 02:18:06 +02:00
Eugen Rochko
f7f23b4a19 Add audio uploads (#11123)
* Add audio uploads

Fix #4827

Accept uploads of OGG, WAV, FLAC, OPUS and MP3 files, and converts
them to OGG. Media attachments get a new `audio` type. In the UI,
audio uploads are displayed identically to video uploads.

* Improve code style
2019-06-19 23:42:38 +02:00
ThibG
3771a993b7 [Glitch] Completely hide toots matched by “irreversible” filters even if they got to the client 2019-06-19 19:16:13 +02:00
Thibaut Girka
032a669622 Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/views/admin/settings/edit.html.haml
  Conflict on theme VS flavour/skin handling.
  Keep our version.
2019-06-19 18:49:25 +02:00
Thibaut Girka
356e9150df Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/lib/sanitize_config.rb
  Keep our version, we support the tags upstream transforms.
- package.json
- yarn.lock
2019-06-19 18:36:16 +02:00
Eugen Rochko
e9a11dca19 Fix inconsistent interpolation in sk.yml (#11124) 2019-06-19 18:25:06 +02:00
Alix Rossi
26d3b2efce Add label for admin theme selector (#11121)
* Add simple_form default for admin theme selector

* Revert "Add simple_form default for admin theme selector"

This reverts commit 0b736f78a87d61075f9b9f774d8da80e1e897b47.

* Add setting_theme label to admin theme selector
2019-06-19 17:30:08 +02:00
Thibaut Girka
f4b008906d Change preferences link in local settings modal from sliders to cog for consistency 2019-06-19 15:19:23 +02:00
Thibaut Girka
ed2f0f6152 Fix bottom margin of lists in toots 2019-06-19 15:19:03 +02:00
Thibaut Girka
a06e7bc3fb Change plaintext icon in composer options 2019-06-19 07:03:17 +02:00
Eugen Rochko
ede0be5dba New Crowdin translations (#11077)
* New translations en.json (Persian)
[ci skip]

* New translations en.yml (Dutch)
[ci skip]

* New translations simple_form.en.yml (Dutch)
[ci skip]

* New translations activerecord.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Dutch)
[ci skip]

* New translations doorkeeper.en.yml (Dutch)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Czech)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.json (Czech)
[ci skip]

* New translations en.json (Greek)
[ci skip]

* New translations simple_form.en.yml (Greek)
[ci skip]

* New translations en.json (Greek)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations en.json (Persian)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations activerecord.en.yml (Persian)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Japanese)
[ci skip]

* New translations en.yml (Japanese)
[ci skip]

* New translations en.json (Bengali)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations simple_form.en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations activerecord.en.yml (Welsh)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations en.json (Welsh)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations doorkeeper.en.yml (Welsh)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations activerecord.en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations activerecord.en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.json (Arabic)
[ci skip]

* New translations en.yml (Arabic)
[ci skip]

* New translations devise.en.yml (Arabic)
[ci skip]

* New translations doorkeeper.en.yml (Arabic)
[ci skip]

* New translations doorkeeper.en.yml (Welsh)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations activerecord.en.yml (Japanese)
[ci skip]

* New translations devise.en.yml (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Japanese)
[ci skip]

* New translations simple_form.en.yml (Japanese)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.json (Corsican)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations activerecord.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations en.yml (Japanese)
[ci skip]

* New translations en.yml (Japanese)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.json (Arabic)
[ci skip]

* New translations en.yml (Arabic)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.json (Spanish)
[ci skip]

* New translations en.yml (Spanish)
[ci skip]

* New translations en.json (Basque)
[ci skip]

* New translations en.yml (Basque)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations activerecord.en.yml (Hungarian)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-18 22:58:18 +02:00
Thibaut Girka
5ea45351a0 Fix rendering of emoji in public pages 2019-06-18 20:14:08 +02:00
ThibG
c8fae508cf Completely hide toots matched by “irreversible” filters even if they got to the client (#11113)
Fixes #11090
2019-06-18 18:23:08 +02:00
ThibG
17747e2cd7 Fix User#active scope only returning suspended users (#11111)
Fix a regression from #10660
2019-06-18 18:22:02 +02:00
Thibaut Girka
48ec6abaca Fix streaming server crashing when updating filters 2019-06-18 16:50:04 +02:00
dependabot-preview[bot]
83dd4d4204 Bump enzyme-adapter-react-16 from 1.7.1 to 1.14.0 (#11105)
Bumps [enzyme-adapter-react-16](https://github.com/airbnb/enzyme/tree/HEAD/packages/enzyme-adapter-react-16) from 1.7.1 to 1.14.0.
- [Release notes](https://github.com/airbnb/enzyme/releases)
- [Changelog](https://github.com/airbnb/enzyme/blob/master/CHANGELOG.md)
- [Commits](https://github.com/airbnb/enzyme/commits/enzyme-adapter-react-16@1.14.0/packages/enzyme-adapter-react-16)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-18 00:28:28 +09:00
dependabot-preview[bot]
b403c33fb4 Bump webpack-cli from 3.3.2 to 3.3.4 (#11106)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.2 to 3.3.4.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.2...v3.3.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-18 00:20:54 +09:00
dependabot-preview[bot]
7555a0017e Bump webpack from 4.29.6 to 4.34.0 (#11108)
Bumps [webpack](https://github.com/webpack/webpack) from 4.29.6 to 4.34.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.29.6...v4.34.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-18 00:19:00 +09:00
dependabot-preview[bot]
85ec79cedf Bump enzyme from 3.8.0 to 3.10.0 (#11107)
Bumps [enzyme](https://github.com/airbnb/enzyme/tree/HEAD/packages/enzyme) from 3.8.0 to 3.10.0.
- [Release notes](https://github.com/airbnb/enzyme/releases)
- [Changelog](https://github.com/airbnb/enzyme/blob/master/CHANGELOG.md)
- [Commits](https://github.com/airbnb/enzyme/commits/enzyme@3.10.0/packages/enzyme)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-18 00:04:24 +09:00
dependabot-preview[bot]
54438042f1 Bump capybara from 3.22.0 to 3.24.0 (#11100)
Bumps [capybara](https://github.com/teamcapybara/capybara) from 3.22.0 to 3.24.0.
- [Release notes](https://github.com/teamcapybara/capybara/releases)
- [Changelog](https://github.com/teamcapybara/capybara/blob/master/History.md)
- [Commits](https://github.com/teamcapybara/capybara/compare/3.22.0...3.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 23:23:13 +09:00
dependabot-preview[bot]
119cb4d473 Bump file-loader from 3.0.1 to 4.0.0 (#11104)
Bumps [file-loader](https://github.com/webpack-contrib/file-loader) from 3.0.1 to 4.0.0.
- [Release notes](https://github.com/webpack-contrib/file-loader/releases)
- [Changelog](https://github.com/webpack-contrib/file-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/file-loader/compare/v3.0.1...v4.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 23:14:52 +09:00
dependabot-preview[bot]
9639a7f87a Bump ox from 2.10.1 to 2.11.0 (#11101)
Bumps ox from 2.10.1 to 2.11.0.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 22:50:20 +09:00
dependabot-preview[bot]
10c4c21298 Bump lograge from 0.11.1 to 0.11.2 (#11102)
Bumps [lograge](https://github.com/roidrage/lograge) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/roidrage/lograge/releases)
- [Changelog](https://github.com/roidrage/lograge/blob/master/CHANGELOG.md)
- [Commits](https://github.com/roidrage/lograge/compare/v0.11.1...v0.11.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:23:29 +09:00
dependabot-preview[bot]
f4539845e0 Bump webmock from 3.5.1 to 3.6.0 (#11031)
Bumps [webmock](https://github.com/bblimke/webmock) from 3.5.1 to 3.6.0.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.5.1...v3.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:17:45 +09:00
dependabot-preview[bot]
31eed31e37 Bump rellax from 1.7.1 to 1.10.0 (#11040)
Bumps [rellax](https://github.com/dixonandmoe/rellax) from 1.7.1 to 1.10.0.
- [Release notes](https://github.com/dixonandmoe/rellax/releases)
- [Commits](https://github.com/dixonandmoe/rellax/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:17:32 +09:00
dependabot-preview[bot]
efb07f177d Bump webpack-bundle-analyzer from 3.1.0 to 3.3.2 (#11039)
Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 3.1.0 to 3.3.2.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v3.1.0...v3.3.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:17:11 +09:00
dependabot-preview[bot]
bab2231470 Bump autoprefixer from 9.5.1 to 9.6.0 (#11038)
Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/9.5.1...9.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:17:02 +09:00
dependabot-preview[bot]
a9ba6a880e Bump eslint-plugin-import from 2.14.0 to 2.17.3 (#11037)
Bumps [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import) from 2.14.0 to 2.17.3.
- [Release notes](https://github.com/benmosher/eslint-plugin-import/releases)
- [Changelog](https://github.com/benmosher/eslint-plugin-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/benmosher/eslint-plugin-import/compare/v2.14.0...v2.17.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:16:47 +09:00
dependabot-preview[bot]
e67f38020f Bump httplog from 1.3.0 to 1.3.1 (#11034)
Bumps [httplog](https://github.com/trusche/httplog) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/trusche/httplog/releases)
- [Changelog](https://github.com/trusche/httplog/blob/master/CHANGELOG.md)
- [Commits](https://github.com/trusche/httplog/compare/v1.3.0...v1.3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:16:22 +09:00
dependabot-preview[bot]
04b4d2b4fa Bump pghero from 2.2.0 to 2.2.1 (#11033)
Bumps [pghero](https://github.com/ankane/pghero) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/ankane/pghero/releases)
- [Changelog](https://github.com/ankane/pghero/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ankane/pghero/compare/v2.2.0...v2.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:16:15 +09:00
dependabot-preview[bot]
7b058c5687 Bump rubocop-rails from 2.0.0 to 2.0.1 (#11032)
Bumps [rubocop-rails](https://github.com/rubocop-hq/rubocop-rails) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/rubocop-hq/rubocop-rails/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop-rails/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop-rails/compare/v2.0.0...v2.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:16:03 +09:00
dependabot-preview[bot]
f765cd97b2 Bump aws-sdk-s3 from 1.41.0 to 1.42.0 (#11030)
Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.41.0 to 1.42.0.
- [Release notes](https://github.com/aws/aws-sdk-ruby/releases)
- [Changelog](https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-ruby/compare/v1.41.0...v1.42.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-17 21:15:33 +09:00
Eugen Rochko
103a9f4466 Fix sanitizer making block level elements unreadable (#10836)
Fix #10834
2019-06-16 21:46:36 +02:00
Thibaut Girka
f57a0f89a8 Fix styling of poll options on public pages in glitch flavour 2019-06-16 21:33:27 +02:00
ThibG
118701b548 Merge pull request #1121 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-16 21:25:37 +02:00
Thibaut Girka
5717f75340 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-16 21:09:19 +02:00
Thibaut Girka
d3aaacb6d4 Do not scroll in the compose panel on single-column 2019-06-16 19:13:22 +02:00
Eugen Rochko
65efe892cf Fix check-i18n builds (#11084)
* Fix check-i18n builds

* Remove check for missing plural forms
2019-06-16 19:12:12 +02:00
ThibG
01e362316c Do not scroll in the compose panel on single-column (#11093) 2019-06-16 18:46:55 +02:00
Yusuke Nakamura
0828126784 Update translating platform has changed to crowdin (CONTRIBUTING.md) (#11094)
Restore and change from 89096860
2019-06-16 18:36:47 +02:00
Thibaut Girka
a29ab6f1bd Fix composer buttons overflowing the composer box in single-column 2019-06-16 15:55:55 +02:00
Eugen Rochko
b6f76d1306 [Glitch] Change full logo to use primary text color of the given theme
Port 20dda5cca0 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-15 18:49:44 +02:00
Thibaut Girka
e433386545 Fix replying not automatically switching to compose form on mobile 2019-06-14 20:37:54 +02:00
Dan Hunsaker
54192a9b6f Resync Nanobox files with the 2.9.0 release (#11083) 2019-06-14 14:52:31 +02:00
ThibG
c0e5f32d13 Merge pull request #1111 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-13 23:53:21 +02:00
Thibaut Girka
0fd7a8b63d Include necessary JS in auth pages 2019-06-13 22:38:22 +02:00
ThibG
7b68e1725c [Glitch] List attachments in boost modal
Port dd45c63921 to glitch-soc
2019-06-13 22:28:51 +02:00
Thibaut Girka
60adda7e59 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-13 22:23:20 +02:00
ThibG
80849812d2 [Glitch] Fix ordering of keyboard access between CW field, textarea and emoji picker
Port b4d67fe57a to glitch-soc
2019-06-13 22:15:31 +02:00
ThibG
d1edbfaed3 [Glitch] Only show profile directory link when it's enabled
Port 1b4dcc3f78 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
ThibG
983cbd558d [Glitch] Fix border-bottom of active tab bars
Port faafc3ae25 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Thibaut Girka
0f41be5581 Fix notification badge 2019-06-13 22:15:31 +02:00
Thibaut Girka
0ccc06b87f Fix multiple-column padding 2019-06-13 22:15:31 +02:00
Thibaut Girka
a773d02946 Add hints to make local settings about layout more explicit 2019-06-13 22:15:31 +02:00
Thibaut Girka
44b1a39682 Replace link to favourites with link to bookmarks 2019-06-13 22:15:31 +02:00
Thibaut Girka
9400ec43cc Fix navigation panel 2019-06-13 22:15:31 +02:00
Thibaut Girka
127ead34c4 Restore navigation bar position glitch-soc setting 2019-06-13 22:15:31 +02:00
Thibaut Girka
b191861e15 Use forceSingleColumn only in automatic layout mode 2019-06-13 22:15:31 +02:00
Thibaut Girka
47307e6c13 Default to multiple column mode if backend doesn't have appropriate seting 2019-06-13 22:15:31 +02:00
Thibaut Girka
b222d1ae26 Fix and refactor SCSS 2019-06-13 22:15:31 +02:00
Thibaut Girka
7fd8797d20 Add app settings link to single-column mode 2019-06-13 22:15:31 +02:00
Eugen Rochko
1b130f964f [Glitch] Fix position of search icon
Port 9add88a920 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
362f3973be [Glitch] Fix some React warnings
Port cc8f6b3cda to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
3485acefed [Glitch] Fix margins on profile metadata in single column mode
Port ed19f33440 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Wenceslao Páez Chávez
867d1233c7 [Glitch] Fix overlap of emoji button on search popup
Port fe3bf3b0fc to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Takeshi Umeda
8f924eb961 [Glitch] Fix emoji picker being always displayed
Port c402c291f4 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
a5398c3df8 [Glitch] Add profile directory link to single column navigation panel
Port 6077eca240 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Takeshi Umeda
1329308bc7 [Glitch] Improvement variable height in single column layout
Port d93b82af87 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Hanage999
02d6187894 [Glitch] Center 2-columns layout without side effect
Port 7c1ca0c37b to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
763735f92e [Glitch] Refactor footers in web UI into a single component
Port 451e5980b6 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
abcang
9bb4f796db [Glitch] Display notifications count on a new single column
Port 3593b85423 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Hanage999
c095eed121 [Glitch] Fix wrong redirect from getting started to home in advanced Web UI
Port 4a818ac2de to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
ff88387a4a [Glitch] Improvements to the single column layout
Port 0e445ebb13 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
d99a661f08 [Glitch] Add responsive panels to the single-column layout
Port 1e5532e693 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
0bd9f23e6d [Glitch] Various improvements to single column layout
Port 84dc21d55d to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
610b4b44c4 [Glitch] Add single-column mode
Port 9ddeb30f90 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-13 22:15:31 +02:00
Eugen Rochko
c9eeb2e832 Bump version to 2.9.0 (#11074) 2019-06-13 20:19:21 +02:00
Eugen Rochko
5c7f1e8e2f New Crowdin translations (#11075)
* New translations en.json (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations en.yml (Korean)
[ci skip]

* New translations en.yml (Esperanto)
[ci skip]

* New translations simple_form.en.yml (Esperanto)
[ci skip]

* i18n-tasks normalize
2019-06-13 20:17:56 +02:00
Eugen Rochko
e6024d610d New Crowdin translations (#11073)
* New translations en.json (Arabic)
[ci skip]

* New translations en.yml (Arabic)
[ci skip]

* New translations simple_form.en.yml (Arabic)
[ci skip]

* New translations en.json (Arabic)
[ci skip]

* New translations en.yml (Arabic)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations simple_form.en.yml (Arabic)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-13 18:05:51 +02:00
ThibG
b4d67fe57a Fix ordering of keyboard access between CW field, textarea and emoji picker (#11066) 2019-06-13 17:07:43 +02:00
ThibG
dd45c63921 List attachments in reply indicator and boost modal (#10997)
* Add media attachments list to boost modal

* Add attachment list to reply indicator
2019-06-13 17:04:52 +02:00
Eugen Rochko
917f0ea619 New Crowdin translations (#11069)
* New translations en.json (Ukrainian)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Bulgarian)
[ci skip]

* New translations en.json (Telugu)
[ci skip]

* New translations en.json (Tamil)
[ci skip]

* New translations en.json (Swedish)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.json (Serbian (Latin))
[ci skip]

* New translations en.json (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Catalan)
[ci skip]

* New translations en.json (Romanian)
[ci skip]

* New translations en.yml (Georgian)
[ci skip]

* New translations en.yml (Czech)
[ci skip]

* New translations en.yml (Indonesian)
[ci skip]

* New translations en.yml (Ido)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hebrew)
[ci skip]

* New translations en.yml (Galician)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (French)
[ci skip]

* New translations en.yml (Finnish)
[ci skip]

* New translations en.yml (Esperanto)
[ci skip]

* New translations en.yml (Dutch)
[ci skip]

* New translations en.yml (Danish)
[ci skip]

* New translations en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.yml (Chinese Traditional)
[ci skip]

* New translations en.yml (Croatian)
[ci skip]

* New translations en.json (Portuguese, Brazilian)
[ci skip]

* New translations en.json (Danish)
[ci skip]

* New translations en.json (Croatian)
[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.json (Chinese Traditional)
[ci skip]

* New translations en.json (Catalan)
[ci skip]

* New translations en.json (Asturian)
[ci skip]

* New translations en.json (Armenian)
[ci skip]

* New translations en.json (Albanian)
[ci skip]

* New translations en.json (Portuguese)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (Bulgarian)
[ci skip]

* New translations en.json (Kazakh)
[ci skip]

* New translations en.json (Georgian)
[ci skip]

* New translations en.json (Persian)
[ci skip]

* New translations en.json (Occitan)
[ci skip]

* New translations en.json (Norwegian)
[ci skip]

* New translations en.json (Lithuanian)
[ci skip]

* New translations en.json (Latvian)
[ci skip]

* New translations en.json (Malay)
[ci skip]

* New translations en.json (Indonesian)
[ci skip]

* New translations en.json (Ido)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Hebrew)
[ci skip]

* New translations en.json (Italian)
[ci skip]

* New translations activerecord.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations devise.en.yml (Kazakh)
[ci skip]

* New translations devise.en.yml (Japanese)
[ci skip]

* New translations devise.en.yml (Italian)
[ci skip]

* New translations devise.en.yml (Indonesian)
[ci skip]

* New translations devise.en.yml (Hebrew)
[ci skip]

* New translations devise.en.yml (Ido)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Greek)
[ci skip]

* New translations devise.en.yml (Georgian)
[ci skip]

* New translations devise.en.yml (German)
[ci skip]

* New translations devise.en.yml (Korean)
[ci skip]

* New translations devise.en.yml (Norwegian)
[ci skip]

* New translations devise.en.yml (Occitan)
[ci skip]

* New translations devise.en.yml (Persian)
[ci skip]

* New translations devise.en.yml (Polish)
[ci skip]

* New translations devise.en.yml (Portuguese)
[ci skip]

* New translations devise.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations devise.en.yml (Russian)
[ci skip]

* New translations devise.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations devise.en.yml (Serbian (Latin))
[ci skip]

* New translations devise.en.yml (French)
[ci skip]

* New translations devise.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Galician)
[ci skip]

* New translations activerecord.en.yml (Turkish)
[ci skip]

* New translations devise.en.yml (Finnish)
[ci skip]

* New translations devise.en.yml (Arabic)
[ci skip]

* New translations devise.en.yml (Spanish)
[ci skip]

* New translations activerecord.en.yml (Serbian (Latin))
[ci skip]

* New translations activerecord.en.yml (Slovak)
[ci skip]

* New translations activerecord.en.yml (Slovenian)
[ci skip]

* New translations activerecord.en.yml (Spanish)
[ci skip]

* New translations activerecord.en.yml (Swedish)
[ci skip]

* New translations activerecord.en.yml (Thai)
[ci skip]

* New translations activerecord.en.yml (Ukrainian)
[ci skip]

* New translations activerecord.en.yml (Welsh)
[ci skip]

* New translations devise.en.yml (Albanian)
[ci skip]

* New translations devise.en.yml (Esperanto)
[ci skip]

* New translations devise.en.yml (Asturian)
[ci skip]

* New translations devise.en.yml (Basque)
[ci skip]

* New translations devise.en.yml (Bulgarian)
[ci skip]

* New translations devise.en.yml (Catalan)
[ci skip]

* New translations devise.en.yml (Chinese Simplified)
[ci skip]

* New translations devise.en.yml (Chinese Traditional)
[ci skip]

* New translations devise.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations devise.en.yml (Corsican)
[ci skip]

* New translations devise.en.yml (Croatian)
[ci skip]

* New translations devise.en.yml (Danish)
[ci skip]

* New translations devise.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Slovenian)
[ci skip]

* New translations devise.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese)
[ci skip]

* New translations doorkeeper.en.yml (Indonesian)
[ci skip]

* New translations doorkeeper.en.yml (Italian)
[ci skip]

* New translations doorkeeper.en.yml (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Kazakh)
[ci skip]

* New translations doorkeeper.en.yml (Norwegian)
[ci skip]

* New translations doorkeeper.en.yml (Occitan)
[ci skip]

* New translations doorkeeper.en.yml (Persian)
[ci skip]

* New translations doorkeeper.en.yml (Polish)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations doorkeeper.en.yml (Russian)
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Spanish)
[ci skip]

* New translations doorkeeper.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Turkish)
[ci skip]

* New translations doorkeeper.en.yml (Ukrainian)
[ci skip]

* New translations doorkeeper.en.yml (Ido)
[ci skip]

* New translations doorkeeper.en.yml (Hebrew)
[ci skip]

* New translations doorkeeper.en.yml (Catalan)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Turkish)
[ci skip]

* New translations devise.en.yml (Ukrainian)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations doorkeeper.en.yml (Albanian)
[ci skip]

* New translations doorkeeper.en.yml (Arabic)
[ci skip]

* New translations doorkeeper.en.yml (Asturian)
[ci skip]

* New translations doorkeeper.en.yml (Bulgarian)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Simplified)
[ci skip]

* New translations doorkeeper.en.yml (Greek)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations doorkeeper.en.yml (Croatian)
[ci skip]

* New translations doorkeeper.en.yml (Czech)
[ci skip]

* New translations doorkeeper.en.yml (Danish)
[ci skip]

* New translations doorkeeper.en.yml (Dutch)
[ci skip]

* New translations doorkeeper.en.yml (Esperanto)
[ci skip]

* New translations doorkeeper.en.yml (Finnish)
[ci skip]

* New translations doorkeeper.en.yml (French)
[ci skip]

* New translations doorkeeper.en.yml (Galician)
[ci skip]

* New translations doorkeeper.en.yml (Georgian)
[ci skip]

* New translations activerecord.en.yml (Russian)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations activerecord.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations simple_form.en.yml (Dutch)
[ci skip]

* New translations simple_form.en.yml (Asturian)
[ci skip]

* New translations simple_form.en.yml (Basque)
[ci skip]

* New translations simple_form.en.yml (Bulgarian)
[ci skip]

* New translations simple_form.en.yml (Catalan)
[ci skip]

* New translations simple_form.en.yml (Chinese Simplified)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations simple_form.en.yml (Corsican)
[ci skip]

* New translations simple_form.en.yml (Croatian)
[ci skip]

* New translations simple_form.en.yml (Danish)
[ci skip]

* New translations simple_form.en.yml (Esperanto)
[ci skip]

* New translations simple_form.en.yml (Albanian)
[ci skip]

* New translations simple_form.en.yml (Finnish)
[ci skip]

* New translations simple_form.en.yml (French)
[ci skip]

* New translations simple_form.en.yml (Galician)
[ci skip]

* New translations simple_form.en.yml (Georgian)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (Hebrew)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Ido)
[ci skip]

* New translations simple_form.en.yml (Indonesian)
[ci skip]

* New translations simple_form.en.yml (Italian)
[ci skip]

* New translations simple_form.en.yml (Japanese)
[ci skip]

* New translations simple_form.en.yml (Arabic)
[ci skip]

* New translations simple_form.en.yml (Korean)
[ci skip]

* New translations simple_form.en.yml (Czech)
[ci skip]

* New translations devise.en.yml (Czech)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations en.yml (Kazakh)
[ci skip]

* New translations en.yml (Latvian)
[ci skip]

* New translations en.yml (Lithuanian)
[ci skip]

* New translations en.yml (Malay)
[ci skip]

* New translations en.yml (Norwegian)
[ci skip]

* New translations en.yml (Occitan)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations en.yml (Portuguese)
[ci skip]

* New translations en.yml (Ukrainian)
[ci skip]

* New translations en.yml (Portuguese, Brazilian)
[ci skip]

* New translations en.yml (Romanian)
[ci skip]

* New translations en.yml (Russian)
[ci skip]

* New translations en.yml (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Serbian (Latin))
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovenian)
[ci skip]

* New translations en.yml (Spanish)
[ci skip]

* New translations en.yml (Swedish)
[ci skip]

* New translations en.yml (Tamil)
[ci skip]

* New translations en.yml (Telugu)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Turkish)
[ci skip]

* New translations activerecord.en.yml (Portuguese)
[ci skip]

* New translations activerecord.en.yml (Hebrew)
[ci skip]

* New translations activerecord.en.yml (Corsican)
[ci skip]

* New translations activerecord.en.yml (Czech)
[ci skip]

* New translations activerecord.en.yml (Danish)
[ci skip]

* New translations activerecord.en.yml (Dutch)
[ci skip]

* New translations activerecord.en.yml (Esperanto)
[ci skip]

* New translations activerecord.en.yml (French)
[ci skip]

* New translations activerecord.en.yml (Galician)
[ci skip]

* New translations activerecord.en.yml (Georgian)
[ci skip]

* New translations activerecord.en.yml (German)
[ci skip]

* New translations activerecord.en.yml (Greek)
[ci skip]

* New translations activerecord.en.yml (Indonesian)
[ci skip]

* New translations activerecord.en.yml (Italian)
[ci skip]

* New translations activerecord.en.yml (Japanese)
[ci skip]

* New translations activerecord.en.yml (Kazakh)
[ci skip]

* New translations activerecord.en.yml (Norwegian)
[ci skip]

* New translations activerecord.en.yml (Occitan)
[ci skip]

* New translations activerecord.en.yml (Persian)
[ci skip]

* New translations activerecord.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations activerecord.en.yml (Chinese Simplified)
[ci skip]

* New translations simple_form.en.yml (Slovenian)
[ci skip]

* New translations simple_form.en.yml (Norwegian)
[ci skip]

* New translations simple_form.en.yml (Occitan)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Portuguese)
[ci skip]

* New translations simple_form.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations simple_form.en.yml (Romanian)
[ci skip]

* New translations simple_form.en.yml (Russian)
[ci skip]

* New translations simple_form.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations simple_form.en.yml (Serbian (Latin))
[ci skip]

* New translations simple_form.en.yml (Slovak)
[ci skip]

* New translations simple_form.en.yml (Spanish)
[ci skip]

* New translations activerecord.en.yml (Catalan)
[ci skip]

* New translations simple_form.en.yml (Swedish)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Turkish)
[ci skip]

* New translations simple_form.en.yml (Ukrainian)
[ci skip]

* New translations activerecord.en.yml (Albanian)
[ci skip]

* New translations activerecord.en.yml (Arabic)
[ci skip]

* New translations activerecord.en.yml (Asturian)
[ci skip]

* New translations activerecord.en.yml (Basque)
[ci skip]

* New translations en.json (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-13 16:49:19 +02:00
Thibaut Girka
fe5c4f976c Fix clicking on the elefriend 2019-06-13 13:49:05 +02:00
Eugen Rochko
275f09ccab Bump version to 2.9.0rc2 (#11070) 2019-06-13 00:43:59 +02:00
ThibG
a4a502e85c Do not expand toot when clicking on a poll option (#11067)
Fixes regression introduced by e9ddd5a159
2019-06-13 00:16:46 +02:00
ThibG
1b4dcc3f78 Only show profile directory link when it's enabled (#11064) 2019-06-13 00:16:27 +02:00
ThibG
c98573fdf9 Add button to conveniently copy OAuth code (#11065) 2019-06-13 00:14:42 +02:00
ThibG
faafc3ae25 Fix border-bottom of active tab bars (#11068) 2019-06-13 00:14:27 +02:00
Eugen Rochko
809d1faa49 New Crowdin translations (#11062)
* New translations doorkeeper.en.yml (French)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations doorkeeper.en.yml (Finnish)
[ci skip]

* New translations doorkeeper.en.yml (Esperanto)
[ci skip]

* New translations doorkeeper.en.yml (Dutch)
[ci skip]

* New translations doorkeeper.en.yml (Danish)
[ci skip]

* New translations doorkeeper.en.yml (Czech)
[ci skip]

* New translations doorkeeper.en.yml (Croatian)
[ci skip]

* New translations activerecord.en.yml (Asturian)
[ci skip]

* New translations activerecord.en.yml (Arabic)
[ci skip]

* New translations en.yml (Danish)
[ci skip]

* New translations simple_form.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Georgian)
[ci skip]

* New translations devise.en.yml (Galician)
[ci skip]

* New translations devise.en.yml (French)
[ci skip]

* New translations devise.en.yml (Finnish)
[ci skip]

* New translations devise.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Danish)
[ci skip]

* New translations devise.en.yml (Croatian)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (Georgian)
[ci skip]

* New translations simple_form.en.yml (Galician)
[ci skip]

* New translations simple_form.en.yml (French)
[ci skip]

* New translations simple_form.en.yml (Finnish)
[ci skip]

* New translations simple_form.en.yml (Esperanto)
[ci skip]

* New translations simple_form.en.yml (Danish)
[ci skip]

* New translations activerecord.en.yml (Albanian)
[ci skip]

* New translations simple_form.en.yml (Basque)
[ci skip]

* New translations en.yml (Czech)
[ci skip]

* New translations devise.en.yml (Corsican)
[ci skip]

* New translations simple_form.en.yml (Albanian)
[ci skip]

* New translations simple_form.en.yml (Asturian)
[ci skip]

* New translations simple_form.en.yml (Croatian)
[ci skip]

* New translations simple_form.en.yml (Bulgarian)
[ci skip]

* New translations simple_form.en.yml (Catalan)
[ci skip]

* New translations simple_form.en.yml (Chinese Simplified)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations simple_form.en.yml (Corsican)
[ci skip]

* New translations en.yml (Dutch)
[ci skip]

* New translations en.yml (Croatian)
[ci skip]

* New translations en.json (Croatian)
[ci skip]

* New translations devise.en.yml (Asturian)
[ci skip]

* New translations en.yml (Corsican)
[ci skip]

* New translations en.json (German)
[ci skip]

* New translations en.json (Georgian)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (Danish)
[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.json (Chinese Traditional)
[ci skip]

* New translations en.json (Bulgarian)
[ci skip]

* New translations en.json (Bengali)
[ci skip]

* New translations en.json (Asturian)
[ci skip]

* New translations en.json (Armenian)
[ci skip]

* New translations devise.en.yml (Czech)
[ci skip]

* New translations simple_form.en.yml (Czech)
[ci skip]

* New translations devise.en.yml (Basque)
[ci skip]

* New translations devise.en.yml (Albanian)
[ci skip]

* New translations devise.en.yml (Bulgarian)
[ci skip]

* New translations en.yml (Armenian)
[ci skip]

* New translations devise.en.yml (Catalan)
[ci skip]

* New translations activerecord.en.yml (Corsican)
[ci skip]

* New translations activerecord.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.yml (Asturian)
[ci skip]

* New translations activerecord.en.yml (Danish)
[ci skip]

* New translations en.yml (Basque)
[ci skip]

* New translations en.yml (Bengali)
[ci skip]

* New translations en.yml (Bulgarian)
[ci skip]

* New translations en.yml (Catalan)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Traditional)
[ci skip]

* New translations en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations activerecord.en.yml (Czech)
[ci skip]

* New translations activerecord.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Chinese Traditional)
[ci skip]

* New translations activerecord.en.yml (French)
[ci skip]

* New translations activerecord.en.yml (Galician)
[ci skip]

* New translations devise.en.yml (Chinese Simplified)
[ci skip]

* New translations activerecord.en.yml (Georgian)
[ci skip]

* New translations activerecord.en.yml (German)
[ci skip]

* New translations devise.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations devise.en.yml (Ido)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hebrew)
[ci skip]

* New translations devise.en.yml (Greek)
[ci skip]

* New translations devise.en.yml (German)
[ci skip]

* New translations devise.en.yml (Italian)
[ci skip]

* New translations activerecord.en.yml (Welsh)
[ci skip]

* New translations activerecord.en.yml (Ukrainian)
[ci skip]

* New translations activerecord.en.yml (Turkish)
[ci skip]

* New translations activerecord.en.yml (Thai)
[ci skip]

* New translations activerecord.en.yml (Swedish)
[ci skip]

* New translations devise.en.yml (Indonesian)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations doorkeeper.en.yml (Italian)
[ci skip]

* New translations doorkeeper.en.yml (Kazakh)
[ci skip]

* New translations doorkeeper.en.yml (Norwegian)
[ci skip]

* New translations doorkeeper.en.yml (Occitan)
[ci skip]

* New translations doorkeeper.en.yml (Persian)
[ci skip]

* New translations doorkeeper.en.yml (Polish)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese)
[ci skip]

* New translations doorkeeper.en.yml (Ido)
[ci skip]

* New translations doorkeeper.en.yml (Russian)
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Turkish)
[ci skip]

* New translations doorkeeper.en.yml (Ukrainian)
[ci skip]

* New translations doorkeeper.en.yml (Indonesian)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Kazakh)
[ci skip]

* New translations devise.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations devise.en.yml (Korean)
[ci skip]

* New translations devise.en.yml (Norwegian)
[ci skip]

* New translations devise.en.yml (Occitan)
[ci skip]

* New translations devise.en.yml (Persian)
[ci skip]

* New translations devise.en.yml (Polish)
[ci skip]

* New translations devise.en.yml (Portuguese)
[ci skip]

* New translations devise.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations devise.en.yml (Russian)
[ci skip]

* New translations devise.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Hebrew)
[ci skip]

* New translations devise.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Slovenian)
[ci skip]

* New translations devise.en.yml (Swedish)
[ci skip]

* New translations activerecord.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Turkish)
[ci skip]

* New translations devise.en.yml (Ukrainian)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations doorkeeper.en.yml (Greek)
[ci skip]

* New translations activerecord.en.yml (Slovenian)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations activerecord.en.yml (Serbian (Latin))
[ci skip]

* New translations en.yml (Latvian)
[ci skip]

* New translations en.json (Ukrainian)
[ci skip]

* New translations en.yml (Greek)
[ci skip]

* New translations en.yml (Hebrew)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Ido)
[ci skip]

* New translations en.yml (Indonesian)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations en.yml (Kazakh)
[ci skip]

* New translations en.yml (Korean)
[ci skip]

* New translations en.yml (Lithuanian)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.yml (Malay)
[ci skip]

* New translations en.yml (Norwegian)
[ci skip]

* New translations en.yml (Occitan)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations en.yml (Polish)
[ci skip]

* New translations en.yml (Portuguese)
[ci skip]

* New translations en.yml (Portuguese, Brazilian)
[ci skip]

* New translations en.yml (Romanian)
[ci skip]

* New translations en.yml (Russian)
[ci skip]

* New translations en.yml (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Serbian (Latin))
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.json (Turkish)
[ci skip]

* New translations en.json (Telugu)
[ci skip]

* New translations en.json (Norwegian)
[ci skip]

* New translations en.json (Hebrew)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.json (Ido)
[ci skip]

* New translations en.json (Indonesian)
[ci skip]

* New translations en.json (Italian)
[ci skip]

* New translations en.json (Kazakh)
[ci skip]

* New translations en.json (Korean)
[ci skip]

* New translations en.json (Latvian)
[ci skip]

* New translations en.json (Lithuanian)
[ci skip]

* New translations en.json (Malay)
[ci skip]

* New translations en.json (Occitan)
[ci skip]

* New translations en.json (Tamil)
[ci skip]

* New translations en.json (Persian)
[ci skip]

* New translations en.json (Polish)
[ci skip]

* New translations en.json (Portuguese)
[ci skip]

* New translations en.json (Portuguese, Brazilian)
[ci skip]

* New translations en.json (Romanian)
[ci skip]

* New translations en.json (Serbian (Cyrillic))
[ci skip]

* New translations en.json (Serbian (Latin))
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Spanish)
[ci skip]

* New translations en.json (Swedish)
[ci skip]

* New translations en.yml (Slovenian)
[ci skip]

* New translations en.yml (Swedish)
[ci skip]

* New translations activerecord.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations activerecord.en.yml (Japanese)
[ci skip]

* New translations simple_form.en.yml (Swedish)
[ci skip]

* New translations simple_form.en.yml (Thai)
[ci skip]

* New translations simple_form.en.yml (Turkish)
[ci skip]

* New translations simple_form.en.yml (Ukrainian)
[ci skip]

* New translations activerecord.en.yml (Greek)
[ci skip]

* New translations activerecord.en.yml (Hebrew)
[ci skip]

* New translations activerecord.en.yml (Indonesian)
[ci skip]

* New translations activerecord.en.yml (Italian)
[ci skip]

* New translations activerecord.en.yml (Kazakh)
[ci skip]

* New translations simple_form.en.yml (Slovenian)
[ci skip]

* New translations activerecord.en.yml (Norwegian)
[ci skip]

* New translations activerecord.en.yml (Occitan)
[ci skip]

* New translations activerecord.en.yml (Persian)
[ci skip]

* New translations activerecord.en.yml (Polish)
[ci skip]

* New translations activerecord.en.yml (Portuguese)
[ci skip]

* New translations activerecord.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations activerecord.en.yml (Russian)
[ci skip]

* New translations simple_form.en.yml (Slovak)
[ci skip]

* New translations en.yml (Tamil)
[ci skip]

* New translations en.yml (Telugu)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Turkish)
[ci skip]

* New translations en.yml (Ukrainian)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Greek)
[ci skip]

* New translations simple_form.en.yml (Hebrew)
[ci skip]

* New translations simple_form.en.yml (Hungarian)
[ci skip]

* New translations simple_form.en.yml (Ido)
[ci skip]

* New translations simple_form.en.yml (Indonesian)
[ci skip]

* New translations simple_form.en.yml (Italian)
[ci skip]

* New translations simple_form.en.yml (Korean)
[ci skip]

* New translations simple_form.en.yml (Serbian (Latin))
[ci skip]

* New translations simple_form.en.yml (Norwegian)
[ci skip]

* New translations simple_form.en.yml (Occitan)
[ci skip]

* New translations simple_form.en.yml (Persian)
[ci skip]

* New translations simple_form.en.yml (Polish)
[ci skip]

* New translations simple_form.en.yml (Portuguese)
[ci skip]

* New translations simple_form.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations simple_form.en.yml (Romanian)
[ci skip]

* New translations simple_form.en.yml (Russian)
[ci skip]

* New translations simple_form.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations doorkeeper.en.yml (Welsh)
[ci skip]

* New translations en.json (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations en.json (Russian)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations simple_form.en.yml (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations en.yml (German)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-12 23:46:19 +02:00
Eugen Rochko
1390da501d Change translations badge in README to Crowdin (#11054) 2019-06-12 15:56:41 +02:00
Eugen Rochko
481cc19d4d New Crowdin translations (#11060)
* New translations devise.en.yml (Chinese Traditional)
[ci skip]

* New translations devise.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations devise.en.yml (Corsican)
[ci skip]

* New translations devise.en.yml (Croatian)
[ci skip]

* New translations devise.en.yml (Danish)
[ci skip]

* New translations devise.en.yml (Dutch)
[ci skip]

* New translations devise.en.yml (Esperanto)
[ci skip]

* New translations devise.en.yml (Finnish)
[ci skip]

* New translations devise.en.yml (French)
[ci skip]

* New translations devise.en.yml (Galician)
[ci skip]

* New translations devise.en.yml (Georgian)
[ci skip]

* New translations devise.en.yml (German)
[ci skip]

* New translations activerecord.en.yml (Tamil)
[ci skip]

* New translations activerecord.en.yml (Spanish)
[ci skip]

* New translations activerecord.en.yml (French)
[ci skip]

* New translations activerecord.en.yml (Latvian)
[ci skip]

* New translations activerecord.en.yml (Galician)
[ci skip]

* New translations activerecord.en.yml (Georgian)
[ci skip]

* New translations activerecord.en.yml (German)
[ci skip]

* New translations activerecord.en.yml (Greek)
[ci skip]

* New translations activerecord.en.yml (Hebrew)
[ci skip]

* New translations activerecord.en.yml (Hungarian)
[ci skip]

* New translations activerecord.en.yml (Ido)
[ci skip]

* New translations activerecord.en.yml (Indonesian)
[ci skip]

* New translations activerecord.en.yml (Italian)
[ci skip]

* New translations activerecord.en.yml (Japanese)
[ci skip]

* New translations activerecord.en.yml (Kazakh)
[ci skip]

* New translations activerecord.en.yml (Korean)
[ci skip]

* New translations activerecord.en.yml (Lithuanian)
[ci skip]

* New translations activerecord.en.yml (Slovenian)
[ci skip]

* New translations activerecord.en.yml (Malay)
[ci skip]

* New translations activerecord.en.yml (Norwegian)
[ci skip]

* New translations activerecord.en.yml (Occitan)
[ci skip]

* New translations activerecord.en.yml (Persian)
[ci skip]

* New translations activerecord.en.yml (Polish)
[ci skip]

* New translations activerecord.en.yml (Portuguese)
[ci skip]

* New translations activerecord.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations activerecord.en.yml (Romanian)
[ci skip]

* New translations activerecord.en.yml (Russian)
[ci skip]

* New translations activerecord.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations activerecord.en.yml (Serbian (Latin))
[ci skip]

* New translations activerecord.en.yml (Slovak)
[ci skip]

* New translations simple_form.en.yml (Corsican)
[ci skip]

* New translations simple_form.en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Chinese Traditional)
[ci skip]

* New translations en.json (Russian)
[ci skip]

* New translations en.json (Kazakh)
[ci skip]

* New translations en.json (Korean)
[ci skip]

* New translations en.json (Latvian)
[ci skip]

* New translations en.json (Lithuanian)
[ci skip]

* New translations en.json (Malay)
[ci skip]

* New translations en.json (Norwegian)
[ci skip]

* New translations en.json (Occitan)
[ci skip]

* New translations en.json (Persian)
[ci skip]

* New translations en.json (Polish)
[ci skip]

* New translations en.json (Portuguese)
[ci skip]

* New translations en.json (Portuguese, Brazilian)
[ci skip]

* New translations en.json (Romanian)
[ci skip]

* New translations en.json (Serbian (Cyrillic))
[ci skip]

* New translations en.json (Italian)
[ci skip]

* New translations en.json (Serbian (Latin))
[ci skip]

* New translations en.json (Slovak)
[ci skip]

* New translations en.json (Slovenian)
[ci skip]

* New translations en.json (Spanish)
[ci skip]

* New translations en.json (Swedish)
[ci skip]

* New translations en.json (Tamil)
[ci skip]

* New translations en.json (Telugu)
[ci skip]

* New translations en.json (Thai)
[ci skip]

* New translations en.json (Turkish)
[ci skip]

* New translations en.json (Ukrainian)
[ci skip]

* New translations en.json (Welsh)
[ci skip]

* New translations en.yml (Albanian)
[ci skip]

* New translations en.json (Japanese)
[ci skip]

* New translations en.json (Indonesian)
[ci skip]

* New translations en.yml (Armenian)
[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)
[ci skip]

* New translations simple_form.en.yml (Czech)
[ci skip]

* New translations devise.en.yml (Czech)
[ci skip]

* New translations en.json (Albanian)
[ci skip]

* New translations en.json (Arabic)
[ci skip]

* New translations en.json (Armenian)
[ci skip]

* New translations en.json (Asturian)
[ci skip]

* New translations en.json (Basque)
[ci skip]

* New translations en.json (Bengali)
[ci skip]

* New translations en.json (Bulgarian)
[ci skip]

* New translations en.json (Catalan)
[ci skip]

* New translations en.json (Chinese Simplified)
[ci skip]

* New translations en.json (Chinese Traditional)
[ci skip]

* New translations en.json (Corsican)
[ci skip]

* New translations en.json (Ido)
[ci skip]

* New translations en.json (Croatian)
[ci skip]

* New translations simple_form.en.yml (Chinese Simplified)
[ci skip]

* New translations en.json (Dutch)
[ci skip]

* New translations en.json (Esperanto)
[ci skip]

* New translations en.json (Finnish)
[ci skip]

* New translations en.json (French)
[ci skip]

* New translations en.json (Galician)
[ci skip]

* New translations en.json (Georgian)
[ci skip]

* New translations en.json (German)
[ci skip]

* New translations en.json (Greek)
[ci skip]

* New translations en.json (Hebrew)
[ci skip]

* New translations en.json (Hungarian)
[ci skip]

* New translations en.yml (Arabic)
[ci skip]

* New translations en.json (Danish)
[ci skip]

* New translations en.yml (Asturian)
[ci skip]

* New translations en.yml (Tamil)
[ci skip]

* New translations en.yml (Persian)
[ci skip]

* New translations en.yml (Polish)
[ci skip]

* New translations en.yml (Portuguese)
[ci skip]

* New translations en.yml (Portuguese, Brazilian)
[ci skip]

* New translations en.yml (Romanian)
[ci skip]

* New translations en.yml (Russian)
[ci skip]

* New translations en.yml (Serbian (Cyrillic))
[ci skip]

* New translations en.yml (Serbian (Latin))
[ci skip]

* New translations en.yml (Slovak)
[ci skip]

* New translations en.yml (Slovenian)
[ci skip]

* New translations en.yml (Spanish)
[ci skip]

* New translations en.yml (Swedish)
[ci skip]

* New translations en.yml (Telugu)
[ci skip]

* New translations en.yml (Norwegian)
[ci skip]

* New translations en.yml (Thai)
[ci skip]

* New translations en.yml (Turkish)
[ci skip]

* New translations en.yml (Ukrainian)
[ci skip]

* New translations en.yml (Welsh)
[ci skip]

* New translations simple_form.en.yml (Arabic)
[ci skip]

* New translations simple_form.en.yml (Armenian)
[ci skip]

* New translations simple_form.en.yml (Asturian)
[ci skip]

* New translations simple_form.en.yml (Basque)
[ci skip]

* New translations simple_form.en.yml (Bengali)
[ci skip]

* New translations simple_form.en.yml (Bulgarian)
[ci skip]

* New translations simple_form.en.yml (Catalan)
[ci skip]

* New translations en.yml (Basque)
[ci skip]

* New translations en.yml (Occitan)
[ci skip]

* New translations simple_form.en.yml (Albanian)
[ci skip]

* New translations en.yml (Malay)
[ci skip]

* New translations en.yml (French)
[ci skip]

* New translations en.yml (Bengali)
[ci skip]

* New translations en.yml (Lithuanian)
[ci skip]

* New translations en.yml (Bulgarian)
[ci skip]

* New translations en.yml (Catalan)
[ci skip]

* New translations en.yml (Chinese Simplified)
[ci skip]

* New translations en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations en.yml (Corsican)
[ci skip]

* New translations en.yml (Croatian)
[ci skip]

* New translations en.yml (Danish)
[ci skip]

* New translations en.yml (Dutch)
[ci skip]

* New translations en.yml (Esperanto)
[ci skip]

* New translations en.yml (Finnish)
[ci skip]

* New translations en.yml (Chinese Traditional)
[ci skip]

* New translations en.yml (Galician)
[ci skip]

* New translations en.yml (Indonesian)
[ci skip]

* New translations en.yml (Georgian)
[ci skip]

* New translations en.yml (Latvian)
[ci skip]

* New translations en.yml (Kazakh)
[ci skip]

* New translations en.yml (Japanese)
[ci skip]

* New translations en.yml (Italian)
[ci skip]

* New translations en.yml (Ido)
[ci skip]

* New translations en.yml (Hungarian)
[ci skip]

* New translations en.yml (Hebrew)
[ci skip]

* New translations en.yml (Greek)
[ci skip]

* New translations en.yml (German)
[ci skip]

* New translations en.yml (Korean)
[ci skip]

* New translations doorkeeper.en.yml (Kazakh)
[ci skip]

* New translations doorkeeper.en.yml (Hebrew)
[ci skip]

* New translations doorkeeper.en.yml (Korean)
[ci skip]

* New translations doorkeeper.en.yml (Japanese)
[ci skip]

* New translations doorkeeper.en.yml (Italian)
[ci skip]

* New translations doorkeeper.en.yml (Indonesian)
[ci skip]

* New translations doorkeeper.en.yml (Ido)
[ci skip]

* New translations doorkeeper.en.yml (Hungarian)
[ci skip]

* New translations doorkeeper.en.yml (Finnish)
[ci skip]

* New translations doorkeeper.en.yml (Greek)
[ci skip]

* New translations doorkeeper.en.yml (German)
[ci skip]

* New translations doorkeeper.en.yml (Georgian)
[ci skip]

* New translations doorkeeper.en.yml (Galician)
[ci skip]

* New translations doorkeeper.en.yml (French)
[ci skip]

* New translations doorkeeper.en.yml (Esperanto)
[ci skip]

* New translations doorkeeper.en.yml (Dutch)
[ci skip]

* New translations doorkeeper.en.yml (Danish)
[ci skip]

* New translations doorkeeper.en.yml (Lithuanian)
[ci skip]

* New translations doorkeeper.en.yml (Czech)
[ci skip]

* New translations doorkeeper.en.yml (Latvian)
[ci skip]

* New translations doorkeeper.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Malay)
[ci skip]

* New translations doorkeeper.en.yml (Slovak)
[ci skip]

* New translations doorkeeper.en.yml (Corsican)
[ci skip]

* New translations doorkeeper.en.yml (Ukrainian)
[ci skip]

* New translations doorkeeper.en.yml (Turkish)
[ci skip]

* New translations doorkeeper.en.yml (Thai)
[ci skip]

* New translations doorkeeper.en.yml (Telugu)
[ci skip]

* New translations doorkeeper.en.yml (Tamil)
[ci skip]

* New translations doorkeeper.en.yml (Swedish)
[ci skip]

* New translations doorkeeper.en.yml (Spanish)
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Norwegian)
[ci skip]

* New translations doorkeeper.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations doorkeeper.en.yml (Russian)
[ci skip]

* New translations doorkeeper.en.yml (Romanian)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations doorkeeper.en.yml (Portuguese)
[ci skip]

* New translations doorkeeper.en.yml (Polish)
[ci skip]

* New translations doorkeeper.en.yml (Persian)
[ci skip]

* New translations doorkeeper.en.yml (Occitan)
[ci skip]

* New translations doorkeeper.en.yml (Croatian)
[ci skip]

* New translations devise.en.yml (Persian)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional, Hong Kong)
[ci skip]

* New translations devise.en.yml (Latvian)
[ci skip]

* New translations devise.en.yml (Romanian)
[ci skip]

* New translations devise.en.yml (Portuguese, Brazilian)
[ci skip]

* New translations devise.en.yml (Portuguese)
[ci skip]

* New translations devise.en.yml (Polish)
[ci skip]

* New translations devise.en.yml (Occitan)
[ci skip]

* New translations devise.en.yml (Norwegian)
[ci skip]

* New translations devise.en.yml (Malay)
[ci skip]

* New translations devise.en.yml (Lithuanian)
[ci skip]

* New translations devise.en.yml (Korean)
[ci skip]

* New translations devise.en.yml (Serbian (Cyrillic))
[ci skip]

* New translations devise.en.yml (Kazakh)
[ci skip]

* New translations devise.en.yml (Japanese)
[ci skip]

* New translations devise.en.yml (Italian)
[ci skip]

* New translations devise.en.yml (Indonesian)
[ci skip]

* New translations devise.en.yml (Ido)
[ci skip]

* New translations devise.en.yml (Hungarian)
[ci skip]

* New translations devise.en.yml (Hebrew)
[ci skip]

* New translations devise.en.yml (Greek)
[ci skip]

* New translations devise.en.yml (Russian)
[ci skip]

* New translations devise.en.yml (Serbian (Latin))
[ci skip]

* New translations doorkeeper.en.yml (Chinese Traditional)
[ci skip]

* New translations doorkeeper.en.yml (Albanian)
[ci skip]

* New translations doorkeeper.en.yml (Chinese Simplified)
[ci skip]

* New translations doorkeeper.en.yml (Catalan)
[ci skip]

* New translations doorkeeper.en.yml (Bulgarian)
[ci skip]

* New translations doorkeeper.en.yml (Bengali)
[ci skip]

* New translations doorkeeper.en.yml (Basque)
[ci skip]

* New translations doorkeeper.en.yml (Asturian)
[ci skip]

* New translations doorkeeper.en.yml (Armenian)
[ci skip]

* New translations doorkeeper.en.yml (Arabic)
[ci skip]

* New translations devise.en.yml (Welsh)
[ci skip]

* New translations devise.en.yml (Slovak)
[ci skip]

* New translations devise.en.yml (Ukrainian)
[ci skip]

* New translations devise.en.yml (Turkish)
[ci skip]

* New translations devise.en.yml (Thai)
[ci skip]

* New translations devise.en.yml (Telugu)
[ci skip]

* New translations devise.en.yml (Tamil)
[ci skip]

* New translations devise.en.yml (Swedish)
[ci skip]

* New translations devise.en.yml (Spanish)
[ci skip]

* New translations devise.en.yml (Slovenian)
[ci skip]

* New translations doorkeeper.en.yml (Welsh)
[ci skip]

* i18n-tasks normalize

* yarn manage:translations
2019-06-12 15:56:18 +02:00
Eugen Rochko
da33c94c14 Fix Serbian pluralization rules requiring a "many" key (#11061) 2019-06-12 15:53:04 +02:00
Eugen Rochko
d13bc8eb6a Update crowdin.yml 2019-06-12 15:11:00 +02:00
Thibaut Girka
32bdff09c1 Properly handle unboosting statuses from detailed view
Fixes #1106
2019-06-12 10:12:51 +02:00
mayaeh
7652190509 i18n: Update Japanese translations (#11035)
* Update Japanese translations

Co-authored-by: Phroneris <phroneris@gmail.com>
Co-authored-by: ruine0213 <ruine@spiele.jp>

* Update Japanese translations
2019-06-12 02:29:48 +02:00
ThibG
cde30407c6 Merge pull request #1105 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-11 22:18:41 +02:00
Eugen Rochko
963d7e0377 Update Crowdin configuration file 2019-06-11 22:04:04 +02:00
Eugen Rochko
82899b3d2e [Glitch] Fix list not being automatically unpinned when it returns 404 in web UI
Port 92b572e2a3 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-11 21:57:32 +02:00
Thibaut Girka
7065ba5930 Merge branch 'master' into glitch-soc/merge-upstream 2019-06-11 21:55:00 +02:00
Eugen Rochko
4913c345b2 Update Crowdin configuration file 2019-06-11 21:49:12 +02:00
Masoud Abkenar
30e2f724ce l10n: update Persian translations (fa.yml) (#11052)
* l10n: update Persian translations (fa.yml)

* Fix fa.yml syntax

* i18n-tasks normalize
2019-06-12 02:55:29 +09:00
Masoud Abkenar
69c2bbcc87 l10n: update Persian translation (#11050)
* l10n: update Persian translation

* Update fa.yml
2019-06-12 00:37:52 +09:00
唐宗勛
02323aa1d8 update zh-CN translations (#11046) 2019-06-11 13:08:40 +09:00
Jeong Arm
40be49fe28 Fix Korean translate (#11047) 2019-06-11 13:06:42 +09:00
Eugen Rochko
92b572e2a3 Fix list not being automatically unpinned when it returns 404 in web UI (#11045) 2019-06-11 02:26:37 +02:00
ThibG
c64eef1206 Merge pull request #1102 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-10 23:01:56 +02:00
Eugen Rochko
ef438bd7e8 Add German translations (#11043) 2019-06-10 21:36:20 +02:00
Thibaut Girka
7778de467c Merge branch 'master' into glitch-soc/merge-upstream 2019-06-10 20:09:05 +02:00
ThibG
5bcd98172c Fix clicking on the left side of a conversation not marking it as read (#11041) 2019-06-10 19:27:10 +02:00
ふるふる
62852252dd Fix can't save preference other (#11042) 2019-06-10 19:26:43 +02:00
Eugen Rochko
fc6d27daf3 [Glitch] Fix RTL layout not being RTL within the columns area
Port 25f93f4097 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-10 18:59:59 +02:00
Eugen Rochko
59d214e54b [Glitch] Change preferences page into appearance, notifications, and other
Port SCSS changes from 1db4117030 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-10 18:59:59 +02:00
Thibaut Girka
1b0ff4cd69 Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/controllers/settings/notifications_controller.rb
- app/javascript/packs/public.js
- app/views/settings/preferences/show.html.haml
- app/views/stream_entries/_simple_status.html.haml
- config/locales/simple_form.en.yml
- config/locales/simple_form.pl.yml
- config/navigation.rb
- config/routes.rb
2019-06-10 18:59:53 +02:00
Thibaut Girka
b45f555a0c Minor cleanup 2019-06-10 16:24:09 +02:00
Thibaut Girka
b551d8aa53 Fix unboost confirmation dialog not showing up on detailed statuses 2019-06-10 16:24:09 +02:00
Thibaut Girka
ccfb48d3eb Add option to display a warning before boosting toots lacking media descriptions 2019-06-10 16:24:09 +02:00
Thibaut Girka
d61a6271c6 Add DM conversations mode similar to upstream 2019-06-10 16:23:42 +02:00
Eugen Rochko
8514ef723c Fix login sometimes redirecting to paths that are not pages (#11019)
Fix #11016
2019-06-10 12:28:13 +02:00
mayaeh
210fa3a94e Fix emoji-button appearing above privacy-dropdown (#11027) 2019-06-10 12:27:17 +02:00
Marek Ľach
420551872d Update simple_form.sk.yml (#11028) 2019-06-10 16:25:27 +09:00
manuelviens
0ef55b341d Update simple_form.fr.yml (#11021) 2019-06-10 13:12:51 +09:00
manuelviens
fbe879ceaf Update fr.yml (#11023) 2019-06-10 13:12:28 +09:00
manuelviens
ce556333ce Update fr.json (#11024) 2019-06-10 13:07:11 +09:00
Marek Ľach
4330629101 Update sk.yml (#11015)
* Update sk.yml

* Update sk.yml

* Update sk.yml

* Update sk.yml

* Update sk.yml

* Update sk.yml
2019-06-09 22:55:37 +02:00
ThibG
e428e320b6 Fix old migrations failing because of new version of strong_migrations (#11018) 2019-06-09 22:55:28 +02:00
Thibaut Girka
e16c8fbc7a Fix old migrations failing because of new version of strong_migrations
Fixes #1099
2019-06-09 22:32:12 +02:00
Jeong Arm
654fd071b7 Add missing Korean translations (#11014) 2019-06-09 21:07:50 +02:00
Alix Rossi
9a281bac8d i18n: Update Corsican front-end translation (#11010) 2019-06-09 23:23:30 +09:00
Eugen Rochko
0949c43ab3 Bump version to 2.9.0rc1 (#11004) 2019-06-09 15:53:08 +02:00
Eugen Rochko
e5bdfd1640 Run yarn manage:translations (#11008) 2019-06-09 15:52:42 +02:00
Eugen Rochko
8746f4d17b Change priority of delete activity forwards for replies and reblogs (#11002)
Fix #11001
2019-06-09 12:47:33 +02:00
Eugen Rochko
9add88a920 Fix position of search icon (#11003) 2019-06-09 01:59:42 +02:00
spla
6ecf1825ef i18n: Update Catalan translations (#10998)
* Updated Catalan strings

* Update ca.yml

* Update ca.yml

* Update ca.yml

* Update ca.yml

* Update ca.yml

* Update ca.yml

* Update ca.yml

* Update simple_form.ca.yml

* Update simple_form.ca.yml

* Update simple_form.ca.yml

* bundle exec i18n-tasks

* Update ca.json

* Update simple_form.ca.yml

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translations

* i18n: Update Catalan translation

* i18n Update Catalan translation

* i18n: Update Catalan translations

* i18n: Update Catalan translations
2019-06-09 08:32:22 +09:00
Eugen Rochko
241a8e7b5f Fix more issues in the light theme (#10996)
* Fix tabs bar in light theme

* Fix borders on small screens in light theme
2019-06-08 22:32:59 +02:00
Darius Kazemi
4431ce52a6 Specify gzip required in tootctl emoji help (#11000) 2019-06-08 12:43:11 -04:00
Aditoo17
537e928186 I18n: Update Czech translation 🇨🇿 (#10995)
* I18n: Update Czech translation

* Fix

* Re-fix

* And also this

* The real fix now (hopefully)
2019-06-08 17:58:40 +02:00
ThibG
e9ddd5a159 Put poll options behind content warnings (#10983)
* Put poll options behind CWs in WebUI

* Put polls behind CWs on public pages

* Add poll icon to public pages CWs

* Revert to not showing an icon in the CW button
2019-06-08 17:40:59 +02:00
Eugen Rochko
20dda5cca0 Change full logo to use primary text color of the given theme (#10994)
* Change full logo to use primary text color of the given theme

* Fix colors of public layout header in light theme
2019-06-08 15:30:06 +02:00
Eugen Rochko
f4bc77f290 Improve light theme (#10992) 2019-06-08 10:23:41 +02:00
Eugen Rochko
25f93f4097 Fix RTL layout not being RTL within the columns area (#10990) 2019-06-07 23:35:26 +02:00
ThibG
11c28abcfe Merge pull request #1097 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
2019-06-07 22:15:44 +02:00
Eugen Rochko
aec3fa35fd [Glitch] Fix not being able to directly switch between list timelines in web UI
Port 5bfd802c57 to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-07 17:39:46 +02:00
ThibG
417989ae34 [Glitch] Fix “mark as sensitive” not being used in delete & redraft
Port 2657765d2a to glitch-soc
2019-06-07 17:39:46 +02:00
Jeong Arm
b32a62fe95 [Glitch] Scroll to compose form when focus
Port 8f3c32e29c to glitch-soc

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
2019-06-07 17:39:46 +02:00
Eugen Rochko
cc8f6b3cda Fix some React warnings (#10989) 2019-06-07 17:15:18 +02:00
Thibaut Girka
01aae33a5f [Glitch] Fix refreshing featured toots when the new collection is empty
Port d34a3a2cc7 to glitch-soc
2019-06-07 17:05:32 +02:00
Thibaut Girka
34b8346e7f Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- app/controllers/statuses_controller.rb
- app/controllers/stream_entries_controller.rb
2019-06-07 17:00:36 +02:00
Eugen Rochko
560ec24e58 Change /settings/preferences to redirect to appearance, add /settings/preferences/other (#10988) 2019-06-07 16:51:08 +02:00
Thibaut Girka
8360019896 Fix display of alt-text when a media attachment is not available 2019-06-07 16:39:07 +02:00
ThibG
cdb420862e Fix display of alt-text when a media attachment is not available (#10981) 2019-06-07 12:41:08 +02:00
Alix Rossi
62e6a29f0b i18n: Update Corsican translations (#10987) 2019-06-07 19:12:17 +09:00
Eugen Rochko
1db4117030 Change preferences page into appearance, notifications, and other (#10977) 2019-06-07 03:39:24 +02:00
Eugen Rochko
a60364ca7d Add waiting time to list of pending accounts in admin UI (#10985) 2019-06-07 03:24:10 +02:00
Yamagishi Kazutoshi
c672676c03 Fix sass-lint config (#10982) 2019-06-06 18:51:46 +02:00
ThibG
a03fb7b703 Change e-mail contact for CoC enforcement 2019-06-06 17:42:07 +02:00
Wenceslao Páez Chávez
fe3bf3b0fc Fix overlap of emoji button on search popup (#10978) 2019-06-06 13:40:17 +02:00
Thibaut Girka
a7f6e72b30 Fix CW field automatically getting the focus when it is always enabled 2019-06-06 13:26:42 +02:00
Eugen Rochko
5bfd802c57 Fix not being able to directly switch between list timelines in web UI (#10973) 2019-06-06 13:04:49 +02:00
ThibG
2657765d2a Fix “mark as sensitive” not being used in delete & redraft (#10980) 2019-06-06 13:04:34 +02:00
Yamagishi Kazutoshi
70423ce81f require rubocop-rails in .rubocop.yml (#10974)
* Revert "Revert #10957 (rubocop-rails) which is incompatible with CodeClimate (#10965)"

This reverts commit 121d19d7fa.

* Disable Rails/HelperInstanceVariable
2019-06-06 12:31:48 +02:00
Takeshi Umeda
c402c291f4 Fix emoji picker being always displayed (#10979)
* Fix emoji picker being always displayed

* Remove duplicate content with other pull-requests
2019-06-06 12:30:14 +02:00
Jeong Arm
8f3c32e29c Scroll to compose form when focus (#10970)
* Scroll to compose form when focus

* Get rid of constructor
2019-06-05 15:29:45 +02:00
ThibG
cac9110533 Cleanup various controllers (#10972)
* Remove skip_session! as it is not supported in Rails 5

* Minor cleanup in StreamEntriesController

* Remove redundant mark_cacheable! calls
2019-06-05 14:02:59 +02:00
ThibG
7fa23ec697 Fix potential private status leak (#10969) 2019-06-05 13:40:20 +02:00
ThibG
d34a3a2cc7 Fix refreshing featured toots when the new collection is empty (#10971)
Fixes #10945
2019-06-05 13:39:59 +02:00
ThibG
6c464cd424 Do not misattribute inlined boosts if attributedTo isn't present (#10967)
* Do not misattribute inlined boosts if `attributedTo` isn't present

Fixes #10950

* Fix tests
2019-06-04 23:24:31 +02:00
Eugen Rochko
ed19f33440 Fix margins on profile metadata in single column mode (#10961) 2019-06-04 23:11:57 +02:00
Eugen Rochko
6a9a759f40 Change reblogs counter to be updated when boosted privately (#10964) 2019-06-04 23:11:44 +02:00
Eugen Rochko
f2b743e715 Refactor all ActivityPub deliveries to be serialized and signed through one concern (#10966) 2019-06-04 23:11:18 +02:00
576 changed files with 10574 additions and 6348 deletions

View File

@@ -177,8 +177,7 @@ jobs:
steps:
- *attach_workspace
- run: bundle exec i18n-tasks check-normalized
- run: bundle exec i18n-tasks unused
- run: bundle exec i18n-tasks missing -t plural
- run: bundle exec i18n-tasks unused -l en
- run: bundle exec i18n-tasks check-consistent-interpolations
workflows:

View File

@@ -169,15 +169,12 @@ STREAMING_CLUSTER_NUM=1
# Maximum allowed display name characters
# MAX_DISPLAY_NAME_CHARS=30
# Maximum image and video upload sizes
# Maximum image and video/audio upload sizes
# Units are in bytes
# 1048576 bytes equals 1 megabyte
# MAX_IMAGE_SIZE=8388608
# MAX_VIDEO_SIZE=41943040
# Maximum length of audio uploads in seconds
# MAX_AUDIO_LENGTH=60
# LDAP authentication (optional)
# LDAP_ENABLED=true
# LDAP_HOST=localhost

View File

@@ -1,3 +1,6 @@
require:
- rubocop-rails
AllCops:
TargetRubyVersion: 2.3
Exclude:
@@ -82,6 +85,9 @@ Rails/Exit:
- 'lib/mastodon/*'
- 'lib/cli.rb'
Rails/HelperInstanceVariable:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false

View File

@@ -4,261 +4,34 @@
files:
include: app/javascript/styles/**/*.scss
ignore:
- app/javascript/styles/reset.scss
- app/javascript/styles/mastodon/reset.scss
linters:
# Reports when you use improper spacing around ! (the "bang") in !default,
# !global, !important, and !optional flags.
BangFormat:
enabled: false
rules:
# Disallows
no-color-literals: 0
no-css-comments: 0
no-duplicate-properties: 0
no-ids: 0
no-important: 0
no-mergeable-selectors: 0
no-misspelled-properties: 0
no-qualifying-elements: 0
no-transition-all: 0
no-vendor-prefixes: 0
# Whether or not to prefer `border: 0` over `border: none`.
BorderZero:
enabled: false
# Nesting
force-element-nesting: 0
force-attribute-nesting: 0
force-pseudo-nesting: 0
# Reports when you define a rule set using a selector with chained classes
# (a.k.a. adjoining classes).
ChainedClasses:
enabled: false
# Name Formats
class-name-format: 0
leading-zero: 0
# Prefer hexadecimal color codes over color keywords.
# (e.g. `color: green` is a color keyword)
ColorKeyword:
enabled: false
# Prefer color literals (keywords or hexadecimal codes) to be used only in
# variable declarations. They should be referred to via variables everywhere
# else.
ColorVariable:
enabled: true
# Which form of comments to prefer in CSS.
Comment:
enabled: false
# Reports @debug statements (which you probably left behind accidentally).
DebugStatement:
enabled: false
# Rule sets should be ordered as follows:
# - @extend declarations
# - @include declarations without inner @content
# - properties, @include declarations with inner @content
# - nested rule sets.
DeclarationOrder:
enabled: false
# `scss-lint:disable` control comments should be preceded by a comment
# explaining why these linters are being disabled for this file.
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
# more information.
DisableLinterReason:
enabled: true
# Reports when you define the same property twice in a single rule set.
DuplicateProperty:
enabled: false
# Separate rule, function, and mixin declarations with empty lines.
EmptyLineBetweenBlocks:
enabled: true
# Reports when you have an empty rule set.
EmptyRule:
enabled: true
# Reports when you have an @extend directive.
ExtendDirective:
enabled: false
# Files should always have a final newline. This results in better diffs
# when adding lines to the file, since SCM systems such as git won't
# think that you touched the last line.
FinalNewline:
enabled: false
# HEX colors should use three-character values where possible.
HexLength:
enabled: false
# HEX color values should use lower-case colors to differentiate between
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
HexNotation:
enabled: true
# Avoid using ID selectors.
IdSelector:
enabled: false
# The basenames of @imported SCSS partials should not begin with an
# underscore and should not include the filename extension.
ImportPath:
enabled: false
# Avoid using !important in properties. It is usually indicative of a
# misunderstanding of CSS specificity and can lead to brittle code.
ImportantRule:
enabled: false
# Indentation should always be done in increments of 2 spaces.
Indentation:
enabled: true
width: 2
# Don't write leading zeros for numeric values with a decimal point.
LeadingZero:
enabled: false
# Reports when you define the same selector twice in a single sheet.
MergeableSelector:
enabled: false
# Functions, mixins, variables, and placeholders should be declared
# with all lowercase letters and hyphens instead of underscores.
NameFormat:
enabled: false
# Avoid nesting selectors too deeply.
NestingDepth:
enabled: false
# Always use placeholder selectors in @extend.
PlaceholderInExtend:
enabled: false
# Sort properties in a strict order.
PropertySortOrder:
enabled: false
# Reports when you use an unknown or disabled CSS property
# (ignoring vendor-prefixed properties).
PropertySpelling:
enabled: false
# Configure which units are allowed for property values.
PropertyUnits:
enabled: false
# Pseudo-elements, like ::before, and ::first-letter, should be declared
# with two colons. Pseudo-classes, like :hover and :first-child, should
# be declared with one colon.
PseudoElement:
enabled: true
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
QualifyingElement:
enabled: false
# Don't write selectors with a depth of applicability greater than 3.
SelectorDepth:
enabled: false
# Selectors should always use hyphenated-lowercase, rather than camelCase or
# snake_case.
SelectorFormat:
enabled: false
convention: hyphenated_lowercase
# Prefer the shortest shorthand form possible for properties that support it.
Shorthand:
enabled: true
# Each property should have its own line, except in the special case of
# single line rulesets.
SingleLinePerProperty:
enabled: true
allow_single_line_rule_sets: true
# Split selectors onto separate lines after each comma, and have each
# individual selector occupy a single line.
SingleLinePerSelector:
enabled: true
# Commas in lists should be followed by a space.
SpaceAfterComma:
enabled: false
# Properties should be formatted with a single space separating the colon
# from the property's value.
SpaceAfterPropertyColon:
enabled: true
# Properties should be formatted with no space between the name and the
# colon.
SpaceAfterPropertyName:
enabled: true
# Variables should be formatted with a single space separating the colon
# from the variable's value.
SpaceAfterVariableColon:
enabled: true
# Variables should be formatted with no space between the name and the
# colon.
SpaceAfterVariableName:
enabled: false
# Operators should be formatted with a single space on both sides of an
# infix operator.
SpaceAroundOperator:
enabled: true
# Opening braces should be preceded by a single space.
SpaceBeforeBrace:
enabled: true
# Parentheses should not be padded with spaces.
SpaceBetweenParens:
enabled: false
# Enforces that string literals should be written with a consistent form
# of quotes (single or double).
StringQuotes:
enabled: false
# Property values, @extend, @include, and @import directives, and variable
# declarations should always end with a semicolon.
TrailingSemicolon:
enabled: true
# Reports lines containing trailing whitespace.
TrailingWhitespace:
enabled: true
# Don't write trailing zeros for numeric values with a decimal point.
TrailingZero:
enabled: false
# Don't use the `all` keyword to specify transition properties.
TransitionAll:
enabled: false
# Numeric values should not contain unnecessary fractional portions.
UnnecessaryMantissa:
enabled: false
# Do not use parent selector references (&) when they would otherwise
# be unnecessary.
UnnecessaryParentReference:
enabled: false
# URLs should be valid and not contain protocols or domain names.
UrlFormat:
enabled: true
# URLs should always be enclosed within quotes.
UrlQuotes:
enabled: true
# Properties, like color and font, are easier to read and maintain
# when defined using variables rather than literals.
VariableForProperty:
enabled: false
# Avoid vendor prefixes. Or rather: don't write them yourself.
VendorPrefix:
enabled: false
# Omit length units on zero values, e.g. `0px` vs. `0`.
ZeroUnit:
enabled: true
# Style Guide
attribute-quotes: 0
hex-length: 0
indentation: 0
nesting-depth: 0
property-sort-order: 0
quotes: 0

View File

@@ -3,6 +3,99 @@ Changelog
All notable changes to this project will be documented in this file.
## [2.9.2] - 2019-06-22
### Added
- Add `short_description` and `approval_required` to `GET /api/v1/instance` ([Gargron](https://github.com/tootsuite/mastodon/pull/11146))
### Changed
- Change camera icon to paperclip icon in upload form ([koyuawsmbrtn](https://github.com/tootsuite/mastodon/pull/11149))
### Fixed
- Fix audio-only OGG and WebM files not being processed as such ([Gargron](https://github.com/tootsuite/mastodon/pull/11151))
- Fix audio not being downloaded from remote servers ([Gargron](https://github.com/tootsuite/mastodon/pull/11145))
## [2.9.1] - 2019-06-22
### Added
- Add moderation API ([Gargron](https://github.com/tootsuite/mastodon/pull/9387))
- Add audio uploads ([Gargron](https://github.com/tootsuite/mastodon/pull/11123), [Gargron](https://github.com/tootsuite/mastodon/pull/11141))
### Changed
- Change domain blocks to automatically support subdomains ([Gargron](https://github.com/tootsuite/mastodon/pull/11138))
- Change Nanobox configuration to bring it up to date ([danhunsaker](https://github.com/tootsuite/mastodon/pull/11083))
### Removed
- Remove expensive counters from federation page in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11139))
### Fixed
- Fix converted media being saved with original extension and mime type ([Gargron](https://github.com/tootsuite/mastodon/pull/11130))
- Fix layout of identity proofs settings ([acid-chicken](https://github.com/tootsuite/mastodon/pull/11126))
- Fix active scope only returning suspended users ([ThibG](https://github.com/tootsuite/mastodon/pull/11111))
- Fix sanitizer making block level elements unreadable ([Gargron](https://github.com/tootsuite/mastodon/pull/10836))
- Fix label for site theme not being translated in admin UI ([palindromordnilap](https://github.com/tootsuite/mastodon/pull/11121))
- Fix statuses not being filtered irreversibly in web UI under some circumstances ([ThibG](https://github.com/tootsuite/mastodon/pull/11113))
- Fix scrolling behaviour in compose form ([ThibG](https://github.com/tootsuite/mastodon/pull/11093))
## [2.9.0] - 2019-06-13
### Added
- **Add single-column mode in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/10807), [Gargron](https://github.com/tootsuite/mastodon/pull/10848), [Gargron](https://github.com/tootsuite/mastodon/pull/11003), [Gargron](https://github.com/tootsuite/mastodon/pull/10961), [Hanage999](https://github.com/tootsuite/mastodon/pull/10915), [noellabo](https://github.com/tootsuite/mastodon/pull/10917), [abcang](https://github.com/tootsuite/mastodon/pull/10859), [Gargron](https://github.com/tootsuite/mastodon/pull/10820), [Gargron](https://github.com/tootsuite/mastodon/pull/10835), [Gargron](https://github.com/tootsuite/mastodon/pull/10809), [Gargron](https://github.com/tootsuite/mastodon/pull/10963), [noellabo](https://github.com/tootsuite/mastodon/pull/10883), [Hanage999](https://github.com/tootsuite/mastodon/pull/10839))
- Add waiting time to the list of pending accounts in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10985))
- Add a keyboard shortcut to hide/show media in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10647), [Gargron](https://github.com/tootsuite/mastodon/pull/10838), [ThibG](https://github.com/tootsuite/mastodon/pull/10872))
- Add `account_id` param to `GET /api/v1/notifications` ([pwoolcoc](https://github.com/tootsuite/mastodon/pull/10796))
- Add confirmation modal for unboosting toots in web UI ([aurelien-reeves](https://github.com/tootsuite/mastodon/pull/10287))
- Add emoji suggestions to content warning and poll option fields in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10555))
- Add `source` attribute to response of `DELETE /api/v1/statuses/:id` ([ThibG](https://github.com/tootsuite/mastodon/pull/10669))
- Add some caching for HTML versions of public status pages ([ThibG](https://github.com/tootsuite/mastodon/pull/10701))
- Add button to conveniently copy OAuth code ([ThibG](https://github.com/tootsuite/mastodon/pull/11065))
### Changed
- **Change default layout to single column in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/10847))
- **Change light theme** ([Gargron](https://github.com/tootsuite/mastodon/pull/10992), [Gargron](https://github.com/tootsuite/mastodon/pull/10996), [yuzulabo](https://github.com/tootsuite/mastodon/pull/10754), [Gargron](https://github.com/tootsuite/mastodon/pull/10845))
- **Change preferences page into appearance, notifications, and other** ([Gargron](https://github.com/tootsuite/mastodon/pull/10977), [Gargron](https://github.com/tootsuite/mastodon/pull/10988))
- Change priority of delete activity forwards for replies and reblogs ([Gargron](https://github.com/tootsuite/mastodon/pull/11002))
- Change Mastodon logo to use primary text color of the given theme ([Gargron](https://github.com/tootsuite/mastodon/pull/10994))
- Change reblogs counter to be updated when boosted privately ([Gargron](https://github.com/tootsuite/mastodon/pull/10964))
- Change bio limit from 160 to 500 characters ([trwnh](https://github.com/tootsuite/mastodon/pull/10790))
- Change API rate limiting to reduce allowed unauthenticated requests ([ThibG](https://github.com/tootsuite/mastodon/pull/10860), [hinaloe](https://github.com/tootsuite/mastodon/pull/10868), [mayaeh](https://github.com/tootsuite/mastodon/pull/10867))
- Change help text of `tootctl emoji import` command to specify a gzipped TAR archive is required ([dariusk](https://github.com/tootsuite/mastodon/pull/11000))
- Change web UI to hide poll options behind content warnings ([ThibG](https://github.com/tootsuite/mastodon/pull/10983))
- Change silencing to ensure local effects and remote effects are the same for silenced local users ([ThibG](https://github.com/tootsuite/mastodon/pull/10575))
- Change `tootctl domains purge` to remove custom emoji as well ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10721))
- Change Docker image to keep `apt` working ([SuperSandro2000](https://github.com/tootsuite/mastodon/pull/10830))
### Removed
- Remove `dist-upgrade` from Docker image ([SuperSandro2000](https://github.com/tootsuite/mastodon/pull/10822))
### Fixed
- Fix RTL layout not being RTL within the columns area in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10990))
- Fix display of alternative text when a media attachment is not available in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10981))
- Fix not being able to directly switch between list timelines in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/10973))
- Fix media sensitivity not being maintained in delete & redraft in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10980))
- Fix emoji picker being always displayed in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/10979), [yuzulabo](https://github.com/tootsuite/mastodon/pull/10801), [wcpaez](https://github.com/tootsuite/mastodon/pull/10978))
- Fix potential private status leak through caching ([ThibG](https://github.com/tootsuite/mastodon/pull/10969))
- Fix refreshing featured toots when the new collection is empty in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10971))
- Fix undoing domain block also undoing individual moderation on users from before the domain block ([ThibG](https://github.com/tootsuite/mastodon/pull/10660))
- Fix time not being local in the audit log ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10751))
- Fix statuses removed by moderation re-appearing on subsequent fetches ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10732))
- Fix misattribution of inlined announces if `attributedTo` isn't present in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/10967))
- Fix `GET /api/v1/polls/:id` not requiring authentication for non-public polls ([Gargron](https://github.com/tootsuite/mastodon/pull/10960))
- Fix handling of blank poll options in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/10946))
- Fix avatar preview aspect ratio on edit profile page ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10931))
- Fix web push notifications not being sent for polls ([ThibG](https://github.com/tootsuite/mastodon/pull/10864))
- Fix cut off letters in last paragraph of statuses in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/10821))
- Fix list not being automatically unpinned when it returns 404 in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/11045))
- Fix login sometimes redirecting to paths that are not pages ([Gargron](https://github.com/tootsuite/mastodon/pull/11019))
## [2.8.4] - 2019-05-24
### Fixed

View File

@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at beatrix.bitrot@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at glitch-abuse@sitedethib.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@@ -52,7 +52,9 @@ Bug reports and feature suggestions can be submitted to [GitHub Issues](https://
## Translations
You can submit translations via pull request.
You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase.
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)][crowdin]
## Pull requests

View File

@@ -15,7 +15,7 @@ gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.2'
gem 'dotenv-rails', '~> 2.7'
gem 'aws-sdk-s3', '~> 1.41', require: false
gem 'aws-sdk-s3', '~> 1.43', require: false
gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'paperclip', '~> 6.0'
@@ -63,7 +63,7 @@ gem 'nokogiri', '~> 1.10'
gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.7'
gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.10'
gem 'ox', '~> 2.11'
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
gem 'pundit', '~> 2.0'
gem 'premailer-rails'
@@ -111,14 +111,14 @@ group :production, :test do
end
group :test do
gem 'capybara', '~> 3.22'
gem 'capybara', '~> 3.24'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 1.9'
gem 'microformats', '~> 4.1'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.0'
gem 'simplecov', '~> 0.16', require: false
gem 'webmock', '~> 3.5'
gem 'webmock', '~> 3.6'
gem 'parallel_tests', '~> 2.29'
end
@@ -132,6 +132,7 @@ group :development do
gem 'letter_opener_web', '~> 1.3'
gem 'memory_profiler'
gem 'rubocop', '~> 0.71', require: false
gem 'rubocop-rails', '~> 2.0', require: false
gem 'brakeman', '~> 4.5', require: false
gem 'bundler-audit', '~> 0.6', require: false

View File

@@ -76,17 +76,17 @@ GEM
av (0.9.0)
cocaine (~> 0.5.3)
aws-eventstream (1.0.3)
aws-partitions (1.169.0)
aws-sdk-core (3.54.0)
aws-partitions (1.177.0)
aws-sdk-core (3.56.0)
aws-eventstream (~> 1.0, >= 1.0.2)
aws-partitions (~> 1.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.21.0)
aws-sdk-core (~> 3, >= 3.53.0)
aws-sdk-kms (1.22.0)
aws-sdk-core (~> 3, >= 3.56.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.41.0)
aws-sdk-core (~> 3, >= 3.53.0)
aws-sdk-s3 (1.43.0)
aws-sdk-core (~> 3, >= 3.56.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.1.0)
@@ -129,7 +129,7 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
capybara (3.22.0)
capybara (3.24.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
@@ -159,7 +159,7 @@ GEM
css_parser (1.6.0)
addressable
debug_inspector (0.0.3)
derailed_benchmarks (1.3.5)
derailed_benchmarks (1.3.6)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
@@ -188,9 +188,9 @@ GEM
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.1.0)
railties (>= 5)
dotenv (2.7.2)
dotenv-rails (2.7.2)
dotenv (= 2.7.2)
dotenv (2.7.4)
dotenv-rails (2.7.4)
dotenv (= 2.7.4)
railties (>= 3.2, < 6.1)
elasticsearch (6.0.2)
elasticsearch-api (= 6.0.2)
@@ -253,7 +253,7 @@ GEM
railties (>= 4.0.1)
hamster (3.0.0)
concurrent-ruby (~> 1.0)
hashdiff (0.3.7)
hashdiff (0.4.0)
hashie (3.6.0)
heapy (0.1.4)
highline (2.0.1)
@@ -271,7 +271,7 @@ GEM
domain_name (~> 0.5)
http-form_data (2.1.1)
http_accept_language (2.1.1)
httplog (1.3.0)
httplog (1.3.1)
rack (>= 1.0)
rainbow (>= 2.0.0)
i18n (1.6.0)
@@ -322,7 +322,7 @@ GEM
letter_opener (~> 1.0)
railties (>= 3.2)
link_header (0.0.8)
lograge (0.11.1)
lograge (0.11.2)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
@@ -384,7 +384,7 @@ GEM
addressable (~> 2.5)
http (~> 3.0)
nokogiri (~> 1.8)
ox (2.10.1)
ox (2.11.0)
paperclip (6.0.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
@@ -395,7 +395,7 @@ GEM
av (~> 0.9.0)
paperclip (>= 2.5.2)
parallel (1.17.0)
parallel_tests (2.29.0)
parallel_tests (2.29.1)
parallel
parser (2.6.3.0)
ast (~> 2.4.0)
@@ -403,7 +403,7 @@ GEM
equatable (~> 0.5.0)
tty-color (~> 0.4.0)
pg (1.1.4)
pghero (2.2.0)
pghero (2.2.1)
activerecord
pkg-config (1.3.7)
premailer (1.11.1)
@@ -534,12 +534,15 @@ GEM
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.0.1)
rack (>= 1.1)
rubocop (>= 0.70.0)
ruby-progressbar (1.10.1)
ruby-saml (1.9.0)
nokogiri (>= 1.5.10)
rufus-scheduler (3.5.2)
fugit (~> 1.1, >= 1.1.5)
safe_yaml (1.0.4)
safe_yaml (1.0.5)
sanitize (5.0.0)
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
@@ -621,10 +624,10 @@ GEM
uniform_notifier (1.12.1)
warden (1.2.8)
rack (>= 2.0.6)
webmock (3.5.1)
webmock (3.6.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (4.0.7)
activesupport (>= 4.2)
rack-proxy (>= 0.6.1)
@@ -647,7 +650,7 @@ DEPENDENCIES
active_record_query_trace (~> 1.6)
addressable (~> 2.6)
annotate (~> 2.7)
aws-sdk-s3 (~> 1.41)
aws-sdk-s3 (~> 1.43)
better_errors (~> 2.5)
binding_of_caller (~> 0.7)
blurhash (~> 0.1)
@@ -660,7 +663,7 @@ DEPENDENCIES
capistrano-rails (~> 1.4)
capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0)
capybara (~> 3.22)
capybara (~> 3.24)
charlock_holmes (~> 0.7.6)
chewy (~> 5.0)
cld3 (~> 3.2.4)
@@ -711,7 +714,7 @@ DEPENDENCIES
omniauth-cas (~> 1.1)
omniauth-saml (~> 1.10)
ostatus2 (~> 2.0)
ox (~> 2.10)
ox (~> 2.11)
paperclip (~> 6.0)
paperclip-av-transcoder (~> 0.6)
parallel_tests (~> 2.29)
@@ -740,6 +743,7 @@ DEPENDENCIES
rspec-rails (~> 3.8)
rspec-sidekiq (~> 3.0)
rubocop (~> 0.71)
rubocop-rails (~> 2.0)
sanitize (~> 5.0)
sidekiq (~> 5.2)
sidekiq-bulk (~> 0.2.0)
@@ -758,7 +762,7 @@ DEPENDENCIES
tty-prompt (~> 0.19)
twitter-text (~> 1.14)
tzinfo-data (~> 1.2019)
webmock (~> 3.5)
webmock (~> 3.6)
webpacker (~> 4.0)
webpush

View File

@@ -51,7 +51,7 @@ class StatusesIndex < Chewy::Index
field :id, type: 'long'
field :account_id, type: 'long'
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).join("\n\n") } do
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
field :stemmed, type: 'text', analyzer: 'content'
end

View File

@@ -47,8 +47,6 @@ class AccountsController < ApplicationController
end
format.json do
mark_cacheable!
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
end

View File

@@ -9,8 +9,6 @@ class ActivityPub::CollectionsController < Api::BaseController
before_action :set_cache_headers
def show
skip_session!
render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(
collection_presenter,

View File

@@ -10,10 +10,7 @@ class ActivityPub::OutboxesController < Api::BaseController
before_action :set_cache_headers
def show
unless page_requested?
skip_session!
expires_in 1.minute, public: true
end
expires_in 1.minute, public: true unless page_requested?
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end

View File

@@ -48,13 +48,13 @@ module Admin
def approve
authorize @account.user, :approve?
@account.user.approve!
redirect_to admin_accounts_path(pending: '1')
redirect_to admin_pending_accounts_path
end
def reject
authorize @account.user, :reject?
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
redirect_to admin_accounts_path(pending: '1')
redirect_to admin_pending_accounts_path
end
def unsilence
@@ -127,6 +127,7 @@ module Admin
:by_domain,
:active,
:pending,
:disabled,
:silenced,
:suspended,
:username,

View File

@@ -13,7 +13,7 @@ module Admin
authorize :domain_block, :create?
@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.find_by(domain: resource_params[:domain]) : nil
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
@domain_block.save

View File

@@ -18,7 +18,7 @@ module Admin
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
@domain_block = DomainBlock.find_by(domain: params[:id])
@domain_block = DomainBlock.rule_for(params[:id])
end
private

View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true
class Api::V1::Admin::AccountActionsController < Api::BaseController
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }
before_action :require_staff!
before_action :set_account
def create
account_action = Admin::AccountAction.new(resource_params)
account_action.target_account = @account
account_action.current_account = current_account
account_action.save!
render_empty
end
private
def set_account
@account = Account.find(params[:account_id])
end
def resource_params
params.permit(
:type,
:report_id,
:warning_preset_id,
:text,
:send_email_notification
)
end
end

View File

@@ -0,0 +1,128 @@
# frozen_string_literal: true
class Api::V1::Admin::AccountsController < Api::BaseController
include Authorization
include AccountableConcern
LIMIT = 100
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
before_action :require_staff!
before_action :set_accounts, only: :index
before_action :set_account, except: :index
before_action :require_local_account!, only: [:enable, :approve, :reject]
after_action :insert_pagination_headers, only: :index
FILTER_PARAMS = %i(
local
remote
by_domain
active
pending
disabled
silenced
suspended
username
display_name
email
ip
staff
).freeze
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
def index
authorize :account, :index?
render json: @accounts, each_serializer: REST::Admin::AccountSerializer
end
def show
authorize @account, :show?
render json: @account, serializer: REST::Admin::AccountSerializer
end
def enable
authorize @account.user, :enable?
@account.user.enable!
log_action :enable, @account.user
render json: @account, serializer: REST::Admin::AccountSerializer
end
def approve
authorize @account.user, :approve?
@account.user.approve!
render json: @account, serializer: REST::Admin::AccountSerializer
end
def reject
authorize @account.user, :reject?
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsilence
authorize @account, :unsilence?
@account.unsilence!
log_action :unsilence, @account
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsuspend
authorize @account, :unsuspend?
@account.unsuspend!
log_action :unsuspend, @account
render json: @account, serializer: REST::Admin::AccountSerializer
end
private
def set_accounts
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def set_account
@account = Account.find(params[:id])
end
def filtered_accounts
AccountFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_accounts_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_accounts_url(pagination_params(min_id: pagination_since_id)) unless @accounts.empty?
end
def pagination_max_id
@accounts.last.id
end
def pagination_since_id
@accounts.first.id
end
def records_continue?
@accounts.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
def require_local_account!
forbidden unless @account.local? && @account.user.present?
end
end

View File

@@ -0,0 +1,108 @@
# frozen_string_literal: true
class Api::V1::Admin::ReportsController < Api::BaseController
include Authorization
include AccountableConcern
LIMIT = 100
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
before_action :require_staff!
before_action :set_reports, only: :index
before_action :set_report, except: :index
after_action :insert_pagination_headers, only: :index
FILTER_PARAMS = %i(
resolved
account_id
target_account_id
).freeze
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
def index
authorize :report, :index?
render json: @reports, each_serializer: REST::Admin::ReportSerializer
end
def show
authorize @report, :show?
render json: @report, serializer: REST::Admin::ReportSerializer
end
def assign_to_self
authorize @report, :update?
@report.update!(assigned_account_id: current_account.id)
log_action :assigned_to_self, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def unassign
authorize @report, :update?
@report.update!(assigned_account_id: nil)
log_action :unassigned, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def reopen
authorize @report, :update?
@report.unresolve!
log_action :reopen, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def resolve
authorize @report, :update?
@report.resolve!(current_account)
log_action :resolve, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
private
def set_reports
@reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def set_report
@report = Report.find(params[:id])
end
def filtered_reports
ReportFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_reports_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_reports_url(pagination_params(min_id: pagination_since_id)) unless @reports.empty?
end
def pagination_max_id
@reports.last.id
end
def pagination_since_id
@reports.first.id
end
def records_continue?
@reports.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
end

View File

@@ -7,7 +7,7 @@ class Api::V1::CustomEmojisController < Api::BaseController
def index
render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false), each_serializer: REST::CustomEmojiSerializer)
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false).includes(:category), each_serializer: REST::CustomEmojiSerializer)
end
end
end

View File

@@ -27,16 +27,18 @@ class Api::V1::Timelines::DirectController < Api::BaseController
end
def direct_timeline_statuses
# this query requires built in pagination.
Status.as_direct_timeline(
current_account,
account_direct_feed.get(
limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_id],
true # returns array of cache_ids object
params[:min_id]
)
end
def account_direct_feed
DirectFeed.new(current_account)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end

View File

@@ -161,11 +161,15 @@ class ApplicationController < ActionController::Base
end
def current_account
@current_account ||= current_user.try(:account)
return @current_account if defined?(@current_account)
@current_account = current_user&.account
end
def current_session
@current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id'])
return @current_session if defined?(@current_session)
@current_session = SessionActivation.find_by(session_id: cookies.signed['_session_id']) if cookies.signed['_session_id'].present?
end
def current_flavour
@@ -228,11 +232,6 @@ class ApplicationController < ActionController::Base
end
def mark_cacheable!
skip_session!
expires_in 0, public: true
end
def skip_session!
request.session_options[:skip] = true
end
end

View File

@@ -70,7 +70,6 @@ module AccountControllerConcern
def check_account_suspension
if @account.suspended?
skip_session!
expires_in(3.minutes, public: true)
gone
end

View File

@@ -1,10 +1,11 @@
# frozen_string_literal: true
class CustomCssController < ApplicationController
skip_before_action :store_current_location
before_action :set_cache_headers
def show
skip_session!
render plain: Setting.custom_css || '', content_type: 'text/css'
end
end

View File

@@ -7,8 +7,6 @@ class EmojisController < ApplicationController
def show
respond_to do |format|
format.json do
skip_session!
render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
end

View File

@@ -20,10 +20,7 @@ class FollowerAccountsController < ApplicationController
format.json do
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
if params[:page].blank?
skip_session!
expires_in 3.minutes, public: true
end
expires_in 3.minutes, public: true if params[:page].blank?
render json: collection_presenter,
serializer: ActivityPub::CollectionSerializer,

View File

@@ -20,10 +20,7 @@ class FollowingAccountsController < ApplicationController
format.json do
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
if params[:page].blank?
skip_session!
expires_in 3.minutes, public: true
end
expires_in 3.minutes, public: true if params[:page].blank?
render json: collection_presenter,
serializer: ActivityPub::CollectionSerializer,

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ManifestsController < ApplicationController
skip_before_action :store_current_location
def show
render json: InstancePresenter.new, serializer: ManifestSerializer
end

View File

@@ -3,8 +3,12 @@
class MediaController < ApplicationController
include Authorization
skip_before_action :store_current_location
before_action :set_media_attachment
before_action :verify_permitted_status!
before_action :check_playable, only: :player
before_action :allow_iframing, only: :player
content_security_policy only: :player do |p|
p.frame_ancestors(false)
@@ -16,8 +20,6 @@ class MediaController < ApplicationController
def player
@body_classes = 'player'
response.headers['X-Frame-Options'] = 'ALLOWALL'
raise ActiveRecord::RecordNotFound unless @media_attachment.video? || @media_attachment.gifv?
end
private
@@ -32,4 +34,12 @@ class MediaController < ApplicationController
# Reraise in order to get a 404 instead of a 403 error code
raise ActiveRecord::RecordNotFound
end
def check_playable
not_found unless @media_attachment.larger_media_format?
end
def allow_iframing
response.headers['X-Frame-Options'] = 'ALLOWALL'
end
end

View File

@@ -3,6 +3,8 @@
class MediaProxyController < ApplicationController
include RoutingHelper
skip_before_action :store_current_location
def show
RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
@@ -37,6 +39,6 @@ class MediaProxyController < ApplicationController
end
def reject_media?
DomainBlock.find_by(domain: @media_attachment.account.domain)&.reject_media?
DomainBlock.reject_media?(@media_attachment.account.domain)
end
end

View File

@@ -61,8 +61,4 @@ class Settings::IdentityProofsController < Settings::BaseController
def post_params
params.require(:account_identity_proof).permit(:post_status, :status_text)
end
def set_body_classes
@body_classes = ''
end
end

View File

@@ -1,28 +0,0 @@
# frozen_string_literal: true
class Settings::NotificationsController < Settings::BaseController
def show; end
def update
user_settings.update(user_settings_params.to_h)
if current_user.save
redirect_to settings_notifications_path, notice: I18n.t('generic.changes_saved_msg')
else
render :show
end
end
private
def user_settings
UserSettingsDecorator.new(current_user)
end
def user_settings_params
params.require(:user).permit(
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
class Settings::Preferences::AppearanceController < Settings::PreferencesController
private
def after_update_redirect_path
settings_preferences_appearance_path
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
class Settings::Preferences::NotificationsController < Settings::PreferencesController
private
def after_update_redirect_path
settings_preferences_notifications_path
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
class Settings::Preferences::OtherController < Settings::PreferencesController
private
def after_update_redirect_path
settings_preferences_other_path
end
end

View File

@@ -8,7 +8,7 @@ class Settings::PreferencesController < Settings::BaseController
if current_user.update(user_params)
I18n.locale = current_user.locale
redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg')
redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
else
render :show
end
@@ -16,6 +16,10 @@ class Settings::PreferencesController < Settings::BaseController
private
def after_update_redirect_path
settings_preferences_path
end
def user_settings
UserSettingsDecorator.new(current_user)
end
@@ -48,8 +52,9 @@ class Settings::PreferencesController < Settings::BaseController
:setting_show_application,
:setting_advanced_layout,
:setting_default_content_type,
:setting_use_blurhash,
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account),
interactions: %i(must_be_follower must_be_following)
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)
end
end

View File

@@ -29,10 +29,7 @@ class StatusesController < ApplicationController
format.html do
use_pack 'public'
unless user_signed_in?
skip_session!
expires_in 10.seconds, public: true
end
expires_in 10.seconds, public: true if current_account.nil?
@body_classes = 'with-modals'
@@ -43,8 +40,6 @@ class StatusesController < ApplicationController
end
format.json do
mark_cacheable! unless @stream_entry.hidden?
render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
end
@@ -53,8 +48,6 @@ class StatusesController < ApplicationController
end
def activity
skip_session!
render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
end
@@ -64,7 +57,6 @@ class StatusesController < ApplicationController
use_pack 'embed'
raise ActiveRecord::RecordNotFound if @status.hidden?
skip_session!
expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL'
@autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
@@ -73,8 +65,6 @@ class StatusesController < ApplicationController
end
def replies
skip_session!
render json: replies_collection_presenter,
serializer: ActivityPub::CollectionSerializer,
adapter: ActivityPub::Adapter,

View File

@@ -17,19 +17,13 @@ class StreamEntriesController < ApplicationController
format.html do
use_pack 'public'
unless user_signed_in?
skip_session!
expires_in 5.minutes, public: true
end
expires_in 5.minutes, public: true unless @stream_entry.hidden?
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status'
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity)
end
format.atom do
unless @stream_entry.hidden?
skip_session!
expires_in 3.minutes, public: true
end
expires_in 3.minutes, public: true unless @stream_entry.hidden?
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true))
end
@@ -57,7 +51,7 @@ class StreamEntriesController < ApplicationController
def set_stream_entry
@stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id])
@type = @stream_entry.activity_type.downcase
@type = 'status'
raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil?
authorize @stream_entry.activity, :show? if @stream_entry.hidden? || @stream_entry.local_only?

View File

@@ -38,6 +38,10 @@ module StreamEntriesHelper
content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976')
end
def svg_logo_full
content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo-full'), 'viewBox' => '0 0 713.35878 175.8678')
end
def account_badge(account, all: false)
if account.bot?
content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')

View File

@@ -14,15 +14,15 @@ delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
return false;
});
delegate(document, '.status__content__spoiler-link', 'click', ({ target }) => {
const contentEl = target.parentNode.parentNode.querySelector('.e-content');
delegate(document, '.status__content__spoiler-link', 'click', function() {
const contentEl = this.parentNode.parentNode.querySelector('.e-content');
if (contentEl.style.display === 'block') {
contentEl.style.display = 'none';
target.parentNode.style.marginBottom = 0;
this.parentNode.style.marginBottom = 0;
} else {
contentEl.style.display = 'block';
target.parentNode.style.marginBottom = null;
this.parentNode.style.marginBottom = null;
}
return false;

View File

@@ -3,7 +3,7 @@
pack:
about:
admin: admin.js
auth:
auth: settings.js
common:
filename: common.js
stylesheet: true

View File

@@ -8,6 +8,7 @@ const messages = defineMessages({
export const ALERT_SHOW = 'ALERT_SHOW';
export const ALERT_DISMISS = 'ALERT_DISMISS';
export const ALERT_CLEAR = 'ALERT_CLEAR';
export const ALERT_NOOP = 'ALERT_NOOP';
export function dismissAlert(alert) {
return {
@@ -36,7 +37,7 @@ export function showAlertForError(error) {
if (status === 404 || status === 410) {
// Skip these errors as they are reflected in the UI
return {};
return { type: ALERT_NOOP };
}
let message = statusText;

View File

@@ -68,6 +68,14 @@ const messages = defineMessages({
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
});
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
export const ensureComposeIsVisible = (getState, routerHistory) => {
if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
routerHistory.push('/statuses/new');
}
};
export function changeCompose(text) {
return {
type: COMPOSE_CHANGE,
@@ -81,16 +89,14 @@ export function cycleElefriendCompose() {
};
};
export function replyCompose(status, router) {
export function replyCompose(status, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_REPLY,
status: status,
});
if (router && !getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
}
ensureComposeIsVisible(getState, routerHistory);
};
};
@@ -106,29 +112,25 @@ export function resetCompose() {
};
};
export function mentionCompose(account, router) {
export function mentionCompose(account, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_MENTION,
account: account,
});
if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
}
ensureComposeIsVisible(getState, routerHistory);
};
};
export function directCompose(account, router) {
export function directCompose(account, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_DIRECT,
account: account,
});
if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
}
ensureComposeIsVisible(getState, routerHistory);
};
};

View File

@@ -0,0 +1,84 @@
import api, { getLinks } from 'flavours/glitch/util/api';
import {
importFetchedAccounts,
importFetchedStatuses,
importFetchedStatus,
} from './importer';
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
export const mountConversations = () => ({
type: CONVERSATIONS_MOUNT,
});
export const unmountConversations = () => ({
type: CONVERSATIONS_UNMOUNT,
});
export const markConversationRead = conversationId => (dispatch, getState) => {
dispatch({
type: CONVERSATIONS_READ,
id: conversationId,
});
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
};
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
dispatch(expandConversationsRequest());
const params = { max_id: maxId };
if (!maxId) {
params.since_id = getState().getIn(['conversations', 'items', 0, 'last_status']);
}
const isLoadingRecent = !!params.since_id;
api(getState).get('/api/v1/conversations', { params })
.then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.reduce((aggr, item) => aggr.concat(item.accounts), [])));
dispatch(importFetchedStatuses(response.data.map(item => item.last_status).filter(x => !!x)));
dispatch(expandConversationsSuccess(response.data, next ? next.uri : null, isLoadingRecent));
})
.catch(err => dispatch(expandConversationsFail(err)));
};
export const expandConversationsRequest = () => ({
type: CONVERSATIONS_FETCH_REQUEST,
});
export const expandConversationsSuccess = (conversations, next, isLoadingRecent) => ({
type: CONVERSATIONS_FETCH_SUCCESS,
conversations,
next,
isLoadingRecent,
});
export const expandConversationsFail = error => ({
type: CONVERSATIONS_FETCH_FAIL,
error,
});
export const updateConversations = conversation => dispatch => {
dispatch(importFetchedAccounts(conversation.accounts));
if (conversation.last_status) {
dispatch(importFetchedStatus(conversation.last_status));
}
dispatch({
type: CONVERSATIONS_UPDATE,
conversation,
});
};

View File

@@ -55,7 +55,7 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
} else {
const spoilerText = normalStatus.spoiler_text || '';
const searchContent = [spoilerText, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
const emojiMap = makeEmojiMap(normalStatus);
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;

View File

@@ -62,9 +62,14 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
let filtered = false;
if (notification.type === 'mention') {
const dropRegex = regexFromFilters(filters.filter(filter => filter.get('irreversible')));
const regex = regexFromFilters(filters);
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
if (dropRegex && dropRegex.test(searchIndex)) {
return;
}
filtered = regex && regex.test(searchIndex);
}

View File

@@ -48,7 +48,7 @@ export function submitSearch() {
dispatch(importFetchedStatuses(response.data.statuses));
}
dispatch(fetchSearchSuccess(response.data));
dispatch(fetchSearchSuccess(response.data, value));
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}).catch(error => {
dispatch(fetchSearchFail(error));
@@ -62,12 +62,11 @@ export function fetchSearchRequest() {
};
};
export function fetchSearchSuccess(results) {
export function fetchSearchSuccess(results, searchTerm) {
return {
type: SEARCH_FETCH_SUCCESS,
results,
accounts: results.accounts,
statuses: results.statuses,
searchTerm,
};
};

View File

@@ -2,6 +2,7 @@ import api from 'flavours/glitch/util/api';
import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses } from './importer';
import { ensureComposeIsVisible } from './compose';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
@@ -80,7 +81,7 @@ export function redraft(status, raw_text, content_type) {
};
};
export function deleteStatus(id, router, withRedraft = false) {
export function deleteStatus(id, routerHistory, withRedraft = false) {
return (dispatch, getState) => {
let status = getState().getIn(['statuses', id]);
@@ -97,9 +98,7 @@ export function deleteStatus(id, router, withRedraft = false) {
if (withRedraft) {
dispatch(redraft(status, response.data.text, response.data.content_type));
if (!getState().getIn(['compose', 'mounted'])) {
router.push('/statuses/new');
}
ensureComposeIsVisible(getState, routerHistory);
}
}).catch(error => {
dispatch(deleteStatusFail(id, error));

View File

@@ -7,6 +7,7 @@ import {
disconnectTimeline,
} from './timelines';
import { updateNotifications, expandNotifications } from './notifications';
import { updateConversations } from './conversations';
import { fetchFilters } from './filters';
import { getLocale } from 'mastodon/locales';
@@ -37,6 +38,9 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
case 'notification':
dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
break;
case 'conversation':
dispatch(updateConversations(JSON.parse(data.payload)));
break;
case 'filters_changed':
dispatch(fetchFilters());
break;

View File

@@ -138,8 +138,11 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
this.setState({ suggestionsHidden: true, focused: false });
}
onFocus = () => {
onFocus = (e) => {
this.setState({ focused: true });
if (this.props.onFocus) {
this.props.onFocus(e);
}
}
onSuggestionClick = (e) => {
@@ -189,7 +192,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
}
render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props;
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
const { suggestionsHidden } = this.state;
const style = { direction: 'ltr' };
@@ -197,34 +200,39 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
style.direction = 'rtl';
}
return (
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
return [
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
<Textarea
inputRef={this.setTextarea}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
onPaste={this.onPaste}
style={style}
aria-autocomplete='list'
/>
</label>
<Textarea
inputRef={this.setTextarea}
className='autosuggest-textarea__textarea'
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
onPaste={this.onPaste}
style={style}
aria-autocomplete='list'
/>
</label>
</div>
{children}
</div>,
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
</div>
</div>
);
</div>,
];
}
}

View File

@@ -0,0 +1,104 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
export default class AvatarComposite extends React.PureComponent {
static propTypes = {
accounts: ImmutablePropTypes.list.isRequired,
animate: PropTypes.bool,
size: PropTypes.number.isRequired,
};
static defaultProps = {
animate: autoPlayGif,
};
renderItem (account, size, index) {
const { animate } = this.props;
let width = 50;
let height = 100;
let top = 'auto';
let left = 'auto';
let bottom = 'auto';
let right = 'auto';
if (size === 1) {
width = 100;
}
if (size === 4 || (size === 3 && index > 0)) {
height = 50;
}
if (size === 2) {
if (index === 0) {
right = '2px';
} else {
left = '2px';
}
} else if (size === 3) {
if (index === 0) {
right = '2px';
} else if (index > 0) {
left = '2px';
}
if (index === 1) {
bottom = '2px';
} else if (index > 1) {
top = '2px';
}
} else if (size === 4) {
if (index === 0 || index === 2) {
right = '2px';
}
if (index === 1 || index === 3) {
left = '2px';
}
if (index < 2) {
bottom = '2px';
} else {
top = '2px';
}
}
const style = {
left: left,
top: top,
right: right,
bottom: bottom,
width: `${width}%`,
height: `${height}%`,
backgroundSize: 'cover',
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
};
return (
<a
href={account.get('url')}
target='_blank'
onClick={(e) => this.props.onAccountClick(account.get('id'), e)}
title={`@${account.get('acct')}`}
key={account.get('id')}
>
<div style={style} data-avatar-of={`@${account.get('acct')}`} />
</a>
);
}
render() {
const { accounts, size } = this.props;
return (
<div className='account__avatar-composite' style={{ width: `${size}px`, height: `${size}px` }}>
{accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))}
</div>
);
}
}

View File

@@ -10,24 +10,56 @@ export default function DisplayName ({
className,
inline,
localDomain,
others,
onAccountClick,
}) {
const computedClass = classNames('display-name', { inline }, className);
if (!account) return null;
let displayName, suffix;
let acct = account.get('acct');
if (acct.indexOf('@') === -1 && localDomain) {
acct = `${acct}@${localDomain}`;
}
// The result.
return account ? (
if (others && others.size > 0) {
displayName = others.take(2).map(a => (
<a
href={a.get('url')}
target='_blank'
onClick={(e) => onAccountClick(a.get('id'), e)}
title={`@${a.get('acct')}`}
>
<bdi key={a.get('id')}>
<strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} />
</bdi>
</a>
)).reduce((prev, cur) => [prev, ', ', cur]);
if (others.size - 2 > 0) {
displayName.push(` +${others.size - 2}`);
}
suffix = (
<a href={account.get('url')} target='_blank' onClick={(e) => onAccountClick(account.get('id'), e)}>
<span className='display-name__account'>@{acct}</span>
</a>
);
} else {
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>;
suffix = <span className='display-name__account'>@{acct}</span>;
}
return (
<span className={computedClass}>
<bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>
{displayName}
{inline ? ' ' : null}
<span className='display-name__account'>@{acct}</span>
{suffix}
</span>
) : null;
);
}
// Props.
@@ -36,4 +68,6 @@ DisplayName.propTypes = {
className: PropTypes.string,
inline: PropTypes.bool,
localDomain: PropTypes.string,
others: ImmutablePropTypes.list,
handleClick: PropTypes.func,
};

View File

@@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'flavours/glitch/components/icon';
const formatNumber = num => num > 40 ? '40+' : num;
const IconWithBadge = ({ id, count, className }) => (
<i className='icon-with-badge'>
<Icon icon={id} fixedWidth className={className} />
{count > 0 && <i className='icon-with-badge__badge'>{formatNumber(count)}</i>}
</i>
);
IconWithBadge.propTypes = {
id: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
className: PropTypes.string,
};
export default IconWithBadge;

View File

@@ -1,10 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task';
import getRectFromEntry from 'flavours/glitch/util/get_rect_from_entry';
export default class IntersectionObserverArticle extends ImmutablePureComponent {
// Diff these props in the "unrendered" state
const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight'];
export default class IntersectionObserverArticle extends React.Component {
static propTypes = {
intersectionObserverWrapper: PropTypes.object.isRequired,
@@ -22,20 +24,21 @@ export default class IntersectionObserverArticle extends ImmutablePureComponent
}
shouldComponentUpdate (nextProps, nextState) {
if (!nextState.isIntersecting && nextState.isHidden) {
// It's only if we're not intersecting (i.e. offscreen) and isHidden is true
// that either "isIntersecting" or "isHidden" matter, and then they're
// the only things that matter (and updated ARIA attributes).
return this.state.isIntersecting || !this.state.isHidden || nextProps.listLength !== this.props.listLength;
} else if (nextState.isIntersecting && !this.state.isIntersecting) {
// If we're going from a non-intersecting state to an intersecting state,
// (i.e. offscreen to onscreen), then we definitely need to re-render
const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight);
const willBeUnrendered = !nextState.isIntersecting && (nextState.isHidden || nextProps.cachedHeight);
if (!!isUnrendered !== !!willBeUnrendered) {
// If we're going from rendered to unrendered (or vice versa) then update
return true;
}
// Otherwise, diff based on "updateOnProps" and "updateOnStates"
return super.shouldComponentUpdate(nextProps, nextState);
// If we are and remain hidden, diff based on props
if (isUnrendered) {
return !updateOnPropsForUnrendered.every(prop => nextProps[prop] === this.props[prop]);
}
// Else, assume the children have changed
return true;
}
componentDidMount () {
const { intersectionObserverWrapper, id } = this.props;

View File

@@ -6,7 +6,7 @@ import IconButton from './icon_button';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { isIOS } from 'flavours/glitch/util/is_mobile';
import classNames from 'classnames';
import { autoPlayGif, displayMedia } from 'flavours/glitch/util/initial_state';
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/util/initial_state';
import { decode } from 'blurhash';
const messages = defineMessages({
@@ -101,6 +101,8 @@ class Item extends React.PureComponent {
}
_decode () {
if (!useBlurhash) return;
const hash = this.props.attachment.get('blurhash');
const pixels = decode(hash, 32, 32);
@@ -177,7 +179,7 @@ class Item extends React.PureComponent {
if (attachment.get('type') === 'unknown') {
return (
<div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }} title={attachment.get('description')}>
<canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' />
</a>
</div>

View File

@@ -66,6 +66,7 @@ export default class Status extends ImmutablePureComponent {
containerId: PropTypes.string,
id: PropTypes.string,
status: ImmutablePropTypes.map,
otherAccounts: ImmutablePropTypes.list,
account: ImmutablePropTypes.map,
onReply: PropTypes.func,
onFavourite: PropTypes.func,
@@ -83,6 +84,7 @@ export default class Status extends ImmutablePureComponent {
muted: PropTypes.bool,
collapse: PropTypes.bool,
hidden: PropTypes.bool,
unread: PropTypes.bool,
prepend: PropTypes.string,
withDismiss: PropTypes.bool,
onMoveUp: PropTypes.func,
@@ -93,6 +95,7 @@ export default class Status extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
cacheMediaWidth: PropTypes.func,
cachedMediaWidth: PropTypes.number,
onClick: PropTypes.func,
};
state = {
@@ -111,8 +114,6 @@ export default class Status extends ImmutablePureComponent {
'account',
'settings',
'prepend',
'boostModal',
'favouriteModal',
'muted',
'collapse',
'notification',
@@ -321,17 +322,21 @@ export default class Status extends ImmutablePureComponent {
const { status } = this.props;
const { isCollapsed } = this.state;
if (!router) return;
if (destination === undefined) {
destination = `/statuses/${
status.getIn(['reblog', 'id'], status.get('id'))
}`;
}
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
if (isCollapsed) this.setCollapsed(false);
else if (e.shiftKey) {
this.setCollapsed(true);
document.getSelection().removeAllRanges();
} else if (this.props.onClick) {
this.props.onClick();
return;
} else {
if (destination === undefined) {
destination = `/statuses/${
status.getIn(['reblog', 'id'], status.get('id'))
}`;
}
let state = {...router.history.location.state};
state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
router.history.push(destination, state);
@@ -441,6 +446,7 @@ export default class Status extends ImmutablePureComponent {
intl,
status,
account,
otherAccounts,
settings,
collapsed,
muted,
@@ -450,6 +456,7 @@ export default class Status extends ImmutablePureComponent {
onOpenMedia,
notification,
hidden,
unread,
featured,
...other
} = this.props;
@@ -514,16 +521,16 @@ export default class Status extends ImmutablePureComponent {
media={status.get('media_attachments')}
/>
);
} else if (attachments.getIn([0, 'type']) === 'video') { // Media type is 'video'
const video = status.getIn(['media_attachments', 0]);
} else if (['video', 'audio'].includes(attachments.getIn([0, 'type']))) {
const attachment = status.getIn(['media_attachments', 0]);
media = (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
{Component => (<Component
preview={video.get('preview_url')}
blurhash={video.get('blurhash')}
src={video.get('url')}
alt={video.get('description')}
preview={attachment.get('preview_url')}
blurhash={attachment.get('blurhash')}
src={attachment.get('url')}
alt={attachment.get('description')}
inline
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
@@ -537,7 +544,7 @@ export default class Status extends ImmutablePureComponent {
/>)}
</Bundle>
);
mediaIcon = 'video-camera';
mediaIcon = attachment.get('type') === 'video' ? 'video-camera' : 'music';
} else { // Media type is 'image' or 'gifv'
media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
@@ -617,6 +624,7 @@ export default class Status extends ImmutablePureComponent {
collapsed: isCollapsed,
'has-background': isCollapsed && background,
'status__wrapper-reply': !!status.get('in_reply_to_id'),
read: unread === false,
muted,
}, 'focusable');
@@ -647,6 +655,7 @@ export default class Status extends ImmutablePureComponent {
friend={account}
collapsed={isCollapsed}
parseClick={parseClick}
otherAccounts={otherAccounts}
/>
) : null}
</span>
@@ -656,6 +665,7 @@ export default class Status extends ImmutablePureComponent {
collapsible={settings.getIn(['collapsed', 'enabled'])}
collapsed={isCollapsed}
setCollapsed={setCollapsed}
directMessage={!!otherAccounts}
/>
</header>
<StatusContent
@@ -673,6 +683,7 @@ export default class Status extends ImmutablePureComponent {
status={status}
account={status.get('account')}
showReplyCount={settings.get('show_reply_count')}
directMessage={!!otherAccounts}
/>
) : null}
{notification ? (

View File

@@ -71,6 +71,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
onBookmark: PropTypes.func,
withDismiss: PropTypes.bool,
showReplyCount: PropTypes.bool,
directMessage: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@@ -191,7 +192,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
}
render () {
const { status, intl, withDismiss, showReplyCount } = this.props;
const { status, intl, withDismiss, showReplyCount, directMessage } = this.props;
const mutingConversation = status.get('muted');
const anonymousAccess = !me;
@@ -282,14 +283,15 @@ export default class StatusActionBar extends ImmutablePureComponent {
return (
<div className='status__action-bar'>
{replyButton}
<IconButton className='status__action-bar-button' disabled={reblogDisabled} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogDisabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(reblogMessage)} icon={reblogIcon} onClick={this.handleReblogClick} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
{shareButton}
<IconButton className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
<div className='status__action-bar-dropdown'>
<DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' ariaLabel={intl.formatMessage(messages.more)} />
</div>
{!directMessage && [
<IconButton className='status__action-bar-button' disabled={reblogDisabled} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogDisabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(reblogMessage)} icon={reblogIcon} onClick={this.handleReblogClick} />,
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />,
shareButton,
<IconButton className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />,
<div className='status__action-bar-dropdown'>
<DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' ariaLabel={intl.formatMessage(messages.more)} />
</div>,
]}
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
</div>

View File

@@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
// Mastodon imports.
import Avatar from './avatar';
import AvatarOverlay from './avatar_overlay';
import AvatarComposite from './avatar_composite';
import DisplayName from './display_name';
export default class StatusHeader extends React.PureComponent {
@@ -14,12 +15,18 @@ export default class StatusHeader extends React.PureComponent {
status: ImmutablePropTypes.map.isRequired,
friend: ImmutablePropTypes.map,
parseClick: PropTypes.func.isRequired,
otherAccounts: ImmutablePropTypes.list,
};
// Handles clicks on account name/image
handleClick = (id, e) => {
const { parseClick } = this.props;
parseClick(e, `/accounts/${id}`);
}
handleAccountClick = (e) => {
const { status, parseClick } = this.props;
parseClick(e, `/accounts/${status.getIn(['account', 'id'])}`);
const { status } = this.props;
this.handleClick(status.getIn(['account', 'id']), e);
}
// Rendering.
@@ -27,36 +34,55 @@ export default class StatusHeader extends React.PureComponent {
const {
status,
friend,
otherAccounts,
} = this.props;
const account = status.get('account');
return (
<div className='status__info__account' >
<a
href={account.get('url')}
target='_blank'
className='status__avatar'
onClick={this.handleAccountClick}
>
{
friend ? (
<AvatarOverlay account={account} friend={friend} />
) : (
<Avatar account={account} size={48} />
)
}
</a>
<a
href={account.get('url')}
target='_blank'
className='status__display-name'
onClick={this.handleAccountClick}
>
<DisplayName account={account} />
</a>
</div>
);
let statusAvatar;
if (otherAccounts && otherAccounts.size > 0) {
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} onAccountClick={this.handleClick} />;
} else if (friend === undefined || friend === null) {
statusAvatar = <Avatar account={account} size={48} />;
} else {
statusAvatar = <AvatarOverlay account={account} friend={friend} />;
}
if (!otherAccounts) {
return (
<div className='status__info__account'>
<a
href={account.get('url')}
target='_blank'
className='status__avatar'
onClick={this.handleAccountClick}
>
{statusAvatar}
</a>
<a
href={account.get('url')}
target='_blank'
className='status__display-name'
onClick={this.handleAccountClick}
>
<DisplayName account={account} others={otherAccounts} />
</a>
</div>
);
} else {
// This is a DM conversation
return (
<div className='status__info__account'>
<span className='status__avatar'>
{statusAvatar}
</span>
<span className='status__display-name'>
<DisplayName account={account} others={otherAccounts} onAccountClick={this.handleClick} />
</span>
</div>
);
}
}
}

View File

@@ -22,6 +22,7 @@ export default class StatusIcons extends React.PureComponent {
mediaIcon: PropTypes.string,
collapsible: PropTypes.bool,
collapsed: PropTypes.bool,
directMessage: PropTypes.bool,
setCollapsed: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
@@ -42,6 +43,7 @@ export default class StatusIcons extends React.PureComponent {
mediaIcon,
collapsible,
collapsed,
directMessage,
intl,
} = this.props;
@@ -59,9 +61,7 @@ export default class StatusIcons extends React.PureComponent {
aria-hidden='true'
/>
) : null}
{(
<VisibilityIcon visibility={status.get('visibility')} />
)}
{!directMessage && <VisibilityIcon visibility={status.get('visibility')} />}
{collapsible ? (
<IconButton
className='status__collapse-button'

View File

@@ -96,11 +96,16 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
},
onReblog (status, e) {
if (e.shiftKey || !boostModal) {
this.onModalReblog(status);
} else {
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
}
dispatch((_, getState) => {
let state = getState();
if (state.getIn(['local_settings', 'confirm_boost_missing_media_description']) && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog, missingMediaDescription: true }));
} else if (e.shiftKey || !boostModal) {
this.onModalReblog(status);
} else {
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
}
});
},
onBookmark (status) {

View File

@@ -55,6 +55,7 @@ class ComposeForm extends ImmutablePureComponent {
onPickEmoji: PropTypes.func,
showSearch: PropTypes.bool,
anyMedia: PropTypes.bool,
singleColumn: PropTypes.bool,
advancedOptions: ImmutablePropTypes.map,
layout: PropTypes.string,
@@ -66,8 +67,6 @@ class ComposeForm extends ImmutablePureComponent {
preselectOnReply: PropTypes.bool,
onChangeSpoilerness: PropTypes.func,
onChangeVisibility: PropTypes.func,
onMount: PropTypes.func,
onUnmount: PropTypes.func,
onPaste: PropTypes.func,
onMediaDescriptionConfirm: PropTypes.func,
};
@@ -141,6 +140,10 @@ class ComposeForm extends ImmutablePureComponent {
}
}
setRef = c => {
this.composeForm = c;
};
// Inserts an emoji at the caret.
handleEmoji = (data) => {
const { textarea: { selectionStart } } = this;
@@ -192,19 +195,9 @@ class ComposeForm extends ImmutablePureComponent {
}
}
// Tells our state the composer has been mounted.
componentDidMount () {
const { onMount } = this.props;
if (onMount) {
onMount();
}
}
// Tells our state the composer has been unmounted.
componentWillUnmount () {
const { onUnmount } = this.props;
if (onUnmount) {
onUnmount();
handleFocus = () => {
if (this.composeForm && !this.props.singleColumn) {
this.composeForm.scrollIntoView();
}
}
@@ -227,6 +220,7 @@ class ComposeForm extends ImmutablePureComponent {
preselectDate,
text,
preselectOnReply,
singleColumn,
} = this.props;
let selectionEnd, selectionStart;
@@ -246,7 +240,7 @@ class ComposeForm extends ImmutablePureComponent {
if (textarea) {
textarea.setSelectionRange(selectionStart, selectionEnd);
textarea.focus();
textarea.scrollIntoView();
if (!singleColumn) textarea.scrollIntoView();
}
// Refocuses the textarea after submitting.
@@ -307,7 +301,7 @@ class ComposeForm extends ImmutablePureComponent {
<ReplyIndicatorContainer />
<div className={`composer--spoiler ${spoiler ? 'composer--spoiler--visible' : ''}`}>
<div className={`composer--spoiler ${spoiler ? 'composer--spoiler--visible' : ''}`} ref={this.setRef}>
<AutosuggestInput
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={spoilerText}
@@ -323,34 +317,32 @@ class ComposeForm extends ImmutablePureComponent {
searchTokens={[':']}
id='glitch.composer.spoiler.input'
className='spoiler-input__input'
autoFocus={false}
/>
</div>
<div className='composer--textarea'>
<TextareaIcons advancedOptions={advancedOptions} />
<AutosuggestTextarea
ref={this.setAutosuggestTextarea}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={isSubmitting}
value={this.props.text}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onKeyDown={this.handleKeyDown}
onSuggestionsFetchRequested={onFetchSuggestions}
onSuggestionsClearRequested={onClearSuggestions}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
/>
<AutosuggestTextarea
ref={this.setAutosuggestTextarea}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={isSubmitting}
value={this.props.text}
onChange={this.handleChange}
suggestions={this.props.suggestions}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
onSuggestionsFetchRequested={onFetchSuggestions}
onSuggestionsClearRequested={onClearSuggestions}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
>
<EmojiPicker onPickEmoji={handleEmoji} />
</div>
<div className='compose-form__modifiers'>
<UploadFormContainer />
<PollFormContainer />
</div>
<TextareaIcons advancedOptions={advancedOptions} />
<div className='compose-form__modifiers'>
<UploadFormContainer />
<PollFormContainer />
</div>
</AutosuggestTextarea>
<OptionsContainer
advancedOptions={advancedOptions}

View File

@@ -17,19 +17,21 @@ export default class NavigationBar extends ImmutablePureComponent {
<div className='drawer--account'>
<Permalink className='avatar' href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
<Avatar account={this.props.account} size={40} />
<Avatar account={this.props.account} size={48} />
</Permalink>
<Permalink className='acct' href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
<strong>@{this.props.account.get('acct')}</strong>
</Permalink>
<div className='navigation-bar__profile'>
<Permalink className='acct' href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
<strong>@{this.props.account.get('acct')}</strong>
</Permalink>
{ profileLink !== undefined && (
<a
className='edit'
href={ profileLink }
><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
)}
{ profileLink !== undefined && (
<a
className='edit'
href={ profileLink }
><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
)}
</div>
</div>
);
};

View File

@@ -232,7 +232,7 @@ class ComposerOptions extends ImmutablePureComponent {
const contentTypeItems = {
plain: {
icon: 'align-left',
icon: 'file-text',
name: 'text/plain',
text: <FormattedMessage {...messages.plain} />,
},

View File

@@ -33,10 +33,10 @@ class SearchPopout extends React.PureComponent {
const { style } = this.props;
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
return (
<div style={{ ...style, position: 'absolute', width: 285 }}>
<div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}>
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<div className='drawer--search--popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
<ul>
@@ -60,6 +60,10 @@ class SearchPopout extends React.PureComponent {
export default @injectIntl
class Search extends React.PureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
};
static propTypes = {
value: PropTypes.string.isRequired,
submitted: PropTypes.bool,
@@ -67,6 +71,7 @@ class Search extends React.PureComponent {
onSubmit: PropTypes.func.isRequired,
onClear: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired,
openInRoute: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@@ -109,8 +114,10 @@ class Search extends React.PureComponent {
const { onSubmit } = this.props;
switch (e.key) {
case 'Enter':
if (onSubmit) {
onSubmit();
onSubmit();
if (this.props.openInRoute) {
this.context.router.history.push('/search');
}
break;
case 'Escape':
@@ -121,14 +128,14 @@ class Search extends React.PureComponent {
render () {
const { intl, value, submitted } = this.props;
const { expanded } = this.state;
const active = value.length > 0 || submitted;
const computedClass = classNames('drawer--search', { active });
const hasValue = value.length > 0 || submitted;
return (
<div className={computedClass}>
<div className='search'>
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
<input
className='search__input'
type='text'
placeholder={intl.formatMessage(messages.placeholder)}
value={value || ''}
@@ -138,17 +145,19 @@ class Search extends React.PureComponent {
onBlur={this.handleBlur}
/>
</label>
<div
aria-label={intl.formatMessage(messages.placeholder)}
className='icon'
className='search__icon'
onClick={this.handleClear}
role='button'
tabIndex='0'
>
<Icon icon='search' />
<Icon icon='times-circle' />
<Icon icon='search' className={hasValue ? '' : 'active'} />
<Icon icon='times-circle' className={hasValue ? 'active' : ''} />
</div>
<Overlay show={expanded && !active} placement='bottom' target={this}>
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
<SearchPopout />
</Overlay>
</div>

View File

@@ -7,6 +7,7 @@ import StatusContainer from 'flavours/glitch/containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Hashtag from 'flavours/glitch/components/hashtag';
import Icon from 'flavours/glitch/components/icon';
import { searchEnabled } from 'flavours/glitch/util/initial_state';
const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
@@ -20,6 +21,7 @@ class SearchResults extends ImmutablePureComponent {
suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired,
searchTerm: PropTypes.string,
intl: PropTypes.object.isRequired,
};
@@ -27,8 +29,8 @@ class SearchResults extends ImmutablePureComponent {
this.props.fetchSuggestions();
}
render() {
const { intl, results, suggestions, dismissSuggestion } = this.props;
render () {
const { intl, results, suggestions, dismissSuggestion, searchTerm } = this.props;
if (results.isEmpty() && !suggestions.isEmpty()) {
return (
@@ -51,6 +53,16 @@ class SearchResults extends ImmutablePureComponent {
</div>
</div>
);
} else if(results.get('statuses') && results.get('statuses').size === 0 && !searchEnabled && !(searchTerm.startsWith('@') || searchTerm.startsWith('#') || searchTerm.includes(' '))) {
statuses = (
<section>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
<div className='search-results__info'>
<FormattedMessage id='search_results.statuses_fts_disabled' defaultMessage='Searching toots by their content is not enabled on this Mastodon server.' />
</div>
</section>
);
}
let accounts, statuses, hashtags;

View File

@@ -9,10 +9,8 @@ import {
clearComposeSuggestions,
fetchComposeSuggestions,
insertEmojiCompose,
mountCompose,
selectComposeSuggestion,
submitCompose,
unmountCompose,
uploadCompose,
} from 'flavours/glitch/actions/compose';
import {
@@ -114,14 +112,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(changeComposeVisibility(value));
},
onMount() {
dispatch(mountCompose());
},
onUnmount() {
dispatch(unmountCompose());
},
onMediaDescriptionConfirm(routerHistory) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.missingDescriptionMessage),

View File

@@ -16,7 +16,7 @@ function mapStateToProps (state) {
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']).toArray().join(','),
resetFileKey: state.getIn(['compose', 'resetFileKey']),
hasPoll: !!poll,
allowMedia: !poll && (media ? media.size < 4 && !media.some(item => item.get('type') === 'video') : true),
allowMedia: !poll && (media ? media.size < 4 && !media.some(item => ['video', 'audio'].includes(item.get('type'))) : true),
hasMedia: media && !!media.size,
allowPoll: !(media && !!media.size),
showContentTypeChoice: state.getIn(['local_settings', 'show_content_type_choice']),

View File

@@ -5,6 +5,7 @@ import { fetchSuggestions, dismissSuggestion } from '../../../actions/suggestion
const mapStateToProps = state => ({
results: state.getIn(['search', 'results']),
suggestions: state.getIn(['suggestions', 'items']),
searchTerm: state.getIn(['search', 'searchTerm']),
});
const mapDispatchToProps = dispatch => ({

View File

@@ -4,6 +4,7 @@ import NavigationContainer from './containers/navigation_container';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose';
import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import SearchContainer from './containers/search_container';
@@ -27,9 +28,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onClickElefriend () {
dispatch(cycleElefriendCompose());
},
onMount () {
dispatch(mountCompose());
},
onUnmount () {
dispatch(unmountCompose());
},
});
export default @connect(mapStateToProps)
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class Compose extends React.PureComponent {
static propTypes = {
@@ -38,9 +47,27 @@ class Compose extends React.PureComponent {
isSearchPage: PropTypes.bool,
elefriend: PropTypes.number,
onClickElefriend: PropTypes.func,
onMount: PropTypes.func,
onUnmount: PropTypes.func,
intl: PropTypes.object.isRequired,
};
componentDidMount () {
const { isSearchPage } = this.props;
if (!isSearchPage) {
this.props.onMount();
}
}
componentWillUnmount () {
const { isSearchPage } = this.props;
if (!isSearchPage) {
this.props.onUnmount();
}
}
render () {
const {
elefriend,
@@ -61,12 +88,12 @@ class Compose extends React.PureComponent {
<div className='drawer__pager'>
{!isSearchPage && <div className='drawer__inner'>
<NavigationContainer />
<ComposeFormContainer />
{multiColumn && (
<div className='drawer__inner__mastodon'>
{mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' onClick={onClickElefriend} />}
</div>
)}
<div className='drawer__inner__mastodon'>
{mascot ? <img alt='' draggable='false' src={mascot} /> : <button className='mastodon' onClick={onClickElefriend} />}
</div>
</div>}
<Motion defaultStyle={{ x: isSearchPage ? 0 : -100 }} style={{ x: spring(showSearch || isSearchPage ? 0 : -100, { stiffness: 210, damping: 20 }) }}>

View File

@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import StatusContainer from 'flavours/glitch/containers/status_container';
export default class Conversation extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
conversationId: PropTypes.string.isRequired,
accounts: ImmutablePropTypes.list.isRequired,
lastStatusId: PropTypes.string,
unread:PropTypes.bool.isRequired,
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
markRead: PropTypes.func.isRequired,
};
handleClick = () => {
if (!this.context.router) {
return;
}
const { lastStatusId, unread, markRead } = this.props;
if (unread) {
markRead();
}
this.context.router.history.push(`/statuses/${lastStatusId}`);
}
handleHotkeyMoveUp = () => {
this.props.onMoveUp(this.props.conversationId);
}
handleHotkeyMoveDown = () => {
this.props.onMoveDown(this.props.conversationId);
}
render () {
const { accounts, lastStatusId, unread } = this.props;
if (lastStatusId === null) {
return null;
}
return (
<StatusContainer
id={lastStatusId}
unread={unread}
otherAccounts={accounts}
onMoveUp={this.handleHotkeyMoveUp}
onMoveDown={this.handleHotkeyMoveDown}
onClick={this.handleClick}
/>
);
}
}

View File

@@ -0,0 +1,73 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ConversationContainer from '../containers/conversation_container';
import ScrollableList from 'flavours/glitch/components/scrollable_list';
import { debounce } from 'lodash';
export default class ConversationsList extends ImmutablePureComponent {
static propTypes = {
conversations: ImmutablePropTypes.list.isRequired,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
onLoadMore: PropTypes.func,
};
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
handleMoveUp = id => {
const elementIndex = this.getCurrentIndex(id) - 1;
this._selectChild(elementIndex, true);
}
handleMoveDown = id => {
const elementIndex = this.getCurrentIndex(id) + 1;
this._selectChild(elementIndex, false);
}
_selectChild (index, align_top) {
const container = this.node.node;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
if (element) {
if (align_top && container.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
}
element.focus();
}
}
setRef = c => {
this.node = c;
}
handleLoadOlder = debounce(() => {
const last = this.props.conversations.last();
if (last && last.get('last_status')) {
this.props.onLoadMore(last.get('last_status'));
}
}, 300, { leading: true })
render () {
const { conversations, onLoadMore, ...other } = this.props;
return (
<ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} scrollKey='direct' ref={this.setRef}>
{conversations.map(item => (
<ConversationContainer
key={item.get('id')}
conversationId={item.get('id')}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
/>
))}
</ScrollableList>
);
}
}

View File

@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import Conversation from '../components/conversation';
import { markConversationRead } from '../../../actions/conversations';
const mapStateToProps = (state, { conversationId }) => {
const conversation = state.getIn(['conversations', 'items']).find(x => x.get('id') === conversationId);
return {
accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)),
unread: conversation.get('unread'),
lastStatusId: conversation.get('last_status', null),
};
};
const mapDispatchToProps = (dispatch, { conversationId }) => ({
markRead: () => dispatch(markConversationRead(conversationId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Conversation);

View File

@@ -0,0 +1,15 @@
import { connect } from 'react-redux';
import ConversationsList from '../components/conversations_list';
import { expandConversations } from 'flavours/glitch/actions/conversations';
const mapStateToProps = state => ({
conversations: state.getIn(['conversations', 'items']),
isLoading: state.getIn(['conversations', 'isLoading'], true),
hasMore: state.getIn(['conversations', 'hasMore'], false),
});
const mapDispatchToProps = dispatch => ({
onLoadMore: maxId => dispatch(expandConversations({ maxId })),
});
export default connect(mapStateToProps, mapDispatchToProps)(ConversationsList);

View File

@@ -5,10 +5,13 @@ import StatusListContainer from 'flavours/glitch/features/ui/containers/status_l
import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header';
import { expandDirectTimeline } from 'flavours/glitch/actions/timelines';
import { mountConversations, unmountConversations, expandConversations } from 'flavours/glitch/actions/conversations';
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectDirectStream } from 'flavours/glitch/actions/streaming';
import { changeSetting } from 'flavours/glitch/actions/settings';
import ConversationsListContainer from './containers/conversations_list_container';
const messages = defineMessages({
title: { id: 'column.direct', defaultMessage: 'Direct messages' },
@@ -16,6 +19,7 @@ const messages = defineMessages({
const mapStateToProps = state => ({
hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
conversationsMode: state.getIn(['settings', 'direct', 'conversations']),
});
@connect(mapStateToProps)
@@ -28,6 +32,7 @@ export default class DirectTimeline extends React.PureComponent {
intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
conversationsMode: PropTypes.bool,
};
handlePin = () => {
@@ -50,13 +55,32 @@ export default class DirectTimeline extends React.PureComponent {
}
componentDidMount () {
const { dispatch } = this.props;
const { dispatch, conversationsMode } = this.props;
dispatch(mountConversations());
if (conversationsMode) {
dispatch(expandConversations());
} else {
dispatch(expandDirectTimeline());
}
dispatch(expandDirectTimeline());
this.disconnect = dispatch(connectDirectStream());
}
componentDidUpdate(prevProps) {
const { dispatch, conversationsMode } = this.props;
if (prevProps.conversationsMode && !conversationsMode) {
dispatch(expandDirectTimeline());
} else if (!prevProps.conversationsMode && conversationsMode) {
dispatch(expandConversations());
}
}
componentWillUnmount () {
this.props.dispatch(unmountConversations());
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
@@ -67,14 +91,49 @@ export default class DirectTimeline extends React.PureComponent {
this.column = c;
}
handleLoadMore = maxId => {
handleLoadMoreTimeline = maxId => {
this.props.dispatch(expandDirectTimeline({ maxId }));
}
handleLoadMoreConversations = maxId => {
this.props.dispatch(expandConversations({ maxId }));
}
handleTimelineClick = () => {
this.props.dispatch(changeSetting(['direct', 'conversations'], false));
}
handleConversationsClick = () => {
this.props.dispatch(changeSetting(['direct', 'conversations'], true));
}
render () {
const { intl, hasUnread, columnId, multiColumn } = this.props;
const { intl, hasUnread, columnId, multiColumn, conversationsMode } = this.props;
const pinned = !!columnId;
let contents;
if (conversationsMode) {
contents = (
<ConversationsListContainer
trackScroll={!pinned}
scrollKey={`direct_timeline-${columnId}`}
timelineId='direct'
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
/>
);
} else {
contents = (
<StatusListContainer
trackScroll={!pinned}
scrollKey={`direct_timeline-${columnId}`}
timelineId='direct'
onLoadMore={this.handleLoadMoreTimeline}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
/>
);
}
return (
<Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
@@ -90,13 +149,28 @@ export default class DirectTimeline extends React.PureComponent {
<ColumnSettingsContainer />
</ColumnHeader>
<StatusListContainer
trackScroll={!pinned}
scrollKey={`direct_timeline-${columnId}`}
timelineId='direct'
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
/>
<div className='notification__filter-bar'>
<button
className={conversationsMode ? 'active' : ''}
onClick={this.handleConversationsClick}
>
<FormattedMessage
id='direct.conversations_mode'
defaultMessage='Conversations'
/>
</button>
<button
className={conversationsMode ? '' : 'active'}
onClick={this.handleTimelineClick}
>
<FormattedMessage
id='direct.timeline_mode'
defaultMessage='Timeline'
/>
</button>
</div>
{contents}
</Column>
);
}

View File

@@ -11,7 +11,7 @@ import Overlay from 'react-overlays/lib/Overlay';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import detectPassiveEvents from 'detect-passive-events';
import { buildCustomEmojis } from 'flavours/glitch/util/emoji';
import { buildCustomEmojis, categoriesFromEmojis } from 'flavours/glitch/util/emoji';
const messages = defineMessages({
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
@@ -110,19 +110,6 @@ let EmojiPicker, Emoji; // load asynchronously
const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`;
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
const categoriesSort = [
'recent',
'custom',
'people',
'nature',
'foods',
'activity',
'places',
'objects',
'symbols',
'flags',
];
class ModifierPickerMenu extends React.PureComponent {
static propTypes = {
@@ -320,8 +307,23 @@ class EmojiPickerMenu extends React.PureComponent {
}
const title = intl.formatMessage(messages.emoji);
const { modifierOpen } = this.state;
const categoriesSort = [
'recent',
'people',
'nature',
'foods',
'activity',
'places',
'objects',
'symbols',
'flags',
];
categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(custom_emojis)).sort());
return (
<div className={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen })} style={style} ref={this.setRef}>
<EmojiPicker

View File

@@ -59,7 +59,7 @@ export default class FollowRequests extends ImmutablePureComponent {
}
return (
<Column name='follow-requests' icon='users' heading={intl.formatMessage(messages.heading)}>
<Column name='follow-requests' icon='user-plus' heading={intl.formatMessage(messages.heading)}>
<ColumnBackButtonSlim />
<ScrollContainer scrollKey='follow_requests' shouldUpdateScroll={this.shouldUpdateScroll}>

View File

@@ -8,12 +8,14 @@ import { openModal } from 'flavours/glitch/actions/modal';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, invitesEnabled, version } from 'flavours/glitch/util/initial_state';
import { me } from 'flavours/glitch/util/initial_state';
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
import { List as ImmutableList } from 'immutable';
import { createSelector } from 'reselect';
import { fetchLists } from 'flavours/glitch/actions/lists';
import { preferencesLink, profileLink, signOutLink } from 'flavours/glitch/util/backend_links';
import { preferencesLink, signOutLink } from 'flavours/glitch/util/backend_links';
import NavigationBar from '../compose/components/navigation_bar';
import LinkFooter from 'flavours/glitch/features/ui/components/link_footer';
const messages = defineMessages({
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@@ -73,9 +75,15 @@ const badgeDisplay = (number, limit) => {
}
};
@connect(makeMapStateToProps, mapDispatchToProps)
@injectIntl
export default class GettingStarted extends ImmutablePureComponent {
const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
export default @connect(makeMapStateToProps, mapDispatchToProps)
@injectIntl
class GettingStarted extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
};
static propTypes = {
intl: PropTypes.object.isRequired,
@@ -95,7 +103,12 @@ export default class GettingStarted extends ImmutablePureComponent {
}
componentDidMount () {
const { myAccount, fetchFollowRequests } = this.props;
const { myAccount, fetchFollowRequests, multiColumn } = this.props;
if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) {
this.context.router.history.replace('/timelines/home');
return;
}
if (myAccount.get('locked')) {
fetchFollowRequests();
@@ -135,7 +148,7 @@ export default class GettingStarted extends ImmutablePureComponent {
}
if (myAccount.get('locked')) {
navItems.push(<ColumnLink key='6' icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
navItems.push(<ColumnLink key='6' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
}
navItems.push(<ColumnLink key='7' icon='ellipsis-h' text={intl.formatMessage(messages.misc)} to='/getting-started-misc' />);
@@ -153,7 +166,8 @@ export default class GettingStarted extends ImmutablePureComponent {
<Column name='getting-started' icon='asterisk' heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile>
<div className='scrollable optionally-scrollable'>
<div className='getting-started__wrapper'>
<ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)} />
{!multiColumn && <NavigationBar account={myAccount} />}
{multiColumn && <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)} />}
{navItems}
<ColumnSubheading text={intl.formatMessage(messages.lists_subheading)} />
{listItems}
@@ -163,25 +177,7 @@ export default class GettingStarted extends ImmutablePureComponent {
<ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href={signOutLink} method='delete' />
</div>
<div className='getting-started__footer'>
<ul>
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
<li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a></li>
</ul>
<p>
<FormattedMessage
id='getting_started.open_source_notice'
defaultMessage='Glitchsoc is open source software, a friendly fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.'
values={{
github: <span><a href='https://github.com/glitch-soc/mastodon' rel='noopener' target='_blank'>glitch-soc/mastodon</a> (v{version})</span>,
Mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>Mastodon</a> }}
/>
</p>
</div>
<LinkFooter />
</div>
</Column>
);

View File

@@ -75,6 +75,23 @@ export default class ListTimeline extends React.PureComponent {
this.disconnect = dispatch(connectListStream(id));
}
componentWillReceiveProps (nextProps) {
const { dispatch } = this.props;
const { id } = nextProps.params;
if (id !== this.props.params.id) {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
dispatch(fetchList(id));
dispatch(expandListTimeline(id));
this.disconnect = dispatch(connectListStream(id));
}
}
componentWillUnmount () {
if (this.disconnect) {
this.disconnect();

View File

@@ -74,7 +74,7 @@ export default class LocalSettingsNavigation extends React.PureComponent {
active={index === 5}
href={ preferencesLink }
index={5}
icon='sliders'
icon='cog'
title={intl.formatMessage(messages.preferences)}
/>
<LocalSettingsNavigationItem

View File

@@ -11,8 +11,11 @@ import LocalSettingsPageItem from './item';
const messages = defineMessages({
layout_auto: { id: 'layout.auto', defaultMessage: 'Auto' },
layout_auto_hint: { id: 'layout.hint.auto', defaultMessage: 'Automatically chose layout based on “Enable advanced web interface” setting and screen size.' },
layout_desktop: { id: 'layout.desktop', defaultMessage: 'Desktop' },
layout_desktop_hint: { id: 'layout.hint.desktop', defaultMessage: 'Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.' },
layout_mobile: { id: 'layout.single', defaultMessage: 'Mobile' },
layout_mobile_hint: { id: 'layout.hint.single', defaultMessage: 'Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.' },
side_arm_none: { id: 'settings.side_arm.none', defaultMessage: 'None' },
side_arm_keep: { id: 'settings.side_arm_reply_mode.keep', defaultMessage: 'Keep secondary toot button to set privacy' },
side_arm_copy: { id: 'settings.side_arm_reply_mode.copy', defaultMessage: 'Copy privacy setting of the toot being replied to' },
@@ -51,6 +54,14 @@ export default class LocalSettingsPage extends React.PureComponent {
<FormattedMessage id='settings.hicolor_privacy_icons' defaultMessage='High color privacy icons' />
<span className='hint'><FormattedMessage id='settings.hicolor_privacy_icons.hint' defaultMessage="Display privacy icons in bright and easily distinguishable colors" /></span>
</LocalSettingsPageItem>
<LocalSettingsPageItem
settings={settings}
item={['confirm_boost_missing_media_description']}
id='mastodon-settings--confirm_boost_missing_media_description'
onChange={onChange}
>
<FormattedMessage id='settings.confirm_boost_missing_media_description' defaultMessage='Show confirmation dialog before boosting toots lacking media descriptions' />
</LocalSettingsPageItem>
<section>
<h2><FormattedMessage id='settings.notifications_opts' defaultMessage='Notifications options' /></h2>
<LocalSettingsPageItem
@@ -79,9 +90,9 @@ export default class LocalSettingsPage extends React.PureComponent {
item={['layout']}
id='mastodon-settings--layout'
options={[
{ value: 'auto', message: intl.formatMessage(messages.layout_auto) },
{ value: 'multiple', message: intl.formatMessage(messages.layout_desktop) },
{ value: 'single', message: intl.formatMessage(messages.layout_mobile) },
{ value: 'auto', message: intl.formatMessage(messages.layout_auto), hint: intl.formatMessage(messages.layout_auto_hint) },
{ value: 'multiple', message: intl.formatMessage(messages.layout_desktop), hint: intl.formatMessage(messages.layout_desktop_hint) },
{ value: 'single', message: intl.formatMessage(messages.layout_mobile), hint: intl.formatMessage(messages.layout_mobile_hint) },
]}
onChange={onChange}
>

View File

@@ -0,0 +1,17 @@
import React from 'react';
import SearchContainer from 'flavours/glitch/features/compose/containers/search_container';
import SearchResultsContainer from 'flavours/glitch/features/compose/containers/search_results_container';
const Search = () => (
<div className='column search-page'>
<SearchContainer />
<div className='drawer__pager'>
<div className='drawer__inner darker'>
<SearchResultsContainer />
</div>
</div>
</div>
);
export default Search;

View File

@@ -131,14 +131,14 @@ export default class DetailedStatus extends ImmutablePureComponent {
} else if (status.get('media_attachments').size > 0) {
if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
media = <AttachmentList media={status.get('media_attachments')} />;
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
const video = status.getIn(['media_attachments', 0]);
} else if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) {
const attachment = status.getIn(['media_attachments', 0]);
media = (
<Video
preview={video.get('preview_url')}
blurhash={video.get('blurhash')}
src={video.get('url')}
alt={video.get('description')}
preview={attachment.get('preview_url')}
blurhash={attachment.get('blurhash')}
src={attachment.get('url')}
alt={attachment.get('description')}
inline
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
@@ -150,7 +150,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
onToggleVisibility={this.props.onToggleMediaVisibility}
/>
);
mediaIcon = 'video-camera';
mediaIcon = attachment.get('type') === 'video' ? 'video-camera' : 'music';
} else {
media = (
<MediaGallery

View File

@@ -231,18 +231,24 @@ export default class Status extends ImmutablePureComponent {
}
handleModalReblog = (status) => {
this.props.dispatch(reblog(status));
const { dispatch } = this.props;
if (status.get('reblogged')) {
dispatch(unreblog(status));
} else {
dispatch(reblog(status));
}
}
handleReblogClick = (status, e) => {
if (status.get('reblogged')) {
this.props.dispatch(unreblog(status));
const { settings, dispatch } = this.props;
if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog, missingMediaDescription: true }));
} else if ((e && e.shiftKey) || !boostModal) {
this.handleModalReblog(status);
} else {
if ((e && e.shiftKey) || !boostModal) {
this.handleModalReblog(status);
} else {
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
}
dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
}
}

View File

@@ -7,6 +7,7 @@ import StatusContent from 'flavours/glitch/components/status_content';
import Avatar from 'flavours/glitch/components/avatar';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import DisplayName from 'flavours/glitch/components/display_name';
import AttachmentList from 'flavours/glitch/components/attachment_list';
import ImmutablePureComponent from 'react-immutable-pure-component';
const messages = defineMessages({
@@ -25,6 +26,7 @@ export default class BoostModal extends ImmutablePureComponent {
status: ImmutablePropTypes.map.isRequired,
onReblog: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
missingMediaDescription: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@@ -52,7 +54,7 @@ export default class BoostModal extends ImmutablePureComponent {
}
render () {
const { status, intl } = this.props;
const { status, missingMediaDescription, intl } = this.props;
const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
return (
@@ -74,11 +76,24 @@ export default class BoostModal extends ImmutablePureComponent {
</div>
<StatusContent status={status} />
{status.get('media_attachments').size > 0 && (
<AttachmentList
compact
media={status.get('media_attachments')}
/>
)}
</div>
</div>
<div className='boost-modal__action-bar'>
<div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <i className='fa fa-retweet' /></span> }} /></div>
<div>
{ missingMediaDescription ?
<FormattedMessage id='boost_modal.missing_description' defaultMessage='This toot contains some media without description' />
:
<FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <i className='fa fa-retweet' /></span> }} />
}
</div>
<Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} />
</div>
</div>

View File

@@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ReactSwipeableViews from 'react-swipeable-views';
import { links, getIndex, getLink } from './tabs_bar';
import TabsBar, { links, getIndex, getLink } from './tabs_bar';
import { Link } from 'react-router-dom';
import BundleContainer from '../containers/bundle_container';
@@ -13,6 +13,8 @@ import ColumnLoading from './column_loading';
import DrawerLoading from './drawer_loading';
import BundleColumnError from './bundle_column_error';
import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, BookmarkedStatuses, ListTimeline } from 'flavours/glitch/util/async-components';
import ComposePanel from './compose_panel';
import NavigationPanel from './navigation_panel';
import detectPassiveEvents from 'detect-passive-events';
import { scrollRight } from 'flavours/glitch/util/scroll';
@@ -49,6 +51,8 @@ export default class ColumnsArea extends ImmutablePureComponent {
swipeToChangeColumns: PropTypes.bool,
singleColumn: PropTypes.bool,
children: PropTypes.node,
navbarUnder: PropTypes.bool,
openSettings: PropTypes.func,
};
state = {
@@ -108,6 +112,11 @@ export default class ColumnsArea extends ImmutablePureComponent {
// React-router does this for us, but too late, feeling laggy.
document.querySelector(currentLinkSelector).classList.remove('active');
document.querySelector(nextLinkSelector).classList.add('active');
if (!this.state.shouldAnimate && typeof this.pendingIndex === 'number') {
this.context.router.history.push(getLink(this.pendingIndex));
this.pendingIndex = null;
}
}
handleAnimationEnd = () => {
@@ -139,7 +148,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
<ColumnLoading title={title} icon={icon} />;
return (
<div className='columns-area' key={index}>
<div className='columns-area columns-area--mobile' key={index}>
{view}
</div>
);
@@ -154,26 +163,45 @@ export default class ColumnsArea extends ImmutablePureComponent {
}
render () {
const { columns, children, singleColumn, swipeToChangeColumns, intl } = this.props;
const { columns, children, singleColumn, swipeToChangeColumns, intl, navbarUnder, openSettings } = this.props;
const { shouldAnimate } = this.state;
const columnIndex = getIndex(this.context.router.history.location.pathname);
this.pendingIndex = null;
if (singleColumn) {
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><i className='fa fa-pencil' /></Link>;
return columnIndex !== -1 ? [
const content = columnIndex !== -1 ? (
<ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={!swipeToChangeColumns}>
{links.map(this.renderView)}
</ReactSwipeableViews>,
</ReactSwipeableViews>
) : (
<div key='content' className='columns-area columns-area--mobile'>{children}</div>
);
floatingActionButton,
] : [
<div className='columns-area'>{children}</div>,
return (
<div className='columns-area__panels'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
<ComposePanel />
</div>
</div>
floatingActionButton,
];
<div className='columns-area__panels__main'>
{!navbarUnder && <TabsBar key='tabs' />}
{content}
{navbarUnder && <TabsBar key='tabs' />}
</div>
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
<div className='columns-area__panels__pane__inner'>
<NavigationPanel onOpenSettings={openSettings} />
</div>
</div>
{floatingActionButton}
</div>
);
}
return (

View File

@@ -0,0 +1,16 @@
import React from 'react';
import SearchContainer from 'flavours/glitch/features/compose/containers/search_container';
import ComposeFormContainer from 'flavours/glitch/features/compose/containers/compose_form_container';
import NavigationContainer from 'flavours/glitch/features/compose/containers/navigation_container';
import LinkFooter from './link_footer';
const ComposePanel = () => (
<div className='compose-panel'>
<SearchContainer openInRoute />
<NavigationContainer />
<ComposeFormContainer singleColumn />
<LinkFooter withHotkeys />
</div>
);
export default ComposePanel;

View File

@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
import { connect } from 'react-redux';
import { NavLink, withRouter } from 'react-router-dom';
import IconWithBadge from 'flavours/glitch/components/icon_with_badge';
import { me } from 'flavours/glitch/util/initial_state';
import { List as ImmutableList } from 'immutable';
import { FormattedMessage } from 'react-intl';
const mapStateToProps = state => ({
locked: state.getIn(['accounts', me, 'locked']),
count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
});
export default @withRouter
@connect(mapStateToProps)
class FollowRequestsNavLink extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
locked: PropTypes.bool,
count: PropTypes.number.isRequired,
};
componentDidMount () {
const { dispatch, locked } = this.props;
if (locked) {
dispatch(fetchFollowRequests());
}
}
render () {
const { locked, count } = this.props;
if (!locked || count === 0) {
return null;
}
return <NavLink className='column-link column-link--transparent' to='/follow_requests'><IconWithBadge className='column-link__icon' id='user-plus' count={count} /><FormattedMessage id='navigation_bar.follow_requests' defaultMessage='Follow requests' /></NavLink>;
}
}

View File

@@ -0,0 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { invitesEnabled, version, repository, source_url } from 'flavours/glitch/util/initial_state';
import { signOutLink } from 'flavours/glitch/util/backend_links';
const LinkFooter = () => (
<div className='getting-started__footer'>
<ul>
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
<li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
<li><a href={signOutLink} data-method='delete'><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
</ul>
<p>
<FormattedMessage
id='getting_started.open_source_notice'
defaultMessage='Glitchsoc is open source software, a friendly fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.'
values={{
github: <span><a href='https://github.com/glitch-soc/mastodon' rel='noopener' target='_blank'>glitch-soc/mastodon</a> (v{version})</span>,
Mastodon: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>Mastodon</a> }}
/>
</p>
</div>
);
LinkFooter.propTypes = {
};
export default LinkFooter;

View File

@@ -0,0 +1,55 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { fetchLists } from 'flavours/glitch/actions/lists';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { NavLink, withRouter } from 'react-router-dom';
import Icon from 'flavours/glitch/components/icon';
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
if (!lists) {
return lists;
}
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))).take(4);
});
const mapStateToProps = state => ({
lists: getOrderedLists(state),
});
export default @withRouter
@connect(mapStateToProps)
class ListPanel extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
lists: ImmutablePropTypes.list,
};
componentDidMount () {
const { dispatch } = this.props;
dispatch(fetchLists());
}
render () {
const { lists } = this.props;
if (!lists || lists.isEmpty()) {
return null;
}
return (
<div>
<hr />
{lists.map(list => (
<NavLink key={list.get('id')} className='column-link column-link--transparent' strict to={`/timelines/list/${list.get('id')}`}><Icon className='column-link__icon' icon='list-ul' fixedWidth />{list.get('title')}</NavLink>
))}
</div>
);
}
}

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { NavLink, withRouter } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import Icon from 'flavours/glitch/components/icon';
import { profile_directory } from 'flavours/glitch/util/initial_state';
import NotificationsCounterIcon from './notifications_counter_icon';
import FollowRequestsNavLink from './follow_requests_nav_link';
import ListPanel from './list_panel';
const NavigationPanel = ({ onOpenSettings }) => (
<div className='navigation-panel'>
<NavLink className='column-link column-link--transparent' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' icon='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
<FollowRequestsNavLink />
<NavLink className='column-link column-link--transparent' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' icon='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
<NavLink className='column-link column-link--transparent' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' icon='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/timelines/direct'><Icon className='column-link__icon' icon='envelope' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' icon='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' icon='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
<ListPanel />
<hr />
<a className='column-link column-link--transparent' href='/settings/preferences' target='_blank'><Icon className='column-link__icon' icon='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>
<a className='column-link column-link--transparent' href='#' onClick={onOpenSettings}><Icon className='column-link__icon' icon='cogs' fixedWidth /><FormattedMessage id='navigation_bar.app_settings' defaultMessage='App settings' /></a>
<a className='column-link column-link--transparent' href='/relationships' target='_blank'><Icon className='column-link__icon' icon='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>
{!!profile_directory && <a className='column-link column-link--transparent' href='/explore'><Icon className='column-link__icon' id='address-book-o' fixedWidth /><FormattedMessage id='navigation_bar.profile_directory' defaultMessage='Profile directory' /></a>}
</div>
);
export default withRouter(NavigationPanel);

View File

@@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import IconWithBadge from 'flavours/glitch/components/icon_with_badge';
const mapStateToProps = state => ({
count: state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0,
id: 'bell',
});
export default connect(mapStateToProps)(IconWithBadge);

View File

@@ -4,40 +4,16 @@ import { NavLink, withRouter } from 'react-router-dom';
import { FormattedMessage, injectIntl } from 'react-intl';
import { debounce } from 'lodash';
import { isUserTouching } from 'flavours/glitch/util/is_mobile';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
unreadNotifications: state.getIn(['notifications', 'unread']),
showBadge: state.getIn(['local_settings', 'notifications', 'tab_badge']),
});
@connect(mapStateToProps)
class NotificationsIcon extends React.PureComponent {
static propTypes = {
unreadNotifications: PropTypes.number,
showBadge: PropTypes.bool,
};
render() {
const { unreadNotifications, showBadge } = this.props;
return (
<span className='icon-badge-wrapper'>
<i className='fa fa-fw fa-bell' />
{ showBadge && unreadNotifications > 0 && <div className='icon-badge' />}
</span>
);
}
}
import NotificationsCounterIcon from './notifications_counter_icon';
export const links = [
<NavLink className='tabs-bar__link primary' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
<NavLink className='tabs-bar__link primary' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
<NavLink className='tabs-bar__link' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
<NavLink className='tabs-bar__link secondary' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
<NavLink className='tabs-bar__link secondary' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
<NavLink className='tabs-bar__link primary' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><i className='fa fa-fw fa-search' /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
<NavLink className='tabs-bar__link primary' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><i className='fa fa-fw fa-bars' /></NavLink>,
<NavLink className='tabs-bar__link' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><i className='fa fa-fw fa-users' /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
<NavLink className='tabs-bar__link' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><i className='fa fa-fw fa-globe' /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><i className='fa fa-fw fa-search' /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
<NavLink className='tabs-bar__link' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><i className='fa fa-fw fa-bars' /></NavLink>,
];
export function getIndex (path) {

View File

@@ -1,9 +1,18 @@
import { connect } from 'react-redux';
import ColumnsArea from '../components/columns_area';
import { openModal } from 'flavours/glitch/actions/modal';
const mapStateToProps = state => ({
columns: state.getIn(['settings', 'columns']),
swipeToChangeColumns: state.getIn(['local_settings', 'swipe_to_change_columns']),
});
export default connect(mapStateToProps, null, null, { forwardRef: true })(ColumnsArea);
const mapDispatchToProps = dispatch => ({
openSettings (e) {
e.preventDefault();
e.stopPropagation();
dispatch(openModal('SETTINGS', {}));
},
});
export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(ColumnsArea);

View File

@@ -2,7 +2,6 @@ import React from 'react';
import NotificationsContainer from './containers/notifications_container';
import PropTypes from 'prop-types';
import LoadingBarContainer from './containers/loading_bar_container';
import TabsBar from './components/tabs_bar';
import ModalContainer from './containers/modal_container';
import { connect } from 'react-redux';
import { Redirect, withRouter } from 'react-router-dom';
@@ -45,6 +44,7 @@ import {
Mutes,
PinnedStatuses,
Lists,
Search,
GettingStartedMisc,
} from 'flavours/glitch/util/async-components';
import { HotKeys } from 'react-hotkeys';
@@ -270,19 +270,6 @@ export default class UI extends React.Component {
};
}
shouldComponentUpdate (nextProps) {
if (nextProps.navbarUnder !== this.props.navbarUnder) {
// Avoid expensive update just to toggle a class
this.node.classList.toggle('navbar-under', nextProps.navbarUnder);
return false;
}
// Why isn't this working?!?
// return super.shouldComponentUpdate(nextProps, nextState);
return true;
}
componentDidUpdate (prevProps) {
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
this.columnsAreaNode.handleChildrenContentChange();
@@ -320,7 +307,7 @@ export default class UI extends React.Component {
handleHotkeyNew = e => {
e.preventDefault();
const element = this.node.querySelector('.composer--textarea textarea');
const element = this.node.querySelector('.compose-form__autosuggest-wrapper textarea');
if (element) {
element.focus();
@@ -330,7 +317,7 @@ export default class UI extends React.Component {
handleHotkeySearch = e => {
e.preventDefault();
const element = this.node.querySelector('.drawer--search input');
const element = this.node.querySelector('.search__input');
if (element) {
element.focus();
@@ -432,6 +419,8 @@ export default class UI extends React.Component {
render () {
const { width, draggingOver } = this.state;
const { children, layout, isWide, navbarUnder, dropdownMenuIsOpen } = this.props;
const singleColumn = isMobile(width, layout);
const redirect = singleColumn ? <Redirect from='/' to='/timelines/home' exact /> : <Redirect from='/' to='/getting-started' exact />;
const columnsClass = layout => {
switch (layout) {
@@ -475,11 +464,9 @@ export default class UI extends React.Component {
return (
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
<div className={className} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
{navbarUnder ? null : (<TabsBar />)}
<ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={isMobile(width, layout)}>
<ColumnsAreaContainer ref={this.setColumnsAreaRef} singleColumn={singleColumn} navbarUnder={navbarUnder}>
<WrappedSwitch>
<Redirect from='/' to='/getting-started' exact />
{redirect}
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
@@ -493,7 +480,7 @@ export default class UI extends React.Component {
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
<WrappedRoute path='/search' component={Compose} content={children} componentParams={{ isSearchPage: true }} />
<WrappedRoute path='/search' component={Search} content={children} />
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} />
@@ -518,7 +505,6 @@ export default class UI extends React.Component {
</ColumnsAreaContainer>
<NotificationsContainer />
{navbarUnder ? (<TabsBar />) : null}
<LoadingBarContainer className='loading-bar' />
<ModalContainer />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />

View File

@@ -5,7 +5,7 @@ import { fromJS, is } from 'immutable';
import { throttle } from 'lodash';
import classNames from 'classnames';
import { isFullscreen, requestFullscreen, exitFullscreen } from 'flavours/glitch/util/fullscreen';
import { displayMedia } from 'flavours/glitch/util/initial_state';
import { displayMedia, useBlurhash } from 'flavours/glitch/util/initial_state';
import { decode } from 'blurhash';
const messages = defineMessages({
@@ -312,7 +312,7 @@ export default class Video extends React.PureComponent {
}
_decode () {
if (!this.canvas) return;
if (!this.canvas || !useBlurhash) return;
const hash = this.props.blurhash;
const pixels = decode(hash, 32, 32);

View File

@@ -442,6 +442,7 @@ export default function compose(state = initialState, action) {
map.set('focusDate', new Date());
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
map.set('sensitive', action.status.get('sensitive'));
if (action.status.get('spoiler_text').length > 0) {
map.set('spoiler', true);

View File

@@ -0,0 +1,102 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import {
CONVERSATIONS_MOUNT,
CONVERSATIONS_UNMOUNT,
CONVERSATIONS_FETCH_REQUEST,
CONVERSATIONS_FETCH_SUCCESS,
CONVERSATIONS_FETCH_FAIL,
CONVERSATIONS_UPDATE,
CONVERSATIONS_READ,
} from '../actions/conversations';
import compareId from 'flavours/glitch/util/compare_id';
const initialState = ImmutableMap({
items: ImmutableList(),
isLoading: false,
hasMore: true,
mounted: 0,
});
const conversationToMap = item => ImmutableMap({
id: item.id,
unread: item.unread,
accounts: ImmutableList(item.accounts.map(a => a.id)),
last_status: item.last_status ? item.last_status.id : null,
});
const updateConversation = (state, item) => state.update('items', list => {
const index = list.findIndex(x => x.get('id') === item.id);
const newItem = conversationToMap(item);
if (index === -1) {
return list.unshift(newItem);
} else {
return list.set(index, newItem);
}
});
const expandNormalizedConversations = (state, conversations, next, isLoadingRecent) => {
let items = ImmutableList(conversations.map(conversationToMap));
return state.withMutations(mutable => {
if (!items.isEmpty()) {
mutable.update('items', list => {
list = list.map(oldItem => {
const newItemIndex = items.findIndex(x => x.get('id') === oldItem.get('id'));
if (newItemIndex === -1) {
return oldItem;
}
const newItem = items.get(newItemIndex);
items = items.delete(newItemIndex);
return newItem;
});
list = list.concat(items);
return list.sortBy(x => x.get('last_status'), (a, b) => {
if(a === null || b === null) {
return -1;
}
return compareId(a, b) * -1;
});
});
}
if (!next && !isLoadingRecent) {
mutable.set('hasMore', false);
}
mutable.set('isLoading', false);
});
};
export default function conversations(state = initialState, action) {
switch (action.type) {
case CONVERSATIONS_FETCH_REQUEST:
return state.set('isLoading', true);
case CONVERSATIONS_FETCH_FAIL:
return state.set('isLoading', false);
case CONVERSATIONS_FETCH_SUCCESS:
return expandNormalizedConversations(state, action.conversations, action.next, action.isLoadingRecent);
case CONVERSATIONS_UPDATE:
return updateConversation(state, action.conversation);
case CONVERSATIONS_MOUNT:
return state.update('mounted', count => count + 1);
case CONVERSATIONS_UNMOUNT:
return state.update('mounted', count => count - 1);
case CONVERSATIONS_READ:
return state.update('items', list => list.map(item => {
if (item.get('id') === action.id) {
return item.set('unread', false);
}
return item;
}));
default:
return state;
}
};

View File

@@ -28,6 +28,7 @@ import lists from './lists';
import listEditor from './list_editor';
import listAdder from './list_adder';
import filters from './filters';
import conversations from './conversations';
import suggestions from './suggestions';
import pinnedAccountsEditor from './pinned_accounts_editor';
import polls from './polls';
@@ -64,6 +65,7 @@ const reducers = {
listEditor,
listAdder,
filters,
conversations,
suggestions,
pinnedAccountsEditor,
polls,

View File

@@ -15,6 +15,7 @@ const initialState = ImmutableMap({
show_reply_count : false,
always_show_spoilers_field: false,
confirm_missing_media_description: false,
confirm_boost_missing_media_description: false,
confirm_before_clearing_draft: true,
preselect_on_reply: true,
inline_preview_cards: true,

View File

@@ -27,7 +27,7 @@ import compareId from 'flavours/glitch/util/compare_id';
const initialState = ImmutableMap({
items: ImmutableList(),
hasMore: true,
top: true,
top: false,
mounted: 0,
unread: 0,
lastReadId: '0',

Some files were not shown because too many files have changed in this diff Show More