From 1accc7597830296e41ac02d2e05cf2532f12b775 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 16 Apr 2022 07:58:10 +0200 Subject: [PATCH 001/166] Change favicon to catcatnya logo --- public/android-chrome-192x192.png | Bin 13534 -> 20129 bytes public/apple-touch-icon.png | Bin 4880 -> 18084 bytes public/browserconfig.xml | 2 +- public/favicon.ico | Bin 9086 -> 15406 bytes public/mstile-150x150.png | Bin 4523 -> 5327 bytes 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png index 7a99a19641acd1558c8416b16c16a814fd953491..f90e21adee9e1a1aac639fbe6b341e71a7751aae 100644 GIT binary patch literal 20129 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 UgVQz<4FCWD07*qoM6N<$f?1SCU;qFB literal 13534 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 or$%(~|6j$7AfEAaB@#ecUPG={#vA68~NO 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 DExVKj literal 4880 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 diff --git a/public/browserconfig.xml b/public/browserconfig.xml index 7fdab5058e..6edeb2178e 100644 --- a/public/browserconfig.xml +++ b/public/browserconfig.xml @@ -3,7 +3,7 @@ - #282c37 + #de5833 diff --git a/public/favicon.ico b/public/favicon.ico index 79000c9cc43c2044b5ab5129202e993aa2cf70af..a017d8c52e870bc882988fe8bfcca29369723aa1 100644 GIT binary patch literal 15406 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 literal 9086 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 Date: Fri, 22 Apr 2022 18:21:41 +0200 Subject: [PATCH 002/166] Revert "Swap position of media attachments and polls" This reverts commit bb12af7250c9368905bae7d91c6ff0b06f3aa400. I do not like that commit - I am used to the buttons being this way. Besides, this would deviate from vanilla Mastodon needlessly. --- app/javascript/flavours/glitch/components/status.js | 10 ++++------ .../features/status/components/detailed_status.js | 9 ++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 21f0e3a6f1..02ff9ab28e 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -581,7 +581,10 @@ class Status extends ImmutablePureComponent { // backgrounds for collapsed statuses are enabled. attachments = status.get('media_attachments'); - + if (status.get('poll')) { + media.push(); + mediaIcons.push('tasks'); + } if (usingPiP) { media.push(); mediaIcons.push('video-camera'); @@ -681,11 +684,6 @@ class Status extends ImmutablePureComponent { mediaIcons.push('link'); } - if (status.get('poll')) { - media.push(); - mediaIcons.push('tasks'); - } - // Here we prepare extra data-* attributes for CSS selectors. // Users can use those for theming, hiding avatars etc via UserStyle const selectorAttribs = { diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index f4e6c24c54..528d2eb737 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -134,6 +134,10 @@ class DetailedStatus extends ImmutablePureComponent { outerStyle.height = `${this.state.height}px`; } + if (status.get('poll')) { + media.push(); + mediaIcons.push('tasks'); + } if (usingPiP) { media.push(); mediaIcons.push('video-camera'); @@ -198,11 +202,6 @@ class DetailedStatus extends ImmutablePureComponent { mediaIcons.push('link'); } - if (status.get('poll')) { - media.push(); - mediaIcons.push('tasks'); - } - if (status.get('application')) { applicationLink = · {status.getIn(['application', 'name'])}; } From 95751c93c216f2b72510d5c8a120c1c6acf95df5 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 27 Apr 2022 23:24:21 +0200 Subject: [PATCH 003/166] Adjust file status component in glitch flavour to match upstream --- app/javascript/flavours/glitch/components/status.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 02ff9ab28e..21f0e3a6f1 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -581,10 +581,7 @@ class Status extends ImmutablePureComponent { // backgrounds for collapsed statuses are enabled. attachments = status.get('media_attachments'); - if (status.get('poll')) { - media.push(); - mediaIcons.push('tasks'); - } + if (usingPiP) { media.push(); mediaIcons.push('video-camera'); @@ -684,6 +681,11 @@ class Status extends ImmutablePureComponent { mediaIcons.push('link'); } + if (status.get('poll')) { + media.push(); + mediaIcons.push('tasks'); + } + // Here we prepare extra data-* attributes for CSS selectors. // Users can use those for theming, hiding avatars etc via UserStyle const selectorAttribs = { From 57ccec8b03e31b7511068106bf96506f0c347cbf Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 27 Apr 2022 23:25:28 +0200 Subject: [PATCH 004/166] Adjust detailed_status component to match upstream --- .../glitch/features/status/components/detailed_status.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index 528d2eb737..f4e6c24c54 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -134,10 +134,6 @@ class DetailedStatus extends ImmutablePureComponent { outerStyle.height = `${this.state.height}px`; } - if (status.get('poll')) { - media.push(); - mediaIcons.push('tasks'); - } if (usingPiP) { media.push(); mediaIcons.push('video-camera'); @@ -202,6 +198,11 @@ class DetailedStatus extends ImmutablePureComponent { mediaIcons.push('link'); } + if (status.get('poll')) { + media.push(); + mediaIcons.push('tasks'); + } + if (status.get('application')) { applicationLink = · {status.getIn(['application', 'name'])}; } From c89c2df34dbf60dfff19e7eafba21f718a5bf7ce Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 27 Apr 2022 23:48:11 +0200 Subject: [PATCH 005/166] Add info about this fork --- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 17 ++++++++--------- README.md | 23 ++++++++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index cc0a7f51b0..4529bd2d82 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -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 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. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jeremy@kescher.at. 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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5b5513ff1..1fe0cb39f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,25 +1,24 @@ -# Contributing to Mastodon Glitch Edition # +# Contributing to Mastodon catcatnya edition # -Thank you for your interest in contributing to the `glitch-soc` project! -Here are some guidelines, and ways you can help. +Thank you for your interest in contributing to a fork of the `glitch-soc` project! +Before you do anything here, please check if you can contribute to either the [vanilla Mastodon project](https://github.com/mastodon/mastodon) or [glitch-soc](https://github.com/glitch-soc/mastodon) first. +If you still decide to contribute here instead, here are some guidelines, and ways you can help. > (This document is a bit of a work-in-progress, so please bear with us. > If you don't see what you're looking for here, please don't hesitate to reach out!) ## Planning ## -Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects. -We're working on ways to improve the planning structure and better solicit feedback, and if you feel like you can help in this respect, feel free to give us a holler. +Right now a lot of the planning for this project takes place... nowhere. Actually, just contact me via Matrix - contact info can be found on [my personal website](https://kescher.at). ## Documentation ## -The documentation for this repository is available at [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)). -Right now, we've mostly focused on the features that make this fork different from upstream in some manner. -Adding screenshots, improving descriptions, and so forth are all ways to help contribute to the project even if you don't know any code. +Unlike glitch-soc, which has [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)), this repo only documents things in a README. Sorry. +Right now, we've mostly focused on the features that make this fork different from upstream, which, may I remind you, is already a fork, in some manner. ## Frontend Development ## -Check out [the documentation here](https://glitch-soc.github.io/docs/contributing/frontend/) for more information. +Check out [the documentation here](https://glitch-soc.github.io/docs/contributing/frontend/) for more information on this topic. We'll be following that a bit. ## Backend Development ## diff --git a/README.md b/README.md index 256f2d2def..d08f25740d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,19 @@ -# Mastodon Glitch Edition # +# Mastodon catcatnya~ edition -> Now with automated deploys! +## Introduction -[![Build Status](https://img.shields.io/circleci/project/github/glitch-soc/mastodon.svg)][circleci] -[![Code Climate](https://img.shields.io/codeclimate/maintainability/glitch-soc/mastodon.svg)][code_climate] +This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github.com/glitch-soc/mastodon). -[circleci]: https://circleci.com/gh/glitch-soc/mastodon -[code_climate]: https://codeclimate.com/github/glitch-soc/mastodon +- To install, take a look at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/). The instructions and features are the same, except for the differences outlined below. +- Contributing guidelines are available [here](CONTRIBUTING.md). -So here's the deal: we all work on this code, and anyone who uses that does so absolutely at their own risk. can you dig it? +## Differences -- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/). -- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/). +- The favicon files are adjusted specifically for the catcatnya instance. Specifically, these: + - public/android-chrome-192x192.png + - public/apple-touch-icon.png + - public/browserconfig.xml + - public/favicon.ico + - public/mstile-150x150.png + You might want to revert these to the upstream files manually. +- TODO: Make more differences From cc157b3de9ffa68cbdd823735790b9a9baa71a1c Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 27 Apr 2022 23:49:01 +0200 Subject: [PATCH 006/166] Slight README.md edit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d08f25740d..273e83f194 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,6 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. - public/browserconfig.xml - public/favicon.ico - public/mstile-150x150.png + You might want to revert these to the upstream files manually. - TODO: Make more differences From 5f3376afe5d8241c71d3240cd97d78895cedfadd Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 1 May 2022 14:44:26 +0200 Subject: [PATCH 007/166] Change sound effects --- public/sounds/boop.mp3 | Bin 12280 -> 7459 bytes public/sounds/boop.ogg | Bin 5247 -> 7790 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/public/sounds/boop.mp3 b/public/sounds/boop.mp3 index bf9c3c1aaff40dea76ad82e3e853d2d32382bcea..2489e19db6ddfd17dfdd1174ccb8cacf771efd6b 100644 GIT binary patch literal 7459 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 literal 12280 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 diff --git a/public/sounds/boop.ogg b/public/sounds/boop.ogg index a6551c9fdd0ae0727e44cf7e1baf1ac3f5000cba..78df190256a903e33051f7b1893e95da3a59d746 100644 GIT binary patch delta 4597 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 delta 2035 zcmYk73pCW*9>@2LnLN&va88B6h?tbg#29248jO`!jLQ2NM#C7S4EhhD6d@-`N90{f zr@SI>JyeH~SKf~t@*1zGJD4->y=&e3-D|J4zx(ssYyI}xd;NZk`h2X6wXd%ofP??a zZ`H7CXPJ8}@>{A54%+cBfj9ete!nM;|9^@W3S$O)(nEF&sD($E4}pjy9>?J|G?0P~ z1;I42I%!QYu_XzhmjLSi8)6uA&;Ohg6NJ#rX%;3VPbM=+M&OKWY36n`650O(Ilv<% zldAka?kI~NOm;xvv%-#&=JD9U3F9>eA3Dkv%{98@}@rV?@I?&AZn&{kaq2TBUy ztm|(7Z^%WNxn;wSrw?s13}fNi-~qi$%ESlJC`$$^Uq|v7N%PhBr0`iH*h3;;KCVb^ zZ(Pp0r&j#|N*$8ZI^a_m*CX2URGQF|I$l1)y4D1<4V#W|+U6co!MDowoBMvA8ew5i zM<+C91yCYgs*;v_QW;VbnWr@)Uvt(HP|3uVTQQZ1boUAEhbI!fn~Huhop1QVlt#aOZoA%x92|Ttr!)9zY?=d1s1T$(b<7|7BD0&)a`3biS zUG&J@9czs9&3Sge+K2-h`!-J1p)X_;W`9&f`$g6kkBnEq6Uiu5u$ zHV9d@->ug#ca5~jv=dw3bq0VLys~=>3LI04OOZf<^1+3$SHp!-cE(FvP_~tDie!*5 zne;Y`SpO>Q{FDcCCzDaqL`T>7lv$@VteagM?#N;CC(dZI>IT+iAW$D=7ki{9h50MC zRKMCaz99Rg`i1+R0d)5*;vqf0_-ECtNt1fWfj-8aYBeV!kz)NrZNq_lR1|NSY0jAY zS@wsezXjG0>{BJJ7Jcn`qnY|wx$v2JF@GU|CP{LvfVQ9U;8h7*1PJ2LM|ENC82~g^ zt66n+9513?H-k!f+_UeVbbhN4L|L=sR2Z`k`a8Y6iv37H4uG<{G819>s3 zs=2~T_%Rm4mf9UP6vKfMwPjTWVI6>H_XUs zi`o!pOs6A$lVq2OY9TJE3gSpvOSK^0=R?BZf-ObHO>YFg-y}$-;Vp$={LQfcNs-vC{(dH}mPFx)_w| z#3b_r%NZ+MyjoEHoyzcnvs<$|POW@DZ2d<~V7mnXB;AF8 z79ffmD2ZrL0Pt_48#0reODezXNTzuuvbd8vx6rWU`{g?oa!eggtAb!-lR?1!%6nIB zIB3+kG#aC)_41M!vi)HlVrQ!t)(W1)O)HtApaj{wK@nV7VOdCxRq z*t;#5V%SUHhlB!3zV!YSM+{wqae`uM-1T7Cro7Gdt*7O8{B(rJ_rqF^rv|l$FqdSg zDEZ5xe)gCm=Jmw&o?T-SZPbSzQeyJy`rcbEx8I2%GvZ0NlrFjm18_TdO;P!rA54*o zI&{+TX-oMFBq*+nym(al9X_Lw-aOu!Qu2~FI$1|N3oS26>f|}=4EJijNRWAzn~374 z*EC5DRiXAq%_mUV^n!hNU^PnOBI#zj)_Xxj;O6!b?TlK^n68wxL%Pyd!W9K|IS@()2I-sJCvG)@t_3iSf`=1`qX>MD G?)e8jZJ3Gx From 81aea995e3f1f592d21bdc7fe7f5d977cb271147 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 1 May 2022 15:49:54 +0200 Subject: [PATCH 008/166] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 273e83f194..9bbb730640 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,13 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. ## Differences -- The favicon files are adjusted specifically for the catcatnya instance. Specifically, these: +- Some files are adjusted specifically for the catcatnya instance. Specifically, these: - public/android-chrome-192x192.png - public/apple-touch-icon.png - public/browserconfig.xml - public/favicon.ico - public/mstile-150x150.png + - sounds/boop.mp3 + - sounds/boop.ogg You might want to revert these to the upstream files manually. -- TODO: Make more differences From ae6b9a6a6911098892b8b7ca177fb7f98e9b35a5 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 1 May 2022 23:50:18 +0200 Subject: [PATCH 009/166] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9bbb730640..a8bbc43dfa 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. - sounds/boop.mp3 - sounds/boop.ogg - You might want to revert these to the upstream files manually. + You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. From 8d34a2c2f53e5de16afa86813596acfd59f69933 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 2 May 2022 01:12:39 +0200 Subject: [PATCH 010/166] Change emoji picker icon to :blobCat:. --- .../flavours/glitch/features/emoji_picker/index.js | 5 +++-- .../features/compose/components/emoji_picker_dropdown.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js index 5de9fe107f..91cef70dd5 100644 --- a/app/javascript/flavours/glitch/features/emoji_picker/index.js +++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js @@ -471,9 +471,10 @@ class EmojiPickerDropdown extends React.PureComponent {
{button || 🙂} + {/* TODO: Fetch :blobCat: From custom_emojis directly */}
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index f433e4de9f..062470a4f1 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -389,9 +389,10 @@ class EmojiPickerDropdown extends React.PureComponent {
{button || 🙂} + {/* TODO: Fetch :blobCat: From custom_emojis directly */}
From 0495ac2651f4c4dd18ac281a4ccc8c564b943ab4 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 5 May 2022 18:14:40 +0200 Subject: [PATCH 011/166] Fix incorrect upload size limit at /admin/custom_emojis/new in glitch-soc --- app/views/admin/custom_emojis/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/custom_emojis/new.html.haml b/app/views/admin/custom_emojis/new.html.haml index 95996dec86..1ea931a2fa 100644 --- a/app/views/admin/custom_emojis/new.html.haml +++ b/app/views/admin/custom_emojis/new.html.haml @@ -7,7 +7,7 @@ .fields-group = f.input :shortcode, wrapper: :with_label, label: t('admin.custom_emojis.shortcode'), hint: t('admin.custom_emojis.shortcode_hint') .fields-group - = f.input :image, wrapper: :with_label, input_html: { accept: CustomEmoji::IMAGE_MIME_TYPES.join(' ') }, hint: t('admin.custom_emojis.image_hint', size: number_to_human_size(CustomEmoji::LIMIT)) + = f.input :image, wrapper: :with_label, input_html: { accept: CustomEmoji::IMAGE_MIME_TYPES.join(' ') }, hint: t('admin.custom_emojis.image_hint', size: number_to_human_size(CustomEmoji::LOCAL_LIMIT)) .actions = f.button :button, t('admin.custom_emojis.upload'), type: :submit From bdccddd09bbc3e53886667d4b5c99221ea1c9160 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 18 May 2022 22:18:14 +0200 Subject: [PATCH 012/166] Rename fork to Catstodon --- CONTRIBUTING.md | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1fe0cb39f6..2c73438b46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to Mastodon catcatnya edition # +# Contributing to Catstodon # Thank you for your interest in contributing to a fork of the `glitch-soc` project! Before you do anything here, please check if you can contribute to either the [vanilla Mastodon project](https://github.com/mastodon/mastodon) or [glitch-soc](https://github.com/glitch-soc/mastodon) first. @@ -9,7 +9,7 @@ If you still decide to contribute here instead, here are some guidelines, and wa ## Planning ## -Right now a lot of the planning for this project takes place... nowhere. Actually, just contact me via Matrix - contact info can be found on [my personal website](https://kescher.at). +Right now a lot of the planning for this project takes place... in my head. Actually, just contact me via Matrix - contact info can be found on [my personal website](https://kescher.at). You can also contribute via GitHub or kescherGit, if you have an account at either. ## Documentation ## diff --git a/README.md b/README.md index a8bbc43dfa..2d7e9d5861 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mastodon catcatnya~ edition +# Catstodon ## Introduction @@ -17,5 +17,5 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. - public/mstile-150x150.png - sounds/boop.mp3 - sounds/boop.ogg - - You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. + You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. +- The web frontend emoji picker is a blobcat instead of the joy emoji. From ee2a8d5a73b4f49f053decf74c6f637b8241beed Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 18 May 2022 22:38:40 +0200 Subject: [PATCH 013/166] README: Fix sentence after unordered list --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d7e9d5861..f324fc8530 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,6 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. - public/mstile-150x150.png - sounds/boop.mp3 - sounds/boop.ogg - You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. + + You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. From fd4c3dbd4b99b2951908eae939c822c23d94116e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 24 May 2022 18:04:24 +0200 Subject: [PATCH 014/166] Upgrade Ruby to 3.0.4 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 75a22a26ac..b0f2dcb32f 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.0.3 +3.0.4 From 9076a7bdd43fb20e53821729f7dcac88eac1eeaa Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 24 May 2022 21:22:48 +0200 Subject: [PATCH 015/166] Revert "Change algorithm of `tootctl search deploy` to improve performance (#18463)" This reverts commit a9b64b24d6c076cb96a66307c07d4f0158dc07da. Reverted commit introduced a regression already reported at https://github.com/glitch-soc/mastodon/issues/1781. --- app/chewy/accounts_index.rb | 6 +- app/chewy/statuses_index.rb | 2 - app/chewy/tags_index.rb | 8 +- app/lib/importer/accounts_index_importer.rb | 30 ----- app/lib/importer/base_importer.rb | 87 -------------- app/lib/importer/statuses_index_importer.rb | 89 -------------- app/lib/importer/tags_index_importer.rb | 26 ----- app/models/trends/history.rb | 20 ++-- lib/mastodon/search_cli.rb | 123 ++++++++++++++------ 9 files changed, 100 insertions(+), 291 deletions(-) delete mode 100644 app/lib/importer/accounts_index_importer.rb delete mode 100644 app/lib/importer/base_importer.rb delete mode 100644 app/lib/importer/statuses_index_importer.rb delete mode 100644 app/lib/importer/tags_index_importer.rb diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index e38e14a106..763958a3f9 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -23,7 +23,7 @@ class AccountsIndex < Chewy::Index }, } - index_scope ::Account.searchable.includes(:account_stat) + index_scope ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } root date_detection: false do field :id, type: 'long' @@ -36,8 +36,8 @@ class AccountsIndex < Chewy::Index field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' end - field :following_count, type: 'long', value: ->(account) { account.following_count } - field :followers_count, type: 'long', value: ->(account) { account.followers_count } + field :following_count, type: 'long', value: ->(account) { account.following.local.count } + field :followers_count, type: 'long', value: ->(account) { account.followers.local.count } field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at } end end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 6dd4fb18b0..c200098799 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -33,8 +33,6 @@ class StatusesIndex < Chewy::Index }, } - # We do not use delete_if option here because it would call a method that we - # expect to be called with crutches without crutches, causing n+1 queries index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) crutch :mentions do |collection| diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb index df3d9e4cce..a5b139bcaa 100644 --- a/app/chewy/tags_index.rb +++ b/app/chewy/tags_index.rb @@ -23,11 +23,7 @@ class TagsIndex < Chewy::Index }, } - index_scope ::Tag.listable - - crutch :time_period do - 7.days.ago.to_date..0.days.ago.to_date - end + index_scope ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } root date_detection: false do field :name, type: 'text', analyzer: 'content' do @@ -35,7 +31,7 @@ class TagsIndex < Chewy::Index end field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } - field :usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts } + field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day.accounts } } field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } end end diff --git a/app/lib/importer/accounts_index_importer.rb b/app/lib/importer/accounts_index_importer.rb deleted file mode 100644 index 792a31b1bd..0000000000 --- a/app/lib/importer/accounts_index_importer.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class Importer::AccountsIndexImporter < Importer::BaseImporter - def import! - scope.includes(:account_stat).find_in_batches(batch_size: @batch_size) do |tmp| - in_work_unit(tmp) do |accounts| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: accounts).bulk_body - - indexed = bulk.select { |entry| entry[:index] }.size - deleted = bulk.select { |entry| entry[:delete] }.size - - Chewy::Index::Import::BulkRequest.new(index).perform(bulk) - - [indexed, deleted] - end - end - - wait! - end - - private - - def index - AccountsIndex - end - - def scope - Account.searchable - end -end diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb deleted file mode 100644 index ea522c600c..0000000000 --- a/app/lib/importer/base_importer.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -class Importer::BaseImporter - # @param [Integer] batch_size - # @param [Concurrent::ThreadPoolExecutor] executor - def initialize(batch_size:, executor:) - @batch_size = batch_size - @executor = executor - @wait_for = Concurrent::Set.new - end - - # Callback to run when a concurrent work unit completes - # @param [Proc] - def on_progress(&block) - @on_progress = block - end - - # Callback to run when a concurrent work unit fails - # @param [Proc] - def on_failure(&block) - @on_failure = block - end - - # Reduce resource usage during and improve speed of indexing - def optimize_for_import! - Chewy.client.indices.put_settings index: index.index_name, body: { index: { refresh_interval: -1 } } - end - - # Restore original index settings - def optimize_for_search! - Chewy.client.indices.put_settings index: index.index_name, body: { index: { refresh_interval: index.settings_hash[:settings][:index][:refresh_interval] } } - end - - # Estimate the amount of documents that would be indexed. Not exact! - # @returns [Integer] - def estimate! - ActiveRecord::Base.connection_pool.with_connection { |connection| connection.select_one("SELECT reltuples AS estimate FROM pg_class WHERE relname = '#{index.adapter.target.table_name}'")['estimate'].to_i } - end - - # Import data from the database into the index - def import! - raise NotImplementedError - end - - # Remove documents from the index that no longer exist in the database - def clean_up! - index.scroll_batches do |documents| - ids = documents.map { |doc| doc['_id'] } - existence_map = index.adapter.target.where(id: ids).pluck(:id).each_with_object({}) { |id, map| map[id.to_s] = true } - tmp = ids.reject { |id| existence_map[id] } - - next if tmp.empty? - - in_work_unit(tmp) do |deleted_ids| - bulk = Chewy::Index::Import::BulkBuilder.new(index, delete: deleted_ids).bulk_body - - Chewy::Index::Import::BulkRequest.new(index).perform(bulk) - - [0, bulk.size] - end - end - - wait! - end - - protected - - def in_work_unit(*args, &block) - work_unit = Concurrent::Promises.future_on(@executor, *args, &block) - - work_unit.on_fulfillment!(&@on_progress) - work_unit.on_rejection!(&@on_failure) - work_unit.on_resolution! { @wait_for.delete(work_unit) } - - @wait_for << work_unit - rescue Concurrent::RejectedExecutionError - sleep(0.1) && retry # Backpressure - end - - def wait! - Concurrent::Promises.zip(*@wait_for).wait - end - - def index - raise NotImplementedError - end -end diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb deleted file mode 100644 index 7c65325600..0000000000 --- a/app/lib/importer/statuses_index_importer.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -class Importer::StatusesIndexImporter < Importer::BaseImporter - def import! - # The idea is that instead of iterating over all statuses in the database - # and calculating the searchable_by for each of them (majority of which - # would be empty), we approach the index from the other end - - scopes.each do |scope| - # We could be tempted to keep track of status IDs we have already processed - # from a different scope to avoid indexing them multiple times, but that - # could end up being a very large array - - scope.find_in_batches(batch_size: @batch_size) do |tmp| - in_work_unit(tmp.map(&:status_id)) do |status_ids| - bulk = ActiveRecord::Base.connection_pool.with_connection do - Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body - end - - indexed = 0 - deleted = 0 - - # We can't use the delete_if proc to do the filtering because delete_if - # is called before rendering the data and we need to filter based - # on the results of the filter, so this filtering happens here instead - bulk.map! do |entry| - new_entry = begin - if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank? - { delete: entry[:index].except(:data) } - else - entry - end - end - - if new_entry[:index] - indexed += 1 - else - deleted += 1 - end - - new_entry - end - - Chewy::Index::Import::BulkRequest.new(index).perform(bulk) - - [indexed, deleted] - end - end - end - - wait! - end - - private - - def index - StatusesIndex - end - - def scopes - [ - local_statuses_scope, - local_mentions_scope, - local_favourites_scope, - local_votes_scope, - local_bookmarks_scope, - ] - end - - def local_mentions_scope - Mention.where(account: Account.local, silent: false).select(:id, :status_id) - end - - def local_favourites_scope - Favourite.where(account: Account.local).select(:id, :status_id) - end - - def local_bookmarks_scope - Bookmark.select(:id, :status_id) - end - - def local_votes_scope - Poll.joins(:votes).where(votes: { account: Account.local }).select('polls.id, polls.status_id') - end - - def local_statuses_scope - Status.local.select('id, coalesce(reblog_of_id, id) as status_id') - end -end diff --git a/app/lib/importer/tags_index_importer.rb b/app/lib/importer/tags_index_importer.rb deleted file mode 100644 index f5bd8f052b..0000000000 --- a/app/lib/importer/tags_index_importer.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -class Importer::TagsIndexImporter < Importer::BaseImporter - def import! - index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| - in_work_unit(tmp) do |tags| - bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: tags).bulk_body - - indexed = bulk.select { |entry| entry[:index] }.size - deleted = bulk.select { |entry| entry[:delete] }.size - - Chewy::Index::Import::BulkRequest.new(index).perform(bulk) - - [indexed, deleted] - end - end - - wait! - end - - private - - def index - TagsIndex - end -end diff --git a/app/models/trends/history.rb b/app/models/trends/history.rb index 74723e35c9..608e337924 100644 --- a/app/models/trends/history.rb +++ b/app/models/trends/history.rb @@ -11,11 +11,11 @@ class Trends::History end def uses - with_redis { |redis| redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum } + redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum end def accounts - with_redis { |redis| redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) } + redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) end end @@ -33,21 +33,19 @@ class Trends::History attr_reader :day def accounts - with_redis { |redis| redis.pfcount(key_for(:accounts)) } + redis.pfcount(key_for(:accounts)) end def uses - with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 } + redis.get(key_for(:uses))&.to_i || 0 end def add(account_id) - with_redis do |redis| - redis.pipelined do |pipeline| - pipeline.incrby(key_for(:uses), 1) - pipeline.pfadd(key_for(:accounts), account_id) - pipeline.expire(key_for(:uses), EXPIRE_AFTER) - pipeline.expire(key_for(:accounts), EXPIRE_AFTER) - end + redis.pipelined do + redis.incrby(key_for(:uses), 1) + redis.pfadd(key_for(:accounts), account_id) + redis.expire(key_for(:uses), EXPIRE_AFTER) + redis.expire(key_for(:accounts), EXPIRE_AFTER) end end diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb index b579ebc143..74f980ba11 100644 --- a/lib/mastodon/search_cli.rb +++ b/lib/mastodon/search_cli.rb @@ -16,21 +16,19 @@ module Mastodon StatusesIndex, ].freeze - option :concurrency, type: :numeric, default: 5, aliases: [:c], desc: 'Workload will be split between this number of threads' - option :batch_size, type: :numeric, default: 100, aliases: [:b], desc: 'Number of records in each batch' + option :concurrency, type: :numeric, default: 2, aliases: [:c], desc: 'Workload will be split between this number of threads' + option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch' option :only, type: :array, enum: %w(accounts tags statuses), desc: 'Only process these indices' - option :import, type: :boolean, default: true, desc: 'Import data from the database to the index' - option :clean, type: :boolean, default: true, desc: 'Remove outdated documents from the index' desc 'deploy', 'Create or upgrade Elasticsearch indices and populate them' long_desc <<~LONG_DESC If Elasticsearch is empty, this command will create the necessary indices and then import data from the database into those indices. This command will also upgrade indices if the underlying schema has been - changed since the last run. Index upgrades erase index data. + changed since the last run. Even if creating or upgrading indices is not necessary, data from the - database will be imported into the indices, unless overriden with --no-import. + database will be imported into the indices. LONG_DESC def deploy if options[:concurrency] < 1 @@ -51,9 +49,7 @@ module Mastodon end end - pool = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10) - importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool) } - progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false) + progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false) # First, ensure all indices are created and have the correct # structure, so that live data can already be written @@ -63,46 +59,99 @@ module Mastodon index.specification.lock! end - progress.title = 'Estimating workload ' - progress.total = indices.sum { |index| importers[index].estimate! } - reset_connection_pools! - added = 0 - removed = 0 + pool = Concurrent::FixedThreadPool.new(options[:concurrency]) + added = Concurrent::AtomicFixnum.new(0) + removed = Concurrent::AtomicFixnum.new(0) + progress.title = 'Estimating workload ' + + # Estimate the amount of data that has to be imported first + progress.total = indices.sum { |index| index.adapter.default_scope.count } + + # Now import all the actual data. Mind that unlike chewy:sync, we don't + # fetch and compare all record IDs from the database and the index to + # find out which to add and which to remove from the index. Because with + # potentially millions of rows, the memory footprint of such a calculation + # is uneconomical. So we only ever add. indices.each do |index| - importer = importers[index] - importer.optimize_for_import! + progress.title = "Importing #{index} " + batch_size = options[:batch_size] + slice_size = (batch_size / options[:concurrency]).ceil - importer.on_progress do |(indexed, deleted)| - progress.total = nil if progress.progress + indexed + deleted > progress.total - progress.progress += indexed + deleted - added += indexed - removed += deleted - end + index.adapter.default_scope.reorder(nil).find_in_batches(batch_size: batch_size) do |batch| + futures = [] - importer.on_failure do |reason| - progress.log(pastel.red("Error while importing #{index}: #{reason}")) - end + batch.each_slice(slice_size) do |records| + futures << Concurrent::Future.execute(executor: pool) do + begin + if !progress.total.nil? && progress.progress + records.size > progress.total + # The number of items has changed between start and now, + # since there is no good way to predict the final count from + # here, just change the progress bar to an indeterminate one - if options[:import] - progress.title = "Importing #{index} " - importer.import! - end + progress.total = nil + end - if options[:clean] - progress.title = "Cleaning #{index} " - importer.clean_up! + grouped_records = nil + bulk_body = nil + index_count = 0 + delete_count = 0 + + ActiveRecord::Base.connection_pool.with_connection do + grouped_records = records.to_a.group_by do |record| + index.adapter.send(:delete_from_index?, record) ? :delete : :to_index + end + + bulk_body = Chewy::Index::Import::BulkBuilder.new(index, **grouped_records).bulk_body + end + + index_count = grouped_records[:to_index].size if grouped_records.key?(:to_index) + delete_count = grouped_records[:delete].size if grouped_records.key?(:delete) + + # The following is an optimization for statuses specifically, since + # we want to de-index statuses that cannot be searched by anybody, + # but can't use Chewy's delete_if logic because it doesn't use + # crutches and our searchable_by logic depends on them + if index == StatusesIndex + bulk_body.map! do |entry| + if entry[:to_index] && entry.dig(:to_index, :data, 'searchable_by').blank? + index_count -= 1 + delete_count += 1 + + { delete: entry[:to_index].except(:data) } + else + entry + end + end + end + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk_body) + + progress.progress += records.size + + added.increment(index_count) + removed.increment(delete_count) + + sleep 1 + rescue => e + progress.log pastel.red("Error importing #{index}: #{e}") + ensure + RedisConfiguration.pool.checkin if Thread.current[:redis] + Thread.current[:redis] = nil + end + end + end + + futures.map(&:value) end - ensure - importer.optimize_for_search! end - progress.title = 'Done! ' - progress.finish + progress.title = '' + progress.stop - say("Indexed #{added} records, de-indexed #{removed}", :green, true) + say("Indexed #{added.value} records, de-indexed #{removed.value}", :green, true) end end end From 22699c98077e67971046e7110985f0f161f8c897 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 26 May 2022 20:57:29 +0200 Subject: [PATCH 016/166] Revert reverting "Change algorithm of `tootctl search deploy` to improve performance (#18463)". This reverts commit 9076a7bdd43fb20e53821729f7dcac88eac1eeaa, since the issue should be fixed by 088dc0ec5a383006952c0b15508af882a4c1109c. --- app/chewy/accounts_index.rb | 6 +- app/chewy/statuses_index.rb | 2 + app/chewy/tags_index.rb | 8 +- app/lib/importer/accounts_index_importer.rb | 30 +++++ app/lib/importer/base_importer.rb | 87 +++++++++++++ app/lib/importer/statuses_index_importer.rb | 89 ++++++++++++++ app/lib/importer/tags_index_importer.rb | 26 ++++ app/models/trends/history.rb | 20 +-- lib/mastodon/search_cli.rb | 129 ++++++-------------- 9 files changed, 294 insertions(+), 103 deletions(-) create mode 100644 app/lib/importer/accounts_index_importer.rb create mode 100644 app/lib/importer/base_importer.rb create mode 100644 app/lib/importer/statuses_index_importer.rb create mode 100644 app/lib/importer/tags_index_importer.rb diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 763958a3f9..e38e14a106 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -23,7 +23,7 @@ class AccountsIndex < Chewy::Index }, } - index_scope ::Account.searchable.includes(:account_stat), delete_if: ->(account) { account.destroyed? || !account.searchable? } + index_scope ::Account.searchable.includes(:account_stat) root date_detection: false do field :id, type: 'long' @@ -36,8 +36,8 @@ class AccountsIndex < Chewy::Index field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content' end - field :following_count, type: 'long', value: ->(account) { account.following.local.count } - field :followers_count, type: 'long', value: ->(account) { account.followers.local.count } + field :following_count, type: 'long', value: ->(account) { account.following_count } + field :followers_count, type: 'long', value: ->(account) { account.followers_count } field :last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at } end end diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index c200098799..6dd4fb18b0 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -33,6 +33,8 @@ class StatusesIndex < Chewy::Index }, } + # We do not use delete_if option here because it would call a method that we + # expect to be called with crutches without crutches, causing n+1 queries index_scope ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) crutch :mentions do |collection| diff --git a/app/chewy/tags_index.rb b/app/chewy/tags_index.rb index a5b139bcaa..df3d9e4cce 100644 --- a/app/chewy/tags_index.rb +++ b/app/chewy/tags_index.rb @@ -23,7 +23,11 @@ class TagsIndex < Chewy::Index }, } - index_scope ::Tag.listable, delete_if: ->(tag) { tag.destroyed? || !tag.listable? } + index_scope ::Tag.listable + + crutch :time_period do + 7.days.ago.to_date..0.days.ago.to_date + end root date_detection: false do field :name, type: 'text', analyzer: 'content' do @@ -31,7 +35,7 @@ class TagsIndex < Chewy::Index end field :reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? } - field :usage, type: 'long', value: ->(tag) { tag.history.reduce(0) { |total, day| total + day.accounts } } + field :usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts } field :last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at } end end diff --git a/app/lib/importer/accounts_index_importer.rb b/app/lib/importer/accounts_index_importer.rb new file mode 100644 index 0000000000..792a31b1bd --- /dev/null +++ b/app/lib/importer/accounts_index_importer.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Importer::AccountsIndexImporter < Importer::BaseImporter + def import! + scope.includes(:account_stat).find_in_batches(batch_size: @batch_size) do |tmp| + in_work_unit(tmp) do |accounts| + bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: accounts).bulk_body + + indexed = bulk.select { |entry| entry[:index] }.size + deleted = bulk.select { |entry| entry[:delete] }.size + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [indexed, deleted] + end + end + + wait! + end + + private + + def index + AccountsIndex + end + + def scope + Account.searchable + end +end diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb new file mode 100644 index 0000000000..ea522c600c --- /dev/null +++ b/app/lib/importer/base_importer.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +class Importer::BaseImporter + # @param [Integer] batch_size + # @param [Concurrent::ThreadPoolExecutor] executor + def initialize(batch_size:, executor:) + @batch_size = batch_size + @executor = executor + @wait_for = Concurrent::Set.new + end + + # Callback to run when a concurrent work unit completes + # @param [Proc] + def on_progress(&block) + @on_progress = block + end + + # Callback to run when a concurrent work unit fails + # @param [Proc] + def on_failure(&block) + @on_failure = block + end + + # Reduce resource usage during and improve speed of indexing + def optimize_for_import! + Chewy.client.indices.put_settings index: index.index_name, body: { index: { refresh_interval: -1 } } + end + + # Restore original index settings + def optimize_for_search! + Chewy.client.indices.put_settings index: index.index_name, body: { index: { refresh_interval: index.settings_hash[:settings][:index][:refresh_interval] } } + end + + # Estimate the amount of documents that would be indexed. Not exact! + # @returns [Integer] + def estimate! + ActiveRecord::Base.connection_pool.with_connection { |connection| connection.select_one("SELECT reltuples AS estimate FROM pg_class WHERE relname = '#{index.adapter.target.table_name}'")['estimate'].to_i } + end + + # Import data from the database into the index + def import! + raise NotImplementedError + end + + # Remove documents from the index that no longer exist in the database + def clean_up! + index.scroll_batches do |documents| + ids = documents.map { |doc| doc['_id'] } + existence_map = index.adapter.target.where(id: ids).pluck(:id).each_with_object({}) { |id, map| map[id.to_s] = true } + tmp = ids.reject { |id| existence_map[id] } + + next if tmp.empty? + + in_work_unit(tmp) do |deleted_ids| + bulk = Chewy::Index::Import::BulkBuilder.new(index, delete: deleted_ids).bulk_body + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [0, bulk.size] + end + end + + wait! + end + + protected + + def in_work_unit(*args, &block) + work_unit = Concurrent::Promises.future_on(@executor, *args, &block) + + work_unit.on_fulfillment!(&@on_progress) + work_unit.on_rejection!(&@on_failure) + work_unit.on_resolution! { @wait_for.delete(work_unit) } + + @wait_for << work_unit + rescue Concurrent::RejectedExecutionError + sleep(0.1) && retry # Backpressure + end + + def wait! + Concurrent::Promises.zip(*@wait_for).wait + end + + def index + raise NotImplementedError + end +end diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb new file mode 100644 index 0000000000..7c65325600 --- /dev/null +++ b/app/lib/importer/statuses_index_importer.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +class Importer::StatusesIndexImporter < Importer::BaseImporter + def import! + # The idea is that instead of iterating over all statuses in the database + # and calculating the searchable_by for each of them (majority of which + # would be empty), we approach the index from the other end + + scopes.each do |scope| + # We could be tempted to keep track of status IDs we have already processed + # from a different scope to avoid indexing them multiple times, but that + # could end up being a very large array + + scope.find_in_batches(batch_size: @batch_size) do |tmp| + in_work_unit(tmp.map(&:status_id)) do |status_ids| + bulk = ActiveRecord::Base.connection_pool.with_connection do + Chewy::Index::Import::BulkBuilder.new(index, to_index: Status.includes(:media_attachments, :preloadable_poll).where(id: status_ids)).bulk_body + end + + indexed = 0 + deleted = 0 + + # We can't use the delete_if proc to do the filtering because delete_if + # is called before rendering the data and we need to filter based + # on the results of the filter, so this filtering happens here instead + bulk.map! do |entry| + new_entry = begin + if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank? + { delete: entry[:index].except(:data) } + else + entry + end + end + + if new_entry[:index] + indexed += 1 + else + deleted += 1 + end + + new_entry + end + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [indexed, deleted] + end + end + end + + wait! + end + + private + + def index + StatusesIndex + end + + def scopes + [ + local_statuses_scope, + local_mentions_scope, + local_favourites_scope, + local_votes_scope, + local_bookmarks_scope, + ] + end + + def local_mentions_scope + Mention.where(account: Account.local, silent: false).select(:id, :status_id) + end + + def local_favourites_scope + Favourite.where(account: Account.local).select(:id, :status_id) + end + + def local_bookmarks_scope + Bookmark.select(:id, :status_id) + end + + def local_votes_scope + Poll.joins(:votes).where(votes: { account: Account.local }).select('polls.id, polls.status_id') + end + + def local_statuses_scope + Status.local.select('id, coalesce(reblog_of_id, id) as status_id') + end +end diff --git a/app/lib/importer/tags_index_importer.rb b/app/lib/importer/tags_index_importer.rb new file mode 100644 index 0000000000..f5bd8f052b --- /dev/null +++ b/app/lib/importer/tags_index_importer.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class Importer::TagsIndexImporter < Importer::BaseImporter + def import! + index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp| + in_work_unit(tmp) do |tags| + bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: tags).bulk_body + + indexed = bulk.select { |entry| entry[:index] }.size + deleted = bulk.select { |entry| entry[:delete] }.size + + Chewy::Index::Import::BulkRequest.new(index).perform(bulk) + + [indexed, deleted] + end + end + + wait! + end + + private + + def index + TagsIndex + end +end diff --git a/app/models/trends/history.rb b/app/models/trends/history.rb index 608e337924..74723e35c9 100644 --- a/app/models/trends/history.rb +++ b/app/models/trends/history.rb @@ -11,11 +11,11 @@ class Trends::History end def uses - redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum + with_redis { |redis| redis.mget(*@days.map { |day| day.key_for(:uses) }).map(&:to_i).sum } end def accounts - redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) + with_redis { |redis| redis.pfcount(*@days.map { |day| day.key_for(:accounts) }) } end end @@ -33,19 +33,21 @@ class Trends::History attr_reader :day def accounts - redis.pfcount(key_for(:accounts)) + with_redis { |redis| redis.pfcount(key_for(:accounts)) } end def uses - redis.get(key_for(:uses))&.to_i || 0 + with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 } end def add(account_id) - redis.pipelined do - redis.incrby(key_for(:uses), 1) - redis.pfadd(key_for(:accounts), account_id) - redis.expire(key_for(:uses), EXPIRE_AFTER) - redis.expire(key_for(:accounts), EXPIRE_AFTER) + with_redis do |redis| + redis.pipelined do |pipeline| + pipeline.incrby(key_for(:uses), 1) + pipeline.pfadd(key_for(:accounts), account_id) + pipeline.expire(key_for(:uses), EXPIRE_AFTER) + pipeline.expire(key_for(:accounts), EXPIRE_AFTER) + end end end diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb index 74f980ba11..b579ebc143 100644 --- a/lib/mastodon/search_cli.rb +++ b/lib/mastodon/search_cli.rb @@ -16,19 +16,21 @@ module Mastodon StatusesIndex, ].freeze - option :concurrency, type: :numeric, default: 2, aliases: [:c], desc: 'Workload will be split between this number of threads' - option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch' + option :concurrency, type: :numeric, default: 5, aliases: [:c], desc: 'Workload will be split between this number of threads' + option :batch_size, type: :numeric, default: 100, aliases: [:b], desc: 'Number of records in each batch' option :only, type: :array, enum: %w(accounts tags statuses), desc: 'Only process these indices' + option :import, type: :boolean, default: true, desc: 'Import data from the database to the index' + option :clean, type: :boolean, default: true, desc: 'Remove outdated documents from the index' desc 'deploy', 'Create or upgrade Elasticsearch indices and populate them' long_desc <<~LONG_DESC If Elasticsearch is empty, this command will create the necessary indices and then import data from the database into those indices. This command will also upgrade indices if the underlying schema has been - changed since the last run. + changed since the last run. Index upgrades erase index data. Even if creating or upgrading indices is not necessary, data from the - database will be imported into the indices. + database will be imported into the indices, unless overriden with --no-import. LONG_DESC def deploy if options[:concurrency] < 1 @@ -49,7 +51,9 @@ module Mastodon end end - progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false) + pool = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10) + importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool) } + progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false) # First, ensure all indices are created and have the correct # structure, so that live data can already be written @@ -59,99 +63,46 @@ module Mastodon index.specification.lock! end + progress.title = 'Estimating workload ' + progress.total = indices.sum { |index| importers[index].estimate! } + reset_connection_pools! - pool = Concurrent::FixedThreadPool.new(options[:concurrency]) - added = Concurrent::AtomicFixnum.new(0) - removed = Concurrent::AtomicFixnum.new(0) + added = 0 + removed = 0 - progress.title = 'Estimating workload ' - - # Estimate the amount of data that has to be imported first - progress.total = indices.sum { |index| index.adapter.default_scope.count } - - # Now import all the actual data. Mind that unlike chewy:sync, we don't - # fetch and compare all record IDs from the database and the index to - # find out which to add and which to remove from the index. Because with - # potentially millions of rows, the memory footprint of such a calculation - # is uneconomical. So we only ever add. indices.each do |index| - progress.title = "Importing #{index} " - batch_size = options[:batch_size] - slice_size = (batch_size / options[:concurrency]).ceil + importer = importers[index] + importer.optimize_for_import! - index.adapter.default_scope.reorder(nil).find_in_batches(batch_size: batch_size) do |batch| - futures = [] - - batch.each_slice(slice_size) do |records| - futures << Concurrent::Future.execute(executor: pool) do - begin - if !progress.total.nil? && progress.progress + records.size > progress.total - # The number of items has changed between start and now, - # since there is no good way to predict the final count from - # here, just change the progress bar to an indeterminate one - - progress.total = nil - end - - grouped_records = nil - bulk_body = nil - index_count = 0 - delete_count = 0 - - ActiveRecord::Base.connection_pool.with_connection do - grouped_records = records.to_a.group_by do |record| - index.adapter.send(:delete_from_index?, record) ? :delete : :to_index - end - - bulk_body = Chewy::Index::Import::BulkBuilder.new(index, **grouped_records).bulk_body - end - - index_count = grouped_records[:to_index].size if grouped_records.key?(:to_index) - delete_count = grouped_records[:delete].size if grouped_records.key?(:delete) - - # The following is an optimization for statuses specifically, since - # we want to de-index statuses that cannot be searched by anybody, - # but can't use Chewy's delete_if logic because it doesn't use - # crutches and our searchable_by logic depends on them - if index == StatusesIndex - bulk_body.map! do |entry| - if entry[:to_index] && entry.dig(:to_index, :data, 'searchable_by').blank? - index_count -= 1 - delete_count += 1 - - { delete: entry[:to_index].except(:data) } - else - entry - end - end - end - - Chewy::Index::Import::BulkRequest.new(index).perform(bulk_body) - - progress.progress += records.size - - added.increment(index_count) - removed.increment(delete_count) - - sleep 1 - rescue => e - progress.log pastel.red("Error importing #{index}: #{e}") - ensure - RedisConfiguration.pool.checkin if Thread.current[:redis] - Thread.current[:redis] = nil - end - end - end - - futures.map(&:value) + importer.on_progress do |(indexed, deleted)| + progress.total = nil if progress.progress + indexed + deleted > progress.total + progress.progress += indexed + deleted + added += indexed + removed += deleted end + + importer.on_failure do |reason| + progress.log(pastel.red("Error while importing #{index}: #{reason}")) + end + + if options[:import] + progress.title = "Importing #{index} " + importer.import! + end + + if options[:clean] + progress.title = "Cleaning #{index} " + importer.clean_up! + end + ensure + importer.optimize_for_search! end - progress.title = '' - progress.stop + progress.title = 'Done! ' + progress.finish - say("Indexed #{added.value} records, de-indexed #{removed.value}", :green, true) + say("Indexed #{added} records, de-indexed #{removed}", :green, true) end end end From 394a536ae0726b7bb6a928af7c2980daa640123f Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 5 Jun 2022 10:15:26 +0200 Subject: [PATCH 017/166] Create distinctions between glitch-soc and Catstodon. Version suffix and string changes. --- CONTRIBUTING.md | 2 +- .../flavours/glitch/features/ui/components/link_footer.js | 3 ++- .../glitch/features/ui/components/onboarding_modal.js | 3 ++- app/javascript/flavours/glitch/locales/de.js | 3 ++- app/javascript/flavours/glitch/locales/en.js | 4 ++-- app/javascript/flavours/glitch/locales/es.js | 2 -- app/javascript/flavours/glitch/locales/ja.js | 2 -- app/javascript/flavours/glitch/locales/ko.js | 2 -- app/javascript/flavours/glitch/locales/pl.js | 2 -- app/javascript/flavours/glitch/locales/zh-CN.js | 4 +--- lib/mastodon/version.rb | 4 ++-- 11 files changed, 12 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c73438b46..6411c3d6fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ Right now a lot of the planning for this project takes place... in my head. Actu ## Documentation ## Unlike glitch-soc, which has [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)), this repo only documents things in a README. Sorry. -Right now, we've mostly focused on the features that make this fork different from upstream, which, may I remind you, is already a fork, in some manner. +Right now, we've mostly focused on the features that make this fork different from upstream, which, may I remind you, is already a fork. ## Frontend Development ## diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index d9579e9c9a..23ae2be82d 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -59,9 +59,10 @@ class LinkFooter extends React.PureComponent {

{repository} (v{version}), + glitchsoc: glitch-soc, Mastodon: Mastodon }} />

diff --git a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js index e7be62ad89..81a58e0085 100644 --- a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js @@ -144,10 +144,11 @@ const PageSix = ({ admin, domain }) => {

fork, + glitchsoc: Glitchsoc, Mastodon: Mastodon, github: GitHub, }} diff --git a/app/javascript/flavours/glitch/locales/de.js b/app/javascript/flavours/glitch/locales/de.js index ce64536235..d3fe3c90cf 100644 --- a/app/javascript/flavours/glitch/locales/de.js +++ b/app/javascript/flavours/glitch/locales/de.js @@ -1,7 +1,8 @@ import inherited from 'mastodon/locales/de.json'; const messages = { - // No translations available. + 'getting_started.open_source_notice': 'Catstodon ist quelloffene Software, zudem ein Fork von {glitchsoc}, was wiederum ein Fork von {Mastodon} ist. Du kannst auf GitHub unter {github} dazu beitragen oder Probleme melden.', + 'onboarding.page_six.github': '{domain} läuft auf Catstodon, was ein Fork ({fork}) von {glitchsoc} ist, welches wiederum ein Fork ({fork}) von {Mastodon} ist. It is fully compatible with all Mastodon apps and instances. Catstodon ist quelloffene Software. Du kannst auf GitHub unter {github} Probleme melden, nach neuen Funktionen fragen oder zum Code beitragen.', }; export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/en.js b/app/javascript/flavours/glitch/locales/en.js index 90e924d4a6..4e0ccc52d1 100644 --- a/app/javascript/flavours/glitch/locales/en.js +++ b/app/javascript/flavours/glitch/locales/en.js @@ -1,7 +1,7 @@ import inherited from 'mastodon/locales/en.json'; const messages = { - 'getting_started.open_source_notice': 'Glitchsoc is free open source software forked from {Mastodon}. You can contribute or report issues on GitHub at {github}.', + 'getting_started.open_source_notice': 'Catstodon is open source software, a friendly fork of {glitchsoc}, which in turn is a fork of {Mastodon}. You can contribute or report issues on GitHub at {github}.', 'layout.auto': 'Auto', 'layout.current_is': 'Your current layout is:', 'layout.desktop': 'Desktop', @@ -10,7 +10,7 @@ const messages = { 'getting_started.onboarding': 'Show me around', 'onboarding.page_one.federation': '{domain} is an \'instance\' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.', 'onboarding.page_one.welcome': 'Welcome to {domain}!', - 'onboarding.page_six.github': '{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.', + 'onboarding.page_six.github': '{domain} runs on Catstodon, which is a {fork} of {glitchsoc}, which, in turn, is a friendly {fork} of {Mastodon}. It is fully compatible with all Mastodon apps and instances. Catstodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.', 'settings.auto_collapse': 'Automatic collapsing', 'settings.auto_collapse_all': 'Everything', 'settings.auto_collapse_lengthy': 'Lengthy toots', diff --git a/app/javascript/flavours/glitch/locales/es.js b/app/javascript/flavours/glitch/locales/es.js index 0868738815..54d37679c3 100644 --- a/app/javascript/flavours/glitch/locales/es.js +++ b/app/javascript/flavours/glitch/locales/es.js @@ -13,7 +13,6 @@ const messages = { 'compose.attach': 'Adjuntar...', 'favourite_modal.combo': 'Puedes presionar {combo} para omitir esto la próxima vez', 'getting_started.onboarding': 'Paseo inicial', - 'getting_started.open_source_notice': 'Glitchsoc es software libre y gratuito bifurcado de {Mastodon}. Puedes contribuir o reportar errores en GitHub en {github}.', 'home.column_settings.show_direct': 'Mostrar mensajes directos', 'layout.auto': 'Automático', 'layout.current_is': 'Tu diseño actual es:', @@ -34,7 +33,6 @@ const messages = { 'notifications.marked_clear': 'Limpiar notificaciones seleccionadas', 'onboarding.page_one.federation': '{domain} es una "instancia" de Mastodon. Mastodon es una red de servidores independientes que se unen para crear una red social más grande. A estos servidores los llamamos instancias.', 'onboarding.page_one.welcome': '¡Bienvenidx a {domain}!', - 'onboarding.page_six.github': '{domain} usa Glitchsoc. Glitchsoc es una bifurcación {fork} amigable de {Mastodon}, y es compatible con cualquier instancia o aplicación de Mastodon. Glitchsoc es completamente gratuito y de código abierto. Puedes reportar errores, solicitar funciones o contribuir al código en {github}.', 'settings.always_show_spoilers_field': 'Siempre mostrar el campo de advertencia de contenido', 'settings.auto_collapse_all': 'Todo', 'settings.auto_collapse_lengthy': 'Toots largos', diff --git a/app/javascript/flavours/glitch/locales/ja.js b/app/javascript/flavours/glitch/locales/ja.js index 0ca5e5fc77..de67e182fc 100644 --- a/app/javascript/flavours/glitch/locales/ja.js +++ b/app/javascript/flavours/glitch/locales/ja.js @@ -1,7 +1,6 @@ import inherited from 'mastodon/locales/ja.json'; const messages = { - 'getting_started.open_source_notice': 'Glitchsocは{Mastodon}によるフリーなオープンソースソフトウェアです。誰でもGitHub({github})から開発に參加したり、問題を報告したりできます。', 'layout.auto': '自動', 'layout.current_is': 'あなたの現在のレイアウト:', 'layout.desktop': 'デスクトップ', @@ -11,7 +10,6 @@ const messages = { 'getting_started.onboarding': '解説を表示', 'onboarding.page_one.federation': '{domain}はMastodonのインスタンスです。Mastodonとは、独立したサーバが連携して作るソーシャルネットワークです。これらのサーバーをインスタンスと呼びます。', 'onboarding.page_one.welcome': '{domain}へようこそ!', - 'onboarding.page_six.github': '{domain}はGlitchsocを使用しています。Glitchsocは{Mastodon}のフレンドリーな{fork}で、どんなMastodonアプリやインスタンスとも互換性があります。Glitchsocは完全に無料で、オープンソースです。{github}でバグ報告や機能要望あるいは貢獻をすることが可能です。', 'settings.always_show_spoilers_field': '常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)', 'settings.auto_collapse': '自動折りたたみ', 'settings.auto_collapse_all': 'すべて', diff --git a/app/javascript/flavours/glitch/locales/ko.js b/app/javascript/flavours/glitch/locales/ko.js index a817044c1b..1c876483e3 100644 --- a/app/javascript/flavours/glitch/locales/ko.js +++ b/app/javascript/flavours/glitch/locales/ko.js @@ -52,7 +52,6 @@ const messages = { 'endorsed_accounts_editor.endorsed_accounts': '추천하는 계정들', 'favourite_modal.combo': '다음엔 {combo}를 눌러 건너뛸 수 있습니다', 'getting_started.onboarding': '둘러보기', - 'getting_started.open_source_notice': '글리치는 {Mastodon}의 자유 오픈소스 포크버전입니다. {github}에서 문제를 리포팅 하거나 기여를 할 수 있습니다.', 'home.column_settings.advanced': '고급', 'home.column_settings.filter_regex': '정규표현식으로 필터', 'home.column_settings.show_direct': 'DM 보여주기', @@ -93,7 +92,6 @@ const messages = { 'onboarding.page_six.almost_done': '거의 다 되었습니다…', 'onboarding.page_six.appetoot': '본 아페툿!', 'onboarding.page_six.apps_available': 'iOS, 안드로이드, 그리고 다른 플랫폼들을 위한 {apps}이 존재합니다.', - 'onboarding.page_six.github': '{domain}은 글리치를 통해 구동 됩니다. 글리치는 {Mastodon}의 {fork}입니다, 그리고 어떤 마스토돈 인스턴스나 앱과도 호환 됩니다. 글리치는 완전한 자유 오픈소스입니다. {github}에서 버그를 리포팅 하거나, 기능을 제안하거나, 코드를 기여할 수 있습니다.', 'onboarding.page_six.guidelines': '커뮤니티 가이드라인', 'onboarding.page_six.read_guidelines': '{domain}의 {guidelines}을 읽어주세요!', 'onboarding.page_six.various_app': '모바일 앱', diff --git a/app/javascript/flavours/glitch/locales/pl.js b/app/javascript/flavours/glitch/locales/pl.js index f430bf5770..3808997833 100644 --- a/app/javascript/flavours/glitch/locales/pl.js +++ b/app/javascript/flavours/glitch/locales/pl.js @@ -1,7 +1,6 @@ import inherited from 'mastodon/locales/pl.json'; const messages = { - 'getting_started.open_source_notice': 'Glitchsoc jest wolnym i otwartoźródłowym forkiem oprogramowania {Mastodon}. Możesz współtworzyć projekt lub zgłaszać błędy na GitHubie pod adresem {github}.', 'layout.auto': 'Automatyczny', 'layout.current_is': 'Twój obecny układ to:', 'layout.desktop': 'Desktopowy', @@ -11,7 +10,6 @@ const messages = { 'getting_started.onboarding': 'Rozejrzyj się', 'onboarding.page_one.federation': '{domain} jest \'instancją\' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.', 'onboarding.page_one.welcome': 'Witamy na {domain}!', - 'onboarding.page_six.github': '{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.', 'settings.auto_collapse': 'Automatyczne zwijanie', 'settings.auto_collapse_all': 'Wszystko', 'settings.auto_collapse_lengthy': 'Długie wpisy', diff --git a/app/javascript/flavours/glitch/locales/zh-CN.js b/app/javascript/flavours/glitch/locales/zh-CN.js index 21a68fc01b..7b754ebcec 100644 --- a/app/javascript/flavours/glitch/locales/zh-CN.js +++ b/app/javascript/flavours/glitch/locales/zh-CN.js @@ -53,7 +53,6 @@ const messages = { 'endorsed_accounts_editor.endorsed_accounts': '推荐用户', 'favourite_modal.combo': '下次你可以按 {combo} 跳过这个', 'getting_started.onboarding': '参观一下', - 'getting_started.open_source_notice': 'Glitchsoc 是由 {Mastodon} 分叉出来的免费开源软件。你可以在 GitHub 上贡献或报告问题,地址是 {github}。', 'home.column_settings.advanced': '高级', 'home.column_settings.filter_regex': '按正则表达式过滤', 'home.column_settings.show_direct': '显示私信', @@ -94,7 +93,6 @@ const messages = { 'onboarding.page_six.almost_done': '就快完成了...', 'onboarding.page_six.appetoot': '尽情享用吧!', 'onboarding.page_six.apps_available': '有适用于 iOS、Android 和其他平台的应用程序。', - 'onboarding.page_six.github': '{domain} 在 Glitchsoc 上运行。Glitchsoc 是 {Mastodon} 的一个友好 {fork},与任何 Mastodon 实例或应用兼容。Glitchsoc 是完全免费和开源的。你可以在 {github} 上报告错误、请求功能或贡献代码。', 'onboarding.page_six.guidelines': '社区准则', 'onboarding.page_six.read_guidelines': '请阅读 {domain} 的 {guidelines}!', 'onboarding.page_six.various_app': '应用程序', @@ -198,4 +196,4 @@ const messages = { 'web_app_crash.title': '抱歉,Mastodon 出了点问题。', }; -export default Object.assign({}, inherited, messages); \ No newline at end of file +export default Object.assign({}, inherited, messages); diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 2d0b283230..819858c74b 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -21,7 +21,7 @@ module Mastodon end def suffix - '+glitch' + '+catstodon' end def to_a @@ -33,7 +33,7 @@ module Mastodon end def repository - ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon') + ENV.fetch('GITHUB_REPOSITORY', 'kescherCode/catstodon') end def source_base_url From 4eecee4bfc8bf411405a0bb6648cf1f4b76d4385 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 5 Jun 2022 12:41:15 +0200 Subject: [PATCH 018/166] GitHub Actions: Update build-image URL --- .github/workflows/build-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 880fdfac94..bfc2910efb 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -26,7 +26,7 @@ jobs: - uses: docker/metadata-action@v3 id: meta with: - images: ghcr.io/${{ github.repository_owner }}/mastodon + images: ghcr.io/${{ github.repository_owner }}/catstodon flavor: | latest=true tags: | @@ -39,5 +39,5 @@ jobs: platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} - cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/mastodon:latest + cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/catstodon:latest cache-to: type=inline From b9d9981ae45e913a1e1366d4ced61cabcf02c043 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 5 Jun 2022 15:10:38 +0200 Subject: [PATCH 019/166] GitHub Actions: Remove build-image workflow We don't need to build an image, so let's remove the failing workflow altogether. --- .github/workflows/build-image.yml | 43 ------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/build-image.yml diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml deleted file mode 100644 index bfc2910efb..0000000000 --- a/.github/workflows/build-image.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build container image -on: - workflow_dispatch: - push: - branches: - - 'main' - tags: - - '*' - pull_request: - paths: - - .github/workflows/build-image.yml - - Dockerfile -jobs: - build-image: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: docker/setup-qemu-action@v1 - - uses: docker/setup-buildx-action@v1 - - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - if: github.event_name != 'pull_request' - - uses: docker/metadata-action@v3 - id: meta - with: - images: ghcr.io/${{ github.repository_owner }}/catstodon - flavor: | - latest=true - tags: | - type=edge,branch=main - type=match,pattern=v(.*),group=0 - type=ref,event=pr - - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/catstodon:latest - cache-to: type=inline From f427ff2487f43a3f1257e47f7be463ea79736f2e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 5 Jun 2022 23:15:24 +0200 Subject: [PATCH 020/166] Change default page title --- config/settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.yml b/config/settings.yml index bb2720ec24..3be8b6bc3c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -2,7 +2,7 @@ # important settings can be changed from the admin interface. defaults: &defaults - site_title: 'Mastodon Glitch Edition' + site_title: 'Catstodon' site_short_description: '' site_description: '' site_extended_description: '' From d189e671c6ee64212e840d7795732924bf0d8117 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 6 Jun 2022 10:08:25 +0200 Subject: [PATCH 021/166] Make rate limits a bit less strict --- app/lib/rate_limiter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/rate_limiter.rb b/app/lib/rate_limiter.rb index 0e2c9a8943..c1db3c7737 100644 --- a/app/lib/rate_limiter.rb +++ b/app/lib/rate_limiter.rb @@ -6,12 +6,12 @@ class RateLimiter FAMILIES = { follows: { limit: 400, - period: 24.hours.freeze, + period: 12.hours.freeze, }.freeze, statuses: { limit: 300, - period: 3.hours.freeze, + period: 1.hours.freeze, }.freeze, reports: { From a6d7063be9c4b3314b59cf8a9e936467f063d3f6 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 12 Jun 2022 23:18:03 +0200 Subject: [PATCH 022/166] Make rate limits even less strict --- app/lib/rate_limiter.rb | 2 +- config/initializers/rack_attack.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/lib/rate_limiter.rb b/app/lib/rate_limiter.rb index c1db3c7737..e9b7722f8a 100644 --- a/app/lib/rate_limiter.rb +++ b/app/lib/rate_limiter.rb @@ -11,7 +11,7 @@ class RateLimiter statuses: { limit: 300, - period: 1.hours.freeze, + period: 1.hour.freeze, }.freeze, reports: { diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 745eb5d3bf..3fb22c6321 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -46,7 +46,7 @@ class Rack::Attack IpBlock.blocked?(req.remote_ip) end - throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req| + throttle('throttle_authenticated_api', limit: 600, period: 5.minutes) do |req| req.authenticated_user_id if req.api_request? end @@ -54,11 +54,11 @@ class Rack::Attack req.remote_ip if req.api_request? && req.unauthenticated? end - throttle('throttle_api_media', limit: 30, period: 30.minutes) do |req| + throttle('throttle_api_media', limit: 100, period: 30.minutes) do |req| req.authenticated_user_id if req.post? && req.path.match?('^/api/v\d+/media') end - throttle('throttle_media_proxy', limit: 30, period: 10.minutes) do |req| + throttle('throttle_media_proxy', limit: 200, period: 10.minutes) do |req| req.remote_ip if req.path.start_with?('/media_proxy') end @@ -66,7 +66,7 @@ class Rack::Attack req.remote_ip if req.post? && req.path == '/api/v1/accounts' end - throttle('throttle_authenticated_paging', limit: 300, period: 15.minutes) do |req| + throttle('throttle_authenticated_paging', limit: 1000, period: 15.minutes) do |req| req.authenticated_user_id if req.paging_request? end @@ -77,7 +77,7 @@ class Rack::Attack API_DELETE_REBLOG_REGEX = /\A\/api\/v1\/statuses\/[\d]+\/unreblog/.freeze API_DELETE_STATUS_REGEX = /\A\/api\/v1\/statuses\/[\d]+/.freeze - throttle('throttle_api_delete', limit: 30, period: 30.minutes) do |req| + throttle('throttle_api_delete', limit: 30, period: 5.minutes) do |req| req.authenticated_user_id if (req.post? && req.path.match?(API_DELETE_REBLOG_REGEX)) || (req.delete? && req.path.match?(API_DELETE_STATUS_REGEX)) end From 970a44a364ffa709c3714d927d7ad0db7eaeede0 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 14 Jun 2022 00:19:45 +0200 Subject: [PATCH 023/166] Update modified files in README --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index f324fc8530..5ac60cd7ac 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,7 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. ## Differences -- Some files are adjusted specifically for the catcatnya instance. Specifically, these: - - public/android-chrome-192x192.png - - public/apple-touch-icon.png - - public/browserconfig.xml - - public/favicon.ico - - public/mstile-150x150.png +- Some files are adjusted specifically for the CatCatNya~ instance. Specifically, these: - sounds/boop.mp3 - sounds/boop.ogg From 0f889d38285273c0eefdd0099508b767dbd0fa19 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 14 Jun 2022 00:20:43 +0200 Subject: [PATCH 024/166] Update blobCat emoji URL in emoji picker --- app/javascript/flavours/glitch/features/emoji_picker/index.js | 2 +- .../features/compose/components/emoji_picker_dropdown.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js index 91cef70dd5..036a515f6c 100644 --- a/app/javascript/flavours/glitch/features/emoji_picker/index.js +++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js @@ -472,7 +472,7 @@ class EmojiPickerDropdown extends React.PureComponent { {button || blobCat emoji} {/* TODO: Fetch :blobCat: From custom_emojis directly */} diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index 062470a4f1..c1419e1785 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -390,7 +390,7 @@ class EmojiPickerDropdown extends React.PureComponent { {button || blobCat emoji} {/* TODO: Fetch :blobCat: From custom_emojis directly */} From 06b9e14a03ebbb1b29414a7e755d12b48edf7ea8 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 23 Jun 2022 00:08:40 +0200 Subject: [PATCH 025/166] Enable editing in web interface --- README.md | 1 + app/javascript/flavours/glitch/components/status_action_bar.js | 2 +- .../flavours/glitch/features/status/components/action_bar.js | 2 +- app/javascript/mastodon/components/status_action_bar.js | 2 +- .../mastodon/features/status/components/action_bar.js | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5ac60cd7ac..aad804bc78 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,4 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. +- Editing posts is enabled in the web frontend (thanks, meave [for the hint](https://toot.site/@meave/108515761669028663)). diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 0a5c5b69d4..1a8e94dac3 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -229,7 +229,7 @@ class StatusActionBar extends ImmutablePureComponent { } if (writtenByMe) { - // menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); + menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js index a67a045da9..33ff0ce7e6 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.js +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js @@ -172,7 +172,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); menu.push(null); - // menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); + menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index 1d8fe23dae..04d232be11 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -261,7 +261,7 @@ class StatusActionBar extends ImmutablePureComponent { } if (writtenByMe) { - // menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); + menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index edaff959e6..d98b687cb5 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -215,7 +215,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); menu.push(null); - // menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); + menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { From 97d03110ed58fbc8294b3be38fd34afc4b88594c Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 27 Jun 2022 00:09:17 +0200 Subject: [PATCH 026/166] Allow modifying IP retention period To allow IP addresses to not be stored for an entire year. This attempts to fix https://github.com/mastodon/mastodon/issues/6474 for Catstodon. --- .env.production.sample | 8 ++++++++ app/workers/scheduler/ip_cleanup_scheduler.rb | 2 +- config/sidekiq.yml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 0df0a87786..5bf4d9f272 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -286,6 +286,14 @@ MAX_POLL_OPTION_CHARS=100 MAX_EMOJI_SIZE=51200 MAX_REMOTE_EMOJI_SIZE=204800 +# IP retention period +# If undefined, the value of 31556952 (1 average year) will be used. +# Keep in mind that this is period will not be completely accurate +# and may have a delay of up to 24 hours on vanilla and glitch-soc Mastodon +# due to the scheduling of ip_cleanup_scheduler in sidekiq.yml. +# Time unit in seconds +IP_RETENTION_PERIOD=31556952 + # Optional hCaptcha support # HCAPTCHA_SECRET_KEY= # HCAPTCHA_SITE_KEY= diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 7afad2f581..5a4a7bc2fe 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -3,7 +3,7 @@ class Scheduler::IpCleanupScheduler include Sidekiq::Worker - IP_RETENTION_PERIOD = 1.year.freeze + IP_RETENTION_PERIOD = ENV.fetch('IP_RETENTION_PERIOD', 1.year).to_i.freeze sidekiq_options retry: 0 diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 2a38714684..71a90e0acf 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -46,7 +46,7 @@ class: Scheduler::UserCleanupScheduler queue: scheduler ip_cleanup_scheduler: - cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *' + every: '1h' class: Scheduler::IpCleanupScheduler queue: scheduler email_scheduler: From 3f0ce808674406780408faabb14205cea9811d3c Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 27 Jun 2022 00:11:33 +0200 Subject: [PATCH 027/166] Add redacted CatCatNya~ production env file. Also add redaction script. --- .env.production.catcatnya | 43 +++++++++++++++++++++++++++++++++++++++ redact-env.bash | 27 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .env.production.catcatnya create mode 100755 redact-env.bash diff --git a/.env.production.catcatnya b/.env.production.catcatnya new file mode 100644 index 0000000000..6d572f5310 --- /dev/null +++ b/.env.production.catcatnya @@ -0,0 +1,43 @@ +LOCAL_DOMAIN=catcatnya.com +ALTERNATE_DOMAINS=0.catcatnya.com,1.catcatnya.com,2.catcatnya.com,3.catcatnya.com,4.catcatnya.com,5.catcatnya.com,6.catcatnya.com,7.catcatnya.com,8.catcatnya.com,9.catcatnya.com +SINGLE_USER_MODE=false +SECRET_KEY_BASE=[REDACTED] +OTP_SECRET=[REDACTED] +VAPID_PRIVATE_KEY=[REDACTED] +VAPID_PUBLIC_KEY=[REDACTED] +DB_HOST=[REDACTED] +DB_PORT=[REDACTED] +DB_NAME=[REDACTED] +DB_USER=[REDACTED] +DB_PASS=[REDACTED] +REDIS_HOST=[REDACTED] +REDIS_PORT=[REDACTED] +REDIS_PASSWORD=[REDACTED] +S3_ENABLED=false +PAPERCLIP_ROOT_PATH=[REDACTED] +PAPERCLIP_ROOT_URL=https://cdn.catcatnya.com +SMTP_SERVER=smtp.kescher.at +SMTP_PORT=[REDACTED] +SMTP_LOGIN=[REDACTED] +SMTP_PASSWORD=[REDACTED] +SMTP_AUTH_METHOD=[REDACTED] +SMTP_OPENSSL_VERIFY_MODE=[REDACTED] +SMTP_FROM_ADDRESS='Mastodon ' +ES_ENABLED=true +ES_HOST=[REDACTED] +ES_PORT=[REDACTED] +ES_PREFIX=[REDACTED] +AUTHORIZED_FETCH=true +RAILS_SERVE_STATIC_FILES=false +RAILS_LOG_LEVEL=warn + +MAX_TOOT_CHARS=6942 +MAX_DESCRIPTION_CHARS=6942 +MAX_BIO_CHARS=6942 +MAX_PROFILE_FIELDS=10 +MAX_PINNED_TOOTS=10 +MAX_DISPLAY_NAME_CHARS=50 +MAX_POLL_OPTIONS=20 +MAX_SEARCH_RESULTS=1000 +MAX_REMOTE_EMOJI_SIZE=1048576 +IP_RETENTION_PERIOD=86400 diff --git a/redact-env.bash b/redact-env.bash new file mode 100755 index 0000000000..0249f3fb06 --- /dev/null +++ b/redact-env.bash @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +if [[ -f "$1" ]]; then + sed -e '/^#.*$/d' \ + -e 's/^SECRET_KEY_BASE=.*/SECRET_KEY_BASE=[REDACTED]/gi' \ + -e 's/^OTP_SECRET=.*/OTP_SECRET=[REDACTED]/gi' \ + -e 's/^VAPID_PRIVATE_KEY=.*/VAPID_PRIVATE_KEY=[REDACTED]/gi' \ + -e 's/^VAPID_PUBLIC_KEY=.*/VAPID_PUBLIC_KEY=[REDACTED]/gi' \ + -e 's/^DB_HOST=.*/DB_HOST=[REDACTED]/gi' \ + -e 's/^DB_PORT=.*/DB_PORT=[REDACTED]/gi' \ + -e 's/^DB_NAME=.*/DB_NAME=[REDACTED]/gi' \ + -e 's/^DB_USER=.*/DB_USER=[REDACTED]/gi' \ + -e 's/^DB_PASS=.*/DB_PASS=[REDACTED]/gi' \ + -e 's/^REDIS_HOST=.*/REDIS_HOST=[REDACTED]/gi' \ + -e 's/^REDIS_PORT=.*/REDIS_PORT=[REDACTED]/gi' \ + -e 's/^REDIS_PASSWORD=.*/REDIS_PASSWORD=[REDACTED]/gi' \ + -e 's/^PAPERCLIP_ROOT_PATH=.*/PAPERCLIP_ROOT_PATH=[REDACTED]/gi' \ + -e 's/^SMTP_PORT=.*/SMTP_PORT=[REDACTED]/gi' \ + -e 's/^SMTP_LOGIN=.*/SMTP_LOGIN=[REDACTED]/gi' \ + -e 's/^SMTP_PASSWORD=.*/SMTP_PASSWORD=[REDACTED]/gi' \ + -e 's/^SMTP_AUTH_METHOD=.*/SMTP_AUTH_METHOD=[REDACTED]/gi' \ + -e 's/^SMTP_OPENSSL_VERIFY_MODE=.*/SMTP_OPENSSL_VERIFY_MODE=[REDACTED]/gi' \ + -e 's/^ES_HOST=.*/ES_HOST=[REDACTED]/gi' \ + -e 's/^ES_PORT=.*/ES_PORT=[REDACTED]/gi' \ + -e 's/^ES_PREFIX=.*/ES_PREFIX=[REDACTED]/gi' \ + "$1" +fi From 1afaba96395a3955eaa3b5c5861d49ebf51f6710 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 27 Jun 2022 00:19:56 +0200 Subject: [PATCH 028/166] Clarify IP_RETENTION_PERIOD caveats in .env.production.sample --- .env.production.sample | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 5bf4d9f272..03b2dd4739 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -288,9 +288,8 @@ MAX_REMOTE_EMOJI_SIZE=204800 # IP retention period # If undefined, the value of 31556952 (1 average year) will be used. -# Keep in mind that this is period will not be completely accurate -# and may have a delay of up to 24 hours on vanilla and glitch-soc Mastodon -# due to the scheduling of ip_cleanup_scheduler in sidekiq.yml. +# You should also change the scheduling (by default, every 24 hours) of ip_cleanup_scheduler in config/sidekiq.yml +# if you change this value to avoid IP addresses being kept for longer than intended. # Time unit in seconds IP_RETENTION_PERIOD=31556952 From c3b6f263b399bfc9275c905b6eb8c8cee0c17412 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 27 Jun 2022 00:28:39 +0200 Subject: [PATCH 029/166] Fix "undefined method `ago` for 86400:Integer" --- app/workers/scheduler/ip_cleanup_scheduler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 5a4a7bc2fe..7a7b787dd7 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -3,7 +3,7 @@ class Scheduler::IpCleanupScheduler include Sidekiq::Worker - IP_RETENTION_PERIOD = ENV.fetch('IP_RETENTION_PERIOD', 1.year).to_i.freeze + IP_RETENTION_PERIOD = ENV.fetch('IP_RETENTION_PERIOD', 1.year).to_i.seconds.freeze sidekiq_options retry: 0 From 223daf9f878eb711a5c41c664f9caea5c86e2cdb Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 3 Jul 2022 08:36:59 +0200 Subject: [PATCH 030/166] Update README.md Mention that IP address retention/cleanup intervals are now configurable, and that there is a bug regarding this. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aad804bc78..516cb4dee1 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,4 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. - Editing posts is enabled in the web frontend (thanks, meave [for the hint](https://toot.site/@meave/108515761669028663)). +- The duration of retention of IP addresses was made configurable. Unfortunately, the retention of IP addresses is currently coupled to the lifetime of sessions. Decoupling this is currently being worked on. From c5e0fac0889ef4763652eb490c4927828029620e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 3 Jul 2022 08:48:35 +0200 Subject: [PATCH 031/166] ip_cleanup_scheduler: Stop destroying sessions, replace IP with nil instead --- app/workers/scheduler/ip_cleanup_scheduler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 7a7b787dd7..0787dd2c59 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -15,7 +15,7 @@ class Scheduler::IpCleanupScheduler private def clean_ip_columns! - SessionActivation.where('updated_at < ?', IP_RETENTION_PERIOD.ago).in_batches.destroy_all + SessionActivation.where('updated_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(ip: nil) User.where('current_sign_in_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(sign_up_ip: nil) LoginActivity.where('created_at < ?', IP_RETENTION_PERIOD.ago).in_batches.destroy_all Doorkeeper::AccessToken.where('last_used_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(last_used_ip: nil) From 54426b77d01715685e832edefa8e9aef09a162c7 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 3 Jul 2022 10:41:48 +0200 Subject: [PATCH 032/166] ip_cleanup_scheduler: Introduce separate session retention period --- .env.production.sample | 6 ++++++ README.md | 3 ++- app/workers/scheduler/ip_cleanup_scheduler.rb | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.env.production.sample b/.env.production.sample index 03b2dd4739..a513322cab 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -293,6 +293,12 @@ MAX_REMOTE_EMOJI_SIZE=204800 # Time unit in seconds IP_RETENTION_PERIOD=31556952 +# Session retention period +# The termination of (web) sessions older than a year is done alongside the ip cleanup in vanilla Mastodon. +# Therefore, to prevent sessions from being destroyed alongside IP addresses, this separate period exists. +# Its default value of 31556952 (1 year) is derived from the previous hardcoded value of IP_RETENTION_PERIOD. +SESSION_RETENTION_PERIOD=31556952 + # Optional hCaptcha support # HCAPTCHA_SECRET_KEY= # HCAPTCHA_SITE_KEY= diff --git a/README.md b/README.md index 516cb4dee1..9cb5bb3de2 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,5 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. - Editing posts is enabled in the web frontend (thanks, meave [for the hint](https://toot.site/@meave/108515761669028663)). -- The duration of retention of IP addresses was made configurable. Unfortunately, the retention of IP addresses is currently coupled to the lifetime of sessions. Decoupling this is currently being worked on. +- The period of retention of IP addresses was made configurable. +- The period of retention of sessions was made configurable. diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 0787dd2c59..8f607db037 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -4,6 +4,7 @@ class Scheduler::IpCleanupScheduler include Sidekiq::Worker IP_RETENTION_PERIOD = ENV.fetch('IP_RETENTION_PERIOD', 1.year).to_i.seconds.freeze + SESSION_RETENTION_PERIOD = ENV.fetch('SESSION_RETENTION_PERIOD', 1.year).to_i.seconds.freeze sidekiq_options retry: 0 @@ -15,6 +16,7 @@ class Scheduler::IpCleanupScheduler private def clean_ip_columns! + SessionActivation.where('updated_at < ?', SESSION_RETENTION_PERIOD.ago).in_batches.destroy_all SessionActivation.where('updated_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(ip: nil) User.where('current_sign_in_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(sign_up_ip: nil) LoginActivity.where('created_at < ?', IP_RETENTION_PERIOD.ago).in_batches.destroy_all From 1def7be825c005400d2a8a1a7b1f454d5cead1c5 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 3 Jul 2022 10:53:09 +0200 Subject: [PATCH 033/166] GitHub: Remove build image workflow --- .github/workflows/build-image.yml | 43 ------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/build-image.yml diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml deleted file mode 100644 index 63aefc37ed..0000000000 --- a/.github/workflows/build-image.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build container image -on: - workflow_dispatch: - push: - branches: - - 'main' - tags: - - '*' - pull_request: - paths: - - .github/workflows/build-image.yml - - Dockerfile -jobs: - build-image: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: docker/setup-qemu-action@v2 - - uses: docker/setup-buildx-action@v2 - - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - if: github.event_name != 'pull_request' - - uses: docker/metadata-action@v4 - id: meta - with: - images: ghcr.io/${{ github.repository_owner }}/mastodon - flavor: | - latest=auto - tags: | - type=edge,branch=main - type=match,pattern=v(.*),group=0 - type=ref,event=pr - - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/mastodon:latest - cache-to: type=inline From 550a0fa08a9e649a464a5e5c3e0f40ce3f40431a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 5 Jul 2022 09:49:31 +0200 Subject: [PATCH 034/166] Exclude /api/v1/custom_emojis from secure fetch (unless allowlist mode is enabled) --- app/controllers/api/v1/custom_emojis_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/v1/custom_emojis_controller.rb b/app/controllers/api/v1/custom_emojis_controller.rb index 08b3474cc8..78b791a2a2 100644 --- a/app/controllers/api/v1/custom_emojis_controller.rb +++ b/app/controllers/api/v1/custom_emojis_controller.rb @@ -2,6 +2,7 @@ class Api::V1::CustomEmojisController < Api::BaseController skip_before_action :set_cache_headers + skip_before_action :require_authenticated_user!, unless: :whitelist_mode? def index expires_in 3.minutes, public: true From 1c419a9af3bfe176eeafe4f8c042d2a731f70cc0 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 5 Jul 2022 10:07:00 +0200 Subject: [PATCH 035/166] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9cb5bb3de2..67ad912e6f 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,5 @@ This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github. - Editing posts is enabled in the web frontend (thanks, meave [for the hint](https://toot.site/@meave/108515761669028663)). - The period of retention of IP addresses was made configurable. - The period of retention of sessions was made configurable. +- The rate limits for authenticated users have been relaxed a bit. +- The API endpoint `/api/v1/custom_emojis` is no longer affected by AUTHORIZED_FETCH, allowing anyone to copy custom emojis. From 62e16b7f1663527e294785b454a7e5130448bd70 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 7 Jul 2022 00:21:05 +0200 Subject: [PATCH 036/166] Allow higher-resolution images (4096x4096) --- app/javascript/flavours/glitch/util/resize_image.js | 2 +- app/javascript/mastodon/utils/resize_image.js | 2 +- app/models/media_attachment.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/util/resize_image.js b/app/javascript/flavours/glitch/util/resize_image.js index fb8c3c11e6..6c759adbac 100644 --- a/app/javascript/flavours/glitch/util/resize_image.js +++ b/app/javascript/flavours/glitch/util/resize_image.js @@ -1,6 +1,6 @@ import EXIF from 'exif-js'; -const MAX_IMAGE_PIXELS = 2073600; // 1920x1080px +const MAX_IMAGE_PIXELS = 16777216; // 4096x4096px const _browser_quirks = {}; diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js index fb8c3c11e6..6c759adbac 100644 --- a/app/javascript/mastodon/utils/resize_image.js +++ b/app/javascript/mastodon/utils/resize_image.js @@ -1,6 +1,6 @@ import EXIF from 'exif-js'; -const MAX_IMAGE_PIXELS = 2073600; // 1920x1080px +const MAX_IMAGE_PIXELS = 16777216; // 4096x4096px const _browser_quirks = {}; diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 69feffbf0c..7fd93bb48a 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -67,7 +67,7 @@ class MediaAttachment < ApplicationRecord IMAGE_STYLES = { original: { - pixels: 2_073_600, # 1920x1080px + pixels: 16_777_216, # 4096x4096px file_geometry_parser: FastGeometryParser, }.freeze, From a0b117450e25a6804af29990d19ae0598a6e1dbe Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 11 Jul 2022 18:27:31 +0200 Subject: [PATCH 037/166] Update README.md --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 67ad912e6f..ac282b1f50 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,25 @@ # Catstodon - ## Introduction - This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github.com/glitch-soc/mastodon). +I intend to contribute some useful differences back to [glitch-soc](https://github.com/glitch-soc/mastodon) and [vanilla Mastodon](https://github.com/mastodon/mastodon). -- To install, take a look at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/). The instructions and features are the same, except for the differences outlined below. -- Contributing guidelines are available [here](CONTRIBUTING.md). +To install, take a look at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/). The instructions and features are the same, except for the differences outlined below. + +Contributing guidelines are available [here](CONTRIBUTING.md). ## Differences - - Some files are adjusted specifically for the CatCatNya~ instance. Specifically, these: - sounds/boop.mp3 - sounds/boop.ogg - - You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. +
You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. -- Editing posts is enabled in the web frontend (thanks, meave [for the hint](https://toot.site/@meave/108515761669028663)). -- The period of retention of IP addresses was made configurable. -- The period of retention of sessions was made configurable. +- Editing posts is enabled in the web frontend (thanks, meave, [for the hint](https://toot.site/@meave/108515761669028663)). - The rate limits for authenticated users have been relaxed a bit. - The API endpoint `/api/v1/custom_emojis` is no longer affected by AUTHORIZED_FETCH, allowing anyone to copy custom emojis. +- Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) + +## Previous differences now merged into glitch-soc +- Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) + +## Previous differences now merged into vanilla Mastodon +- The period of retention of IP addresses and sessions was made configurable. ([Pull request](https://github.com/mastodon/mastodon/pull/18757)) From 172e73535ecf0c60f7667b2922790adcd6c3908b Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 12 Jul 2022 00:55:35 +0200 Subject: [PATCH 038/166] Make the emoji picker blob cat a static asset --- .../glitch/features/emoji_picker/index.js | 3 +-- .../compose/components/emoji_picker_dropdown.js | 3 +-- public/blobCat.png | Bin 0 -> 9348 bytes 3 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 public/blobCat.png diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js index 036a515f6c..8d06ea2194 100644 --- a/app/javascript/flavours/glitch/features/emoji_picker/index.js +++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js @@ -472,9 +472,8 @@ class EmojiPickerDropdown extends React.PureComponent { {button || blobCat emoji} - {/* TODO: Fetch :blobCat: From custom_emojis directly */} diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index c1419e1785..3d00d139dc 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -390,9 +390,8 @@ class EmojiPickerDropdown extends React.PureComponent { {button || blobCat emoji} - {/* TODO: Fetch :blobCat: From custom_emojis directly */} diff --git a/public/blobCat.png b/public/blobCat.png new file mode 100644 index 0000000000000000000000000000000000000000..08e1b408072ba3fd009ce097a52762a8012fabc3 GIT binary patch literal 9348 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 Date: Mon, 18 Jul 2022 00:04:44 +0200 Subject: [PATCH 039/166] Remove extraneous stuff from .env.production.sample --- .env.production.sample | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 116e09991e..ad058b2b79 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -286,19 +286,6 @@ MAX_POLL_OPTION_CHARS=100 # MAX_EMOJI_SIZE=262144 # MAX_REMOTE_EMOJI_SIZE=262144 -# IP retention period -# If undefined, the value of 31556952 (1 average year) will be used. -# You should also change the scheduling (by default, every 24 hours) of ip_cleanup_scheduler in config/sidekiq.yml -# if you change this value to avoid IP addresses being kept for longer than intended. -# Time unit in seconds -IP_RETENTION_PERIOD=31556952 - -# Session retention period -# The termination of (web) sessions older than a year is done alongside the ip cleanup in vanilla Mastodon. -# Therefore, to prevent sessions from being destroyed alongside IP addresses, this separate period exists. -# Its default value of 31556952 (1 year) is derived from the previous hardcoded value of IP_RETENTION_PERIOD. -SESSION_RETENTION_PERIOD=31556952 - # Optional hCaptcha support # HCAPTCHA_SECRET_KEY= # HCAPTCHA_SITE_KEY= From 65efd9a77da13be655a4a2058a6870c519232f9b Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 18 Jul 2022 00:07:51 +0200 Subject: [PATCH 040/166] Adjust README a bit --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac282b1f50..0c55bb7348 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ Contributing guidelines are available [here](CONTRIBUTING.md). - Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) ## Previous differences now merged into glitch-soc -- Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) +- Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) +- Everything merged into vanilla Mastodon ## Previous differences now merged into vanilla Mastodon -- The period of retention of IP addresses and sessions was made configurable. ([Pull request](https://github.com/mastodon/mastodon/pull/18757)) +- The period of retention of IP addresses and sessions was made configurable. ([Pull request](https://github.com/mastodon/mastodon/pull/18757)) From dc03059a7b4a853583dcc3b10c19e6d29b9a4d67 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 18 Jul 2022 01:03:27 +0200 Subject: [PATCH 041/166] Temporarily undo tag_serializer changes due to an error --- app/serializers/rest/tag_serializer.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/app/serializers/rest/tag_serializer.rb b/app/serializers/rest/tag_serializer.rb index 7801e77d1f..52bfaa4ce4 100644 --- a/app/serializers/rest/tag_serializer.rb +++ b/app/serializers/rest/tag_serializer.rb @@ -5,8 +5,6 @@ class REST::TagSerializer < ActiveModel::Serializer attributes :name, :url, :history - attribute :following, if: :current_user? - def url tag_url(object) end @@ -14,16 +12,4 @@ class REST::TagSerializer < ActiveModel::Serializer def name object.display_name end - - def following - if instance_options && instance_options[:relationships] - instance_options[:relationships].following_map[object.id] || false - else - TagFollow.where(tag_id: object.id, account_id: current_user.account_id).exists? - end - end - - def current_user? - !current_user.nil? - end end From 92268f5da77fb72e7b2190215ecbb7154a7ce6e9 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 18 Jul 2022 06:48:08 +0200 Subject: [PATCH 042/166] Revert "Temporarily undo tag_serializer changes due to an error" This reverts commit dc03059a7b4a853583dcc3b10c19e6d29b9a4d67. --- app/serializers/rest/tag_serializer.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/serializers/rest/tag_serializer.rb b/app/serializers/rest/tag_serializer.rb index 52bfaa4ce4..7801e77d1f 100644 --- a/app/serializers/rest/tag_serializer.rb +++ b/app/serializers/rest/tag_serializer.rb @@ -5,6 +5,8 @@ class REST::TagSerializer < ActiveModel::Serializer attributes :name, :url, :history + attribute :following, if: :current_user? + def url tag_url(object) end @@ -12,4 +14,16 @@ class REST::TagSerializer < ActiveModel::Serializer def name object.display_name end + + def following + if instance_options && instance_options[:relationships] + instance_options[:relationships].following_map[object.id] || false + else + TagFollow.where(tag_id: object.id, account_id: current_user.account_id).exists? + end + end + + def current_user? + !current_user.nil? + end end From 2cd46ac82e8b78f6937f422035c5ee0c883df547 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 18 Jul 2022 06:50:09 +0200 Subject: [PATCH 043/166] Fix missing scope for current_user in _sidebar.html.haml Fix suggested by https://miaow.gay/@meganeko/108665430982072358 --- app/views/application/_sidebar.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml index 0a952add07..cc157bf479 100644 --- a/app/views/application/_sidebar.html.haml +++ b/app/views/application/_sidebar.html.haml @@ -13,4 +13,4 @@ %h4.emojify= t('footer.trending_now') - trends.each do |tag| - = react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer).as_json + = react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer, scope: current_user, scope_name: :current_user).as_json From df02b6629527f7ff64bde1d4d3033d8d694a4b88 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 20 Jul 2022 18:03:40 +0200 Subject: [PATCH 044/166] Allow adjusting avatar sizes (local and remote) via config --- .env.production.catcatnya | 1 + .env.production.sample | 7 +++++++ app/models/concerns/account_avatar.rb | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.env.production.catcatnya b/.env.production.catcatnya index 6d572f5310..9e5a275fb2 100644 --- a/.env.production.catcatnya +++ b/.env.production.catcatnya @@ -40,4 +40,5 @@ MAX_DISPLAY_NAME_CHARS=50 MAX_POLL_OPTIONS=20 MAX_SEARCH_RESULTS=1000 MAX_REMOTE_EMOJI_SIZE=1048576 +# MAX_REMOTE_AVATAR_SIZE=4194304 IP_RETENTION_PERIOD=86400 diff --git a/.env.production.sample b/.env.production.sample index ad058b2b79..2b56299955 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -286,6 +286,13 @@ MAX_POLL_OPTION_CHARS=100 # MAX_EMOJI_SIZE=262144 # MAX_REMOTE_EMOJI_SIZE=262144 +# Maximum avatar (upload) sizes +# If undefined or smaller than MAX_AVATAR_SIZE, the value +# of MAX_AVATAR_SIZE will be used for MAX_REMOTE_AVATAR_SIZE +# Units are in bytes +# MAX_AVATAR_SIZE=2097152 +# MAX_REMOTE_AVATAR_SIZE=2097152 + # Optional hCaptcha support # HCAPTCHA_SECRET_KEY= # HCAPTCHA_SITE_KEY= diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb index 0cfd9167cd..164ae98d2e 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account_avatar.rb @@ -4,7 +4,8 @@ module AccountAvatar extend ActiveSupport::Concern IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze - LIMIT = 2.megabytes + LOCAL_LIMIT = (ENV['MAX_AVATAR_SIZE'] || 2.megabytes).to_i + LIMIT = [LOCAL_LIMIT, (ENV['MAX_REMOTE_AVATAR_SIZE'] || 2.megabytes).to_i].max class_methods do def avatar_styles(file) @@ -20,7 +21,8 @@ module AccountAvatar # Avatar upload has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' }, processors: [:lazy_thumbnail] validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES - validates_attachment_size :avatar, less_than: LIMIT + validates_attachment_size :avatar, less_than: LIMIT, unless: :local? + validates_attachment_size :avatar, less_than: LOCAL_LIMIT, if: :local? remotable_attachment :avatar, LIMIT, suppress_errors: false end From 554ac88290dcc11658453c657dac653747ab2aac Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 20 Jul 2022 22:33:34 +0200 Subject: [PATCH 045/166] Revert "Allow adjusting avatar sizes (local and remote) via config" This reverts commit df02b6629527f7ff64bde1d4d3033d8d694a4b88. --- .env.production.catcatnya | 1 - .env.production.sample | 7 ------- app/models/concerns/account_avatar.rb | 6 ++---- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.env.production.catcatnya b/.env.production.catcatnya index 9e5a275fb2..6d572f5310 100644 --- a/.env.production.catcatnya +++ b/.env.production.catcatnya @@ -40,5 +40,4 @@ MAX_DISPLAY_NAME_CHARS=50 MAX_POLL_OPTIONS=20 MAX_SEARCH_RESULTS=1000 MAX_REMOTE_EMOJI_SIZE=1048576 -# MAX_REMOTE_AVATAR_SIZE=4194304 IP_RETENTION_PERIOD=86400 diff --git a/.env.production.sample b/.env.production.sample index 2b56299955..ad058b2b79 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -286,13 +286,6 @@ MAX_POLL_OPTION_CHARS=100 # MAX_EMOJI_SIZE=262144 # MAX_REMOTE_EMOJI_SIZE=262144 -# Maximum avatar (upload) sizes -# If undefined or smaller than MAX_AVATAR_SIZE, the value -# of MAX_AVATAR_SIZE will be used for MAX_REMOTE_AVATAR_SIZE -# Units are in bytes -# MAX_AVATAR_SIZE=2097152 -# MAX_REMOTE_AVATAR_SIZE=2097152 - # Optional hCaptcha support # HCAPTCHA_SECRET_KEY= # HCAPTCHA_SITE_KEY= diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account_avatar.rb index 164ae98d2e..0cfd9167cd 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account_avatar.rb @@ -4,8 +4,7 @@ module AccountAvatar extend ActiveSupport::Concern IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze - LOCAL_LIMIT = (ENV['MAX_AVATAR_SIZE'] || 2.megabytes).to_i - LIMIT = [LOCAL_LIMIT, (ENV['MAX_REMOTE_AVATAR_SIZE'] || 2.megabytes).to_i].max + LIMIT = 2.megabytes class_methods do def avatar_styles(file) @@ -21,8 +20,7 @@ module AccountAvatar # Avatar upload has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' }, processors: [:lazy_thumbnail] validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES - validates_attachment_size :avatar, less_than: LIMIT, unless: :local? - validates_attachment_size :avatar, less_than: LOCAL_LIMIT, if: :local? + validates_attachment_size :avatar, less_than: LIMIT remotable_attachment :avatar, LIMIT, suppress_errors: false end From e354a296c10ba8b9f16df706a24341e4175e431b Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 12 Aug 2022 21:17:29 +0200 Subject: [PATCH 046/166] Web UI: Allow creating polls with only one option --- app/javascript/flavours/glitch/reducers/compose.js | 2 +- app/javascript/mastodon/reducers/compose.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index 2ef08b2a6b..314acebcc6 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -126,7 +126,7 @@ const initialState = ImmutableMap({ }); const initialPoll = ImmutableMap({ - options: ImmutableList(['', '']), + options: ImmutableList(['']), expires_in: 24 * 3600, multiple: false, }); diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 7aac87b5c4..eda30b7180 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -94,7 +94,7 @@ const initialState = ImmutableMap({ }); const initialPoll = ImmutableMap({ - options: ImmutableList(['', '']), + options: ImmutableList(['']), expires_in: 24 * 3600, multiple: false, }); From 1c107575d858d053334d211392a991318c7efb47 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 12 Aug 2022 21:38:35 +0200 Subject: [PATCH 047/166] Update poll validation to allow one-option polls if configured --- .env.production.catcatnya | 1 + .env.production.sample | 3 +++ app/validators/poll_validator.rb | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.env.production.catcatnya b/.env.production.catcatnya index 6d572f5310..6e180d08b0 100644 --- a/.env.production.catcatnya +++ b/.env.production.catcatnya @@ -37,6 +37,7 @@ MAX_BIO_CHARS=6942 MAX_PROFILE_FIELDS=10 MAX_PINNED_TOOTS=10 MAX_DISPLAY_NAME_CHARS=50 +MIN_POLL_OPTIONS=1 MAX_POLL_OPTIONS=20 MAX_SEARCH_RESULTS=1000 MAX_REMOTE_EMOJI_SIZE=1048576 diff --git a/.env.production.sample b/.env.production.sample index ad058b2b79..98fa676129 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -263,6 +263,9 @@ MAX_PROFILE_FIELDS=4 # Maximum allowed display name characters MAX_DISPLAY_NAME_CHARS=30 +# Minimum allowed poll options. (Minimum of 1) +MIN_POLL_OPTIONS=2 + # Maximum allowed poll options MAX_POLL_OPTIONS=5 diff --git a/app/validators/poll_validator.rb b/app/validators/poll_validator.rb index 1aaf5a5d02..da7bf3daff 100644 --- a/app/validators/poll_validator.rb +++ b/app/validators/poll_validator.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class PollValidator < ActiveModel::Validator - MAX_OPTIONS = (ENV['MAX_POLL_OPTIONS'] || 5).to_i + MIN_OPTIONS = [1, (ENV['MIN_POLL_OPTIONS'] || 2).to_i].max + MAX_OPTIONS = [MIN_OPTIONS, (ENV['MAX_POLL_OPTIONS'] || 5).to_i].max MAX_OPTION_CHARS = (ENV['MAX_POLL_OPTION_CHARS'] || 100).to_i MAX_EXPIRATION = 1.month.freeze MIN_EXPIRATION = 5.minutes.freeze @@ -9,7 +10,7 @@ class PollValidator < ActiveModel::Validator def validate(poll) current_time = Time.now.utc - poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1 + poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size >= MIN_OPTIONS poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS } poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size From f2fef712b37f9daeec732c06db9f85a02d44b53f Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 12 Aug 2022 21:42:19 +0200 Subject: [PATCH 048/166] Web UI: Allow removing a second poll choice --- .../flavours/glitch/features/compose/components/poll_form.js | 2 +- .../mastodon/features/compose/components/poll_form.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/poll_form.js b/app/javascript/flavours/glitch/features/compose/components/poll_form.js index e4b5104f35..247cc5c13e 100644 --- a/app/javascript/flavours/glitch/features/compose/components/poll_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/poll_form.js @@ -81,7 +81,7 @@ class Option extends React.PureComponent {

- +
); diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js index 88894ae591..1628aa6b31 100644 --- a/app/javascript/mastodon/features/compose/components/poll_form.js +++ b/app/javascript/mastodon/features/compose/components/poll_form.js @@ -102,7 +102,7 @@ class Option extends React.PureComponent {
- +
); From fa0195396aeaf76efe64547c651de750b3773c2e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 12 Aug 2022 21:46:49 +0200 Subject: [PATCH 049/166] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c55bb7348..6273e0b8f8 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Contributing guidelines are available [here](CONTRIBUTING.md). - The rate limits for authenticated users have been relaxed a bit. - The API endpoint `/api/v1/custom_emojis` is no longer affected by AUTHORIZED_FETCH, allowing anyone to copy custom emojis. - Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) +- Allow posting polls with only one poll option (if `MIN_POLL_OPTIONS` is set to 1 on your instance). ## Previous differences now merged into glitch-soc - Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) From 1591adb62384f511436e94131c7553197ff5000d Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 15 Aug 2022 21:19:43 +0200 Subject: [PATCH 050/166] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6273e0b8f8..caee5edd3e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Catstodon ## Introduction -This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github.com/glitch-soc/mastodon). +This Mastodon fork is based on the [glitch-soc Fork of Mastodon](https://github.com/glitch-soc/mastodon), with changes made to suit [CatCatNya~](https://catcatnya.com). +The aforementioned instance is running the `develop` branch. I intend to contribute some useful differences back to [glitch-soc](https://github.com/glitch-soc/mastodon) and [vanilla Mastodon](https://github.com/mastodon/mastodon). To install, take a look at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/). The instructions and features are the same, except for the differences outlined below. From f9f3ed9c84639374f332c6969a78faf1faff2306 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 27 Aug 2022 10:49:42 +0200 Subject: [PATCH 051/166] Add Oatstodon glitch flavour --- .../flavours/glitch/styles/oatstodon.scss | 335 ++++++++++++++++++ .../skins/glitch/oatstodon/common.scss | 1 + .../skins/glitch/oatstodon/names.yml | 8 + 3 files changed, 344 insertions(+) create mode 100644 app/javascript/flavours/glitch/styles/oatstodon.scss create mode 100644 app/javascript/skins/glitch/oatstodon/common.scss create mode 100644 app/javascript/skins/glitch/oatstodon/names.yml diff --git a/app/javascript/flavours/glitch/styles/oatstodon.scss b/app/javascript/flavours/glitch/styles/oatstodon.scss new file mode 100644 index 0000000000..88f8572470 --- /dev/null +++ b/app/javascript/flavours/glitch/styles/oatstodon.scss @@ -0,0 +1,335 @@ +@import 'index'; + +/* customize ya stuff */ +:root { + --border-radius: 5px; + + /* rgb for transparency to work */ + --text-color: 217, 225, 232; + --text-color-secondary: 96, 105, 132; /* less bright, for unimportant bits */ + + --background-color: 18, 18, 37; + --background-color-brighter: 22, 22, 47; + --app-background-color: 8, 8, 17; /* used only for the VERY background in the back */ + + --accent-color: 39, 183, 145; + --accent-color-secondary: 62, 91, 84; /* less saturated ver of --accent-color */ + --accent-color-bright: 92, 193, 162; +} + +/* here for you to remove */ + +.account__avatar-overlay-base, .account__avatar-overlay-overlay, .account__avatar { + border-radius: 50%; + /* border-bottom-right-radius: 15%; */ /* uncomment for teardrop */ +} + +/* roundening shenanigans */ + +.drawer > div, nav, .search, .drawer__header a, .drawer--header a, .search__input { + border-radius: var(--border-radius) !important; +} + +.column-header, .column-back-button, .navigation-panel .column-link:nth-child(1), .navigation-panel .column-link:nth-child(10), .navigation-bar { + border-radius: var(--border-radius) var(--border-radius) 0px 0px; +} +.column > .scrollable, .getting-started, .navigation-panel .column-link:nth-child(8), .navigation-panel .column-link:nth-child(11) { + border-radius: 0px 0px var(--border-radius) var(--border-radius); +} + +/* standard fg/bg color changes */ + +.drawer__inner, .drawer__inner__mastodon, .drawer__header, .drawer--header, .actions-modal, .block-modal, .boost-modal, .confirmation-modal, .mute-modal, .report-modal, article, .getting-started, .column-subheading, .column-link, .column-subheading, .column-link, .emoji-mart-scroll, .emoji-mart-search, .emoji-mart-category-label > span, .emoji-picker-dropdown__menu, .scrollable, .empty-column-indicator, .column-inline-form, .dropdown-menu, .dropdown-menu__item a, .account__header__fields dt, .search-popout, .confirmation-modal__action-bar, .reactions-bar__item, .emoji-picker-dropdown__modifiers__menu, .content-wrapper, .sidebar-wrapper--empty, .regeneration-indicator, .tabs-bar, .navigation-bar, .trends__header, .modal-layout { + background-color: rgb(var(--background-color)) !important; + color: rgb(var(--text-color)) !important; +} +.glitch.local-settings__navigation, .glitch.local-settings__navigation__item, .glitch.local-settings__page, .glitch.local-settings { + background-color: rgb(var(--background-color)); + color: rgb(var(--text-color)); +} + +.modal-layout, .modal-layout__mastodon > * { + background-image: none; +} + +.account__section-headline a.active::after, .account__section-headline button.active::after, .notification__filter-bar a.active::after, .notification__filter-bar button.active::after, .account__section-headline a.active::after, .account__section-headline a.active::before, .account__section-headline button.active::after, .account__section-headline button.active::before, .notification__filter-bar a.active::after, .notification__filter-bar a.active::before, .notification__filter-bar button.active::after, .notification__filter-bar button.active::before { + border-color: transparent transparent rgb(var(--background-color)); +} + +.dropdown-menu__arrow { + border-bottom-color: rgb(var(--background-color)) !important; +} +.dropdown-menu__arrow.top { + border-top-color: rgb(var(--background-color)) !important; +} + +.reply-indicator__content, .status__content, .reply-indicator__display-name, .privacy-dropdown__option__icon, .composer--options--dropdown--content--item .icon, .composer--reply > .content { + color: rgb(var(--text-color)) !important; +} + +html { + scrollbar-color: rgb(var(--background-color-brighter)) rgba(0,0,0,0.1); +} + +.tabs-bar__wrapper { + background: rgb(var(--app-background-color)); +} + +.column-header, .column-header__button, .account__section-headline, .notification__filter-bar > button, .emoji-mart-bar, .column-back-button, .column-header__back-button, .announcements, .column-header__collapsible-inner, .status.status-direct:not(.read), .notification__filter-bar, .glitch.local-settings__page { + background-color: rgb(var(--background-color-brighter)) !important; + border-bottom: none; +} + +.reply-indicator, .emoji-picker-dropdown__modifiers__menu button:hover, .compose-form .compose-form__buttons-wrapper, .composer--options-wrapper, .compose-form__poll-wrapper select, .flash-message, .card__bar, .card > a:hover .card__bar, .glitch.local-settings__navigation__item:hover { + background-color: rgb(var(--background-color-brighter)); +} + +.columns-area, .app-body, .getting-started__wrapper { + background-color: rgb(var(--app-background-color)); +} + +.privacy-dropdown__option__content strong, .composer--options--dropdown--content strong, .character-counter, .report-modal__comment .setting-text-label, .compose-form__poll-wrapper select { + color: rgb(var(--text-color)) !important; +} + +input, textarea, .compose-form__modifiers, .privacy-dropdown__dropdown, .composer--options--dropdown--content, .privacy-dropdown__value { + background-color: rgb(var(--background-color-brighter)) !important; + color: rgb(var(--text-color)) !important; +} + +.compose-form__buttons-wrapper, .admin-wrapper .sidebar-wrapper__inner, .admin-wrapper .sidebar ul a:hover, .admin-wrapper .sidebar ul a, .admin-wrapper .sidebar ul a.selected, .account__disclaimer, .account__action-bar-links { + background-color: rgb(var(--background-color-brighter)); +} + +.detailed-status, .detailed-status__action-bar, .account__header__bar, .focusable:focus { + background-color: rgb(var(--background-color-brighter)) !important; + border-bottom: none; + border-top: none; +} + +.status.collapsed .status__content::after { + background: linear-gradient(rgba(var(--background-color),0), rgba(var(--background-color),0)) !important; +} + +/* accent color changes */ + +.button, .react-toggle--checked .react-toggle-track, .react-toggle--checked:hover .react-toggle-track, .react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track, .button.logo-button, .emoji-mart-anchor-bar, .loading-bar, .icon-with-badge__badge, .video-player__volume__current, .video-player__volume__handle, .upload-progress__tracker, .video-player__seek__buffer, .video-player__seek__progress, .floating-action-button { + background-color: rgb(var(--accent-color)); +} +.react-toggle--checked .react-toggle-thumb, .compose-form__sensitive-button .checkbox, .filters .filter-subset a.selected, .account__action-bar__tab.active, .tabs-bar__link.active, .notification.unread::before, .status.unread::before { + border-color: rgb(var(--accent-color)); +} + +.text-icon-button, .icon-button.inverted, button.inverted:hover, .icon-button, .icon-button:hover, .status__action-bar__counter__label, .text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .icon-button.disabled, .composer--options--dropdown.open > .value { + color: rgb(var(--accent-color-secondary)); +} +.status__info__icons i { + color: rgb(var(--accent-color-secondary)) !important; +} + +.status__content__spoiler-link { + background-color: rgb(var(--accent-color-secondary)) !important; +} +.column-header__wrapper.active::before { + background: radial-gradient(ellipse, rgba(var(--accent-color),.23) 0%, rgba(var(--accent-color),0) 60%); +} +.column-header__wrapper.active { + box-shadow: 0 1px 0 rgba(var(--accent-color),.3); +} + +.compose-form__sensitive-button .checkbox.active, .poll__chart.leading { + border-color: rgb(var(--accent-color)); + background-color: rgb(var(--accent-color)); +} + +.poll__chart { + background-color: rgb(var(--accent-color-secondary)); +} + +.column-header.active .column-header__icon { + text-shadow: 0 0 10px rgba(var(--accent-color),.4); +} + +.text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .drawer__header a:hover, .drawer--header a:hover, .drawer--header a:focus, .icon-button:hover, .reactions-bar__item:hover { + background-color: rgba(var(--accent-color-secondary), .1); +} + +.icon-button.inverted:active, .icon-button.inverted:focus, .icon-button.inverted:hover, .reactions-bar__item.active { + background-color: rgba(var(--accent-color-secondary), .25); + color: rgb(var(--accent-color-secondary)); +} + +.button:active, .button:focus, .button:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a:hover, .simple_form .block-button:hover, .simple_form .button:hover, .simple_form button:hover, .button.logo-button:active, .button.logo-button:focus, .button.logo-button:hover, .floating-action-button:hover, .glitch.local-settings__navigation__item.active:hover { + background-color: rgb(var(--accent-color-bright)); +} + +.privacy-dropdown__option.active, .composer--options--dropdown--content--item.active, .privacy-dropdown__option:hover, .composer--options--dropdown--content--item:hover, .privacy-dropdown__option.active:hover, .composer--options--dropdown--content.active:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a, .simple_form .block-button, .simple_form .button, .simple_form button, .simple_form .block-button:active, .simple_form .block-button:focus, .simple_form .button:active, .simple_form .button:focus, .simple_form button:active, .simple_form button:focus, .composer--options--dropdown.open > .value, .glitch.local-settings__navigation__item.active { + background-color: rgb(var(--accent-color)); +} + +.status__info__icons .icon-button.active i, .tabs-bar__link.active, .status__content a { + color: rgb(var(--accent-color)) !important; +} + + +.trends__item__sparkline path:last-child { + stroke: rgb(var(--accent-color)) !important; +} +.trends__item__sparkline path:first-child { + fill: rgb(var(--accent-color-secondary)) !important; +} + +a.u-url, .status-link, .column-header__back-button, .status__content__read-more-button, .column-header.active .column-header__icon, .column-link.active, .account__section-headline a.active, .account__section-headline button.active, .notification__filter-bar a.active, .notification__filter-bar button.active, .account__header__content a, .account__header__bio .account__header__fields a, .reactions-bar__item.active .reactions-bar__item__count, .emoji-mart-anchor-selected, .reply-indicator__content a, .compose-form .compose-form__warning a, .text-icon-button.active, .icon-button.inverted.active, .drawer__tab:hover, .icon-button.active, .column-back-button, .filters .filter-subset a.selected, .admin-wrapper .content .muted-hint a, body .muted-hint a, .table a, .notification__message .fa, .drawer--header a:hover, .drawer--header a:focus { + color: rgb(var(--accent-color)) !important; +} + +/* fixes */ + +/* boost hack, v2 */ +/* https://codepen.io/sosuke/pen/Pjoqqp */ +button.icon-button i.fa-retweet { + filter: brightness(0) saturate(100%) invert(31%) sepia(28%) saturate(388%) hue-rotate(115deg) brightness(94%) contrast(90%); /* accent-color-secondary */ + color: transparent !important; +} +button.icon-button.active i.fa-retweet { + filter: brightness(0) saturate(100%) invert(57%) sepia(61%) saturate(481%) hue-rotate(114deg) brightness(93%) contrast(91%); /* accent-color */ +} +button.icon-button.disabled i.fa-retweet, button.icon-button.disabled i.fa-lock { + filter: brightness(0) saturate(100%) invert(31%) sepia(28%) saturate(388%) hue-rotate(115deg) brightness(60%) contrast(90%); /* accent-color-secondary with brightness set to 50% */ +} + +.load-more:hover, .mbstobon-2 .drawer__inner__mastodon, .mbstobon-1 .drawer__inner__mastodon, .mbstobon-0 .drawer__inner__mastodon { + background: inherit; +} + +.account__action-bar__tab, .account__action-bar { + border: none; +} + +.notification__filter-bar, .account__header__bar, .admin-wrapper .content-heading, .admin-wrapper .content h4, .tabs-bar__link:not(.active) { + border-bottom: none; +} + +.dropdown-menu__separator, hr { + opacity: 0; +} +.compose-form .autosuggest-textarea__textarea, .compose-form .spoiler-input__input, .compose-panel .compose-form__autosuggest-wrapper, .mbstobon-3 .drawer__inner__mastodon { + background: transparent; +} +.status, .account, .account__header__fields dl, .account__header__fields, .account__header__bio .account__header__fields, .glitch.local-settings__navigation__item { + border-top: none; + border-bottom: none; +} + +.report-modal__container, .report-modal__comment, .report-modal__comment .setting-text__wrapper { + border-color: rgba(0, 0, 0, 0) !important; +} + +.drawer__inner__mastodon { + background: inherit; +} + +/* misc */ +.column-link:hover, .dropdown-menu__item a:active, .dropdown-menu__item a:focus, .dropdown-menu__item a:hover, header strong.display-name__html { + color: #fff !important; +} + +.notification__filter-bar button.active, .account__section-headline .active { + border-bottom: 3px solid rgb(var(--accent-color)); +} +.notification__filter-bar button:not(.active):hover { + top: -3px; +} +.account__section-headline a.active::after, .account__section-headline a.active::before { + display: none; +} + +.account__header__extra__links a:hover { + text-decoration: underline; +} +.account__section-headline a:hover, .confirmation-modal__cancel-button span { + color: #fff; +} + +.notification__filter-bar button.active::after { + opacity: 0; +} +.notification__filter-bar button.active::before { + opacity: 0; +} + +.column-link__badge, .column-subheading { + background-color: rgb(var(--accent-color)); + animation-name: flash; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-direction: alternate-reverse; +} + +@keyframes flash { + from {background-color: rgb(var(--accent-color));} + to {background-color: rgb(var(--accent-color-secondary));} +} + +.reply-indicator { + max-height: 38px; + overflow-y: hidden; + transition: max-height 1s; +} +.reply-indicator:hover { + max-height: 100%; +} +.reply-indicator:before { + content: 'Replying to:'; + font-size: 12px; + color: rgb(var(--text-color-secondary)); +} + +.getting-started__footer p:after { + content: ' Oat was here'; +} + + +/* public/static css */ +/* for pages like /@username */ + +.public-layout .public-account-header__tabs__tabs .counter.active::after { + border-bottom-color: rgb(var(--accent-color)); +} +.public-layout .public-account-bio .account__header__fields a { + color: rgb(var(--accent-color)); +} +.public-layout .header .nav-button { + color: #fff; + background-color: rgb(var(--accent-color)); +} +.public-layout .header .nav-button:active, .public-layout .header .nav-button:focus, .public-layout .header .nav-button:hover { + background-color: rgb(var(--accent-color-bright)) +} +.public-layout .activity-stream .entry, .hero-widget__text, .table-of-contents { + background-color: rgb(var(--background-color)); +} +body { + background-color: rgb(var(--app-background-color)); +} +.public-layout .public-account-header__tabs__tabs .counter { + border-right: none; +} +.public-layout .public-account-bio, .public-layout .public-account-header__bar::before, .public-layout .header, .directory__tag > a, .directory__tag > div, .directory__tag > a:active, .directory__tag > a:focus, .directory__tag > a:hover, .public-layout .header .brand:hover, .landing-page__call-to-action, .box-widget { + background-color: rgb(var(--background-color-brighter)); +} +.public-layout .display-name, .status__relative-time time, .status__relative-time { + color: rgb(var(--text-color-secondary)); +} +.rich-formatting, .rich-formatting p { + color: rgb(var(--text-color)); +} +.rich-formatting table tbody tr, .rich-formatting table thead tr, .notification-follow, .notification-follow-request { + border-bottom: none; +} + +// https://types.pl/@haskal/106569437674907815 +.search-popout em { + color: rgb(var(--accent-color)) !important; +} + diff --git a/app/javascript/skins/glitch/oatstodon/common.scss b/app/javascript/skins/glitch/oatstodon/common.scss new file mode 100644 index 0000000000..4c6f4af978 --- /dev/null +++ b/app/javascript/skins/glitch/oatstodon/common.scss @@ -0,0 +1 @@ +@import 'flavours/glitch/styles/oatstodon'; diff --git a/app/javascript/skins/glitch/oatstodon/names.yml b/app/javascript/skins/glitch/oatstodon/names.yml new file mode 100644 index 0000000000..dfc72b37f6 --- /dev/null +++ b/app/javascript/skins/glitch/oatstodon/names.yml @@ -0,0 +1,8 @@ +en: + skins: + glitch: + oatstodon: oatstodon (by @oat@hellsite.site) +es: + skins: + glitch: + oatstodon: oatstodon (por @oat@hellsite.site) From 691f82d346a308b3a2552ad4f0c21ab1f4e53708 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 27 Aug 2022 11:24:24 +0200 Subject: [PATCH 052/166] Oatstodon flavour: Make avatars rounded squares again rather than circles --- app/javascript/flavours/glitch/styles/oatstodon.scss | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/oatstodon.scss b/app/javascript/flavours/glitch/styles/oatstodon.scss index 88f8572470..c630bad7c1 100644 --- a/app/javascript/flavours/glitch/styles/oatstodon.scss +++ b/app/javascript/flavours/glitch/styles/oatstodon.scss @@ -3,25 +3,22 @@ /* customize ya stuff */ :root { --border-radius: 5px; - + /* rgb for transparency to work */ --text-color: 217, 225, 232; --text-color-secondary: 96, 105, 132; /* less bright, for unimportant bits */ - + --background-color: 18, 18, 37; --background-color-brighter: 22, 22, 47; --app-background-color: 8, 8, 17; /* used only for the VERY background in the back */ - + --accent-color: 39, 183, 145; --accent-color-secondary: 62, 91, 84; /* less saturated ver of --accent-color */ --accent-color-bright: 92, 193, 162; } -/* here for you to remove */ - .account__avatar-overlay-base, .account__avatar-overlay-overlay, .account__avatar { - border-radius: 50%; - /* border-bottom-right-radius: 15%; */ /* uncomment for teardrop */ + border-radius: var(--border-radius); } /* roundening shenanigans */ From ca18ac7d4133014280ad825db3e3e062f949316a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 27 Aug 2022 14:17:04 +0200 Subject: [PATCH 053/166] Update README.md: Oatstodon is a difference --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index caee5edd3e..ad36e91219 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Contributing guidelines are available [here](CONTRIBUTING.md). - The API endpoint `/api/v1/custom_emojis` is no longer affected by AUTHORIZED_FETCH, allowing anyone to copy custom emojis. - Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) - Allow posting polls with only one poll option (if `MIN_POLL_OPTIONS` is set to 1 on your instance). +- Added oatstodon flavour (taken from [types.pl fork](https://github.com/ralsei/types.pl), by [@oat@hellsite.site](https://hellsite.site/@oat)) ## Previous differences now merged into glitch-soc - Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) From 45159ce12803929d2db14adb18c00343cd384c3c Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 8 Sep 2022 09:44:24 +0200 Subject: [PATCH 054/166] Update README.md Add info regarding develop branch --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ad36e91219..3673409d59 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ To install, take a look at [glitch-soc.github.io/docs/](https://glitch-soc.githu Contributing guidelines are available [here](CONTRIBUTING.md). +Note: [CatCatNya~](https://catcatnya.com) runs on the `develop` branch. +That branch may, at times, be force-pushed to (mostly for undoing cherry-picking of vanilla commits when upstream adopts them). +I highly suggest only ever running the `main` branch in production! + ## Differences - Some files are adjusted specifically for the CatCatNya~ instance. Specifically, these: - sounds/boop.mp3 From 7f3dc33cc0f7bf5e77f90e2c46a1d6db405a6974 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 27 Sep 2022 23:33:56 +0200 Subject: [PATCH 055/166] CatCatNya~ env: Set Rails log level to error --- .env.production.catcatnya | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.production.catcatnya b/.env.production.catcatnya index 6e180d08b0..e99422ecc4 100644 --- a/.env.production.catcatnya +++ b/.env.production.catcatnya @@ -29,7 +29,7 @@ ES_PORT=[REDACTED] ES_PREFIX=[REDACTED] AUTHORIZED_FETCH=true RAILS_SERVE_STATIC_FILES=false -RAILS_LOG_LEVEL=warn +RAILS_LOG_LEVEL=error MAX_TOOT_CHARS=6942 MAX_DESCRIPTION_CHARS=6942 From c33675bdb188f75ecc2fd22eb9909590b0a5b117 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 3 Oct 2022 17:50:15 +0200 Subject: [PATCH 056/166] Add suffix version --- lib/mastodon/version.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 819858c74b..a466b900f5 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -24,12 +24,16 @@ module Mastodon '+catstodon' end + def suffix_version + '+1.0.0' + end + def to_a [major, minor, patch].compact end def to_s - [to_a.join('.'), flags, suffix].join + [to_a.join('.'), flags, suffix, suffix_version].join end def repository From daaf383236c1c135f84f583105c69010ed67c0c5 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 4 Oct 2022 17:43:42 +0200 Subject: [PATCH 057/166] Add titles to RSS feeds again --- README.md | 3 +++ app/views/accounts/show.rss.ruby | 1 + app/views/tags/show.rss.ruby | 1 + 3 files changed, 5 insertions(+) diff --git a/README.md b/README.md index 3673409d59..80fedb8c44 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ I highly suggest only ever running the `main` branch in production! - Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) - Allow posting polls with only one poll option (if `MIN_POLL_OPTIONS` is set to 1 on your instance). - Added oatstodon flavour (taken from [types.pl fork](https://github.com/ralsei/types.pl), by [@oat@hellsite.site](https://hellsite.site/@oat)) +- RSS feeds have titles again. + - Account RSS feeds show the CW (if applicable). + - Tag RSS feeds show the handle (username if local, username@domain if remote) and the CW (if applicable). ## Previous differences now merged into glitch-soc - Fixed incorrect upload size limit display when adding new a new custom emoji. ([Pull request](https://github.com/glitch-soc/mastodon/pull/1763)) diff --git a/app/views/accounts/show.rss.ruby b/app/views/accounts/show.rss.ruby index 34e29d483f..30cb42f8d0 100644 --- a/app/views/accounts/show.rss.ruby +++ b/app/views/accounts/show.rss.ruby @@ -9,6 +9,7 @@ RSS::Builder.build do |doc| @statuses.each do |status| doc.item do |item| + item.title("New post#{" (CW: #{status.spoiler_text})" unless status.spoiler_text.empty?}") item.link(ActivityPub::TagManager.instance.url_for(status)) item.pub_date(status.created_at) item.description(rss_status_content_format(status)) diff --git a/app/views/tags/show.rss.ruby b/app/views/tags/show.rss.ruby index 8e0c2327b5..25f76610ff 100644 --- a/app/views/tags/show.rss.ruby +++ b/app/views/tags/show.rss.ruby @@ -7,6 +7,7 @@ RSS::Builder.build do |doc| @statuses.each do |status| doc.item do |item| + item.title("New post by #{status.account.pretty_acct}#{" (CW: #{status.spoiler_text})" unless status.spoiler_text.empty?}") item.link(ActivityPub::TagManager.instance.url_for(status)) item.pub_date(status.created_at) item.description(rss_status_content_format(status)) From 68fe934a489942b430f32ce885fe536fd0f4742c Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 4 Oct 2022 21:12:09 +0200 Subject: [PATCH 058/166] Increase suffix to 1.0.1 for release --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index a466b900f5..3d6ce758ba 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.0' + '+1.0.1' end def to_a From f955ea727bcec22764574dd1e0be1a013a983c82 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 4 Oct 2022 21:14:35 +0200 Subject: [PATCH 059/166] Increase suffix to 1.0.2 for next --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 3d6ce758ba..82a97ca664 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.1' + '+1.0.2' end def to_a From 7815af774feb76d096c24ee08a95e21ab25210f7 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 5 Oct 2022 17:59:25 +0200 Subject: [PATCH 060/166] v3.5.3+1.0.3 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 82a97ca664..5c6a1daa5f 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.2' + '+1.0.3' end def to_a From 7944e785fe5b11f3e06edb35c0619a589faffbe7 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 7 Oct 2022 16:41:08 +0200 Subject: [PATCH 061/166] v3.5.3+1.0.4 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 5c6a1daa5f..910ad391d6 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.3' + '+1.0.4' end def to_a From 7a385567b26b47b94c53ebb40fa0280ea49f6981 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 8 Oct 2022 21:08:55 +0200 Subject: [PATCH 062/166] v3.5.3+1.0.5 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 910ad391d6..8cf33edcde 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.4' + '+1.0.5' end def to_a From 01d702f603c65cbce8492e4c91fb2bcb13cde011 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 9 Oct 2022 12:14:43 +0200 Subject: [PATCH 063/166] v3.5.3+1.0.6 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 8cf33edcde..6778318477 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.5' + '+1.0.6' end def to_a From d09dd82fbdddbf75e4fc79668347d6075bb4c903 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 9 Oct 2022 13:37:40 +0200 Subject: [PATCH 064/166] Remove/update old "tootsuite" references, except those needed for Docker --- .env.production.sample | 4 ++-- CONTRIBUTING.md | 2 +- .../glitch/components/intersection_observer_article.js | 2 +- .../flavours/glitch/features/ui/components/link_footer.js | 4 ++-- .../glitch/features/ui/components/onboarding_modal.js | 2 +- chart/values.yaml | 4 ++-- db/migrate/20170918125918_ids_to_bigints.rb | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 98fa676129..3c4a81ba12 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -17,7 +17,7 @@ LOCAL_DOMAIN=example.com # Use this only if you need to run mastodon on a different domain than the one used for federation. -# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md +# You can read more about this option on https://docs.joinmastodon.org/admin/config/#web-domain # DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. # WEB_DOMAIN=mastodon.example.com @@ -247,7 +247,7 @@ SMTP_FROM_ADDRESS=notifications@example.com # --------------- # Various ways to customize Mastodon's behavior # --------------- - + # Maximum allowed character count MAX_TOOT_CHARS=500 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6411c3d6fe..e8fb938a94 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ See the guidelines below. - - - -You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `tootsuite/mastodon`, reproduced below. +You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `mastodon/mastodon`, reproduced below.
diff --git a/app/javascript/flavours/glitch/components/intersection_observer_article.js b/app/javascript/flavours/glitch/components/intersection_observer_article.js index 88f29892e8..bfab0a25bf 100644 --- a/app/javascript/flavours/glitch/components/intersection_observer_article.js +++ b/app/javascript/flavours/glitch/components/intersection_observer_article.js @@ -94,7 +94,7 @@ export default class IntersectionObserverArticle extends React.Component { // When the browser gets a chance, test if we're still not intersecting, // and if so, set our isHidden to true to trigger an unrender. The point of // this is to save DOM nodes and avoid using up too much memory. - // See: https://github.com/tootsuite/mastodon/issues/2900 + // See: https://github.com/mastodon/mastodon/issues/2900 this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); } diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index da6d621087..7482073e19 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -43,7 +43,7 @@ class LinkFooter extends React.PureComponent { e.stopPropagation(); this.props.onLogout(); - + return false; } @@ -69,7 +69,7 @@ class LinkFooter extends React.PureComponent { values={{ github: {repository} (v{version}), glitchsoc: glitch-soc, - Mastodon: Mastodon }} + Mastodon: Mastodon }} />

diff --git a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js index 81a58e0085..7a7840987d 100644 --- a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js @@ -149,7 +149,7 @@ const PageSix = ({ admin, domain }) => { domain, fork: fork, glitchsoc: Glitchsoc, - Mastodon: Mastodon, + Mastodon: Mastodon, github: GitHub, }} /> diff --git a/chart/values.yaml b/chart/values.yaml index 4b18a9dfa5..48554412f9 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -24,7 +24,7 @@ mastodon: removeMedia: enabled: true schedule: "0 0 * * 0" - # available locales: https://github.com/tootsuite/mastodon/blob/master/config/application.rb#L43 + # available locales: https://github.com/mastodon/mastodon/blob/main/config/application.rb#L71 locale: en local_domain: mastodon.local # Use of WEB_DOMAIN requires careful consideration: https://docs.joinmastodon.org/admin/config/#federation @@ -261,7 +261,7 @@ externalAuth: # search: "., -" # replace: _ -# https://github.com/tootsuite/mastodon/blob/master/Dockerfile#L88 +# https://github.com/mastodon/mastodon/blob/main/Dockerfile#L75 # # if you manually change the UID/GID environment variables, ensure these values # match: diff --git a/db/migrate/20170918125918_ids_to_bigints.rb b/db/migrate/20170918125918_ids_to_bigints.rb index bcb2e9eca5..bf875e4e59 100644 --- a/db/migrate/20170918125918_ids_to_bigints.rb +++ b/db/migrate/20170918125918_ids_to_bigints.rb @@ -80,7 +80,7 @@ class IdsToBigints < ActiveRecord::Migration[5.1] say 'This migration has some sections that can be safely interrupted' say 'and restarted later, and will tell you when those are occurring.' say '' - say 'For more information, see https://github.com/tootsuite/mastodon/pull/5088' + say 'For more information, see https://github.com/mastodon/mastodon/pull/5088' 10.downto(1) do |i| say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true From a33508f687dcc2d0f85e1bfafdcaf93ef8be072a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 10 Oct 2022 06:46:48 +0200 Subject: [PATCH 065/166] v3.5.3+1.0.7 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 6778318477..39cad31fde 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.6' + '+1.0.7' end def to_a From 727da8dcc9b5b898cac85d4ed93e299d4d417fd1 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 13 Oct 2022 06:52:46 +0200 Subject: [PATCH 066/166] v3.5.3+1.0.8 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 39cad31fde..0905200aec 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.7' + '+1.0.8' end def to_a From 19413ebb0be88dadce29514f14087e0aa9851eeb Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 13 Oct 2022 22:39:51 +0200 Subject: [PATCH 067/166] v3.5.3+1.0.9 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 0905200aec..46210b2837 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.8' + '+1.0.9' end def to_a From 6067b452aed710a88535645ab3f7966c85851aab Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 29 Oct 2022 23:41:37 +0200 Subject: [PATCH 068/166] Use mimalloc instead of jemalloc --- dist/mastodon-sidekiq.service | 2 +- dist/mastodon-web.service | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/mastodon-sidekiq.service b/dist/mastodon-sidekiq.service index 324cd5a398..7ff8a6a8ae 100644 --- a/dist/mastodon-sidekiq.service +++ b/dist/mastodon-sidekiq.service @@ -9,7 +9,7 @@ WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="DB_POOL=25" Environment="MALLOC_ARENA_MAX=2" -Environment="LD_PRELOAD=libjemalloc.so" +Environment="LD_PRELOAD=libmimalloc.so" ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 TimeoutSec=15 Restart=always diff --git a/dist/mastodon-web.service b/dist/mastodon-web.service index a5598fdaa8..bc96c0accb 100644 --- a/dist/mastodon-web.service +++ b/dist/mastodon-web.service @@ -8,7 +8,7 @@ User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="PORT=3000" -Environment="LD_PRELOAD=libjemalloc.so" +Environment="LD_PRELOAD=libmimalloc.so" ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb ExecReload=/bin/kill -SIGUSR1 $MAINPID TimeoutSec=15 From 30223ea2246e74748fa69da34eaf0ef349f99386 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 31 Oct 2022 21:47:02 +0100 Subject: [PATCH 069/166] Revert version bump for now Shorten "catstodon" to "cat" in version suffix --- lib/mastodon/version.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 77d90b17d9..b0e7276772 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -5,27 +5,27 @@ module Mastodon module_function def major - 4 + 3 end def minor - 0 + 5 end def patch - 0 + 3 end def flags - 'rc1' + '' end def suffix - '+catstodon' + '+cat' end def suffix_version - '+1.0.9' + '+1.1.0' end def to_a From 06c090fda51144e6236d443018333db2bb2a587e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 31 Oct 2022 22:29:47 +0100 Subject: [PATCH 070/166] Redirect old /web links --- dist/nginx.conf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dist/nginx.conf b/dist/nginx.conf index 716c277dd0..e22acaeeba 100644 --- a/dist/nginx.conf +++ b/dist/nginx.conf @@ -56,6 +56,14 @@ server { try_files $uri @proxy; } + location /web { + return 301 /$is_args$args; + } + + location ~ ^/web(\/.*) { + return 301 $1$is_args$args; + } + # If Docker is used for deployment and Rails serves static files, # then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`. location = sw.js { From d797cefec3db5588e103abbf778124e2b3848a6a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 31 Oct 2022 23:05:20 +0100 Subject: [PATCH 071/166] Change missing profile picture image --- public/avatars/original/missing.png | Bin 3292 -> 8868 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/avatars/original/missing.png b/public/avatars/original/missing.png index 34c8e45e61ce1e1ddeb6a9f69dc1979816a7286e..ebcbc561ee92d9f83bbd28585286d26d356038c7 100644 GIT binary patch literal 8868 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 DU0f%l literal 3292 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 T1m@pU{u3x2G_pqP0p|YzWF96{ From 9c8fb2d475dde8a74c8cd551879fca0a48e1cd7f Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 1 Nov 2022 10:29:44 +0100 Subject: [PATCH 072/166] Improve min poll options, expose value in API --- .../flavours/glitch/features/compose/components/poll_form.js | 2 +- app/javascript/flavours/glitch/reducers/compose.js | 3 ++- .../mastodon/features/compose/components/poll_form.js | 3 ++- app/javascript/mastodon/initial_state.js | 3 +++ app/javascript/mastodon/reducers/compose.js | 4 ++-- app/serializers/initial_state_serializer.rb | 1 + app/serializers/rest/instance_serializer.rb | 1 + app/serializers/rest/v1/instance_serializer.rb | 2 ++ 8 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/javascript/flavours/glitch/features/compose/components/poll_form.js b/app/javascript/flavours/glitch/features/compose/components/poll_form.js index d0c39c35a4..0a52ecd310 100644 --- a/app/javascript/flavours/glitch/features/compose/components/poll_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/poll_form.js @@ -81,7 +81,7 @@ class Option extends React.PureComponent {
- +
); diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index 6aef0ecf10..46bcd8146e 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -60,6 +60,7 @@ import { me, defaultContentType } from 'flavours/glitch/initial_state'; import { overwrite } from 'flavours/glitch/utils/js_helpers'; import { unescapeHTML } from 'flavours/glitch/utils/html'; import { recoverHashtags } from 'flavours/glitch/utils/hashtag'; +import { pollLimits } from 'flavours/glitch/initial_state'; const totalElefriends = 3; @@ -127,7 +128,7 @@ const initialState = ImmutableMap({ }); const initialPoll = ImmutableMap({ - options: ImmutableList(['']), + options: ImmutableList(new Array(pollLimits.min_options).fill('')), expires_in: 24 * 3600, multiple: false, }); diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js index 1628aa6b31..69aee9d981 100644 --- a/app/javascript/mastodon/features/compose/components/poll_form.js +++ b/app/javascript/mastodon/features/compose/components/poll_form.js @@ -7,6 +7,7 @@ import IconButton from 'mastodon/components/icon_button'; import Icon from 'mastodon/components/icon'; import AutosuggestInput from 'mastodon/components/autosuggest_input'; import classNames from 'classnames'; +import { pollLimits } from 'mastodon/initial_state'; const messages = defineMessages({ option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' }, @@ -102,7 +103,7 @@ class Option extends React.PureComponent {
- +
); diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index bb05dafdf7..b21567cd0a 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -129,4 +129,7 @@ export const version = getMeta('version'); export const translationEnabled = getMeta('translation_enabled'); export const languages = initialState?.languages; +// CatCatNya~ specific setting for vanilla flavor +export const pollLimits = (initialState && initialState.poll_limits); + export default initialState; diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 55a07249dc..82bc64a8ba 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -52,7 +52,7 @@ import { STORE_HYDRATE } from '../actions/store'; import { REDRAFT } from '../actions/statuses'; import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import uuid from '../uuid'; -import { me } from '../initial_state'; +import { me, pollLimits } from '../initial_state'; import { unescapeHTML } from '../utils/html'; const initialState = ImmutableMap({ @@ -95,7 +95,7 @@ const initialState = ImmutableMap({ }); const initialPoll = ImmutableMap({ - options: ImmutableList(['']), + options: ImmutableList(new Array(pollLimits.min_options).fill('')), expires_in: 24 * 3600, multiple: false, }); diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 7e57ce4bf0..d88acf1edc 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -17,6 +17,7 @@ class InitialStateSerializer < ActiveModel::Serializer def poll_limits { + min_options: PollValidator::MIN_OPTIONS, max_options: PollValidator::MAX_OPTIONS, max_option_chars: PollValidator::MAX_OPTION_CHARS, min_expiration: PollValidator::MIN_EXPIRATION, diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 5ae1099d04..23525116d6 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -67,6 +67,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer }, polls: { + min_options: PollValidator::MIN_OPTIONS, max_options: PollValidator::MAX_OPTIONS, max_characters_per_option: PollValidator::MAX_OPTION_CHARS, min_expiration: PollValidator::MIN_EXPIRATION, diff --git a/app/serializers/rest/v1/instance_serializer.rb b/app/serializers/rest/v1/instance_serializer.rb index 389ec7dffc..0262f10d01 100644 --- a/app/serializers/rest/v1/instance_serializer.rb +++ b/app/serializers/rest/v1/instance_serializer.rb @@ -42,6 +42,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer def poll_limits { + min_options: PollValidator::MIN_OPTIONS, max_options: PollValidator::MAX_OPTIONS, max_option_chars: PollValidator::MAX_OPTION_CHARS, min_expiration: PollValidator::MIN_EXPIRATION, @@ -91,6 +92,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer }, polls: { + min_options: PollValidator::MIN_OPTIONS, max_options: PollValidator::MAX_OPTIONS, max_characters_per_option: PollValidator::MAX_OPTION_CHARS, min_expiration: PollValidator::MIN_EXPIRATION, From 025e6ce6438c1bdb2147c6e968c0ff43ca8ff81e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 1 Nov 2022 10:30:21 +0100 Subject: [PATCH 073/166] v3.5.3+1.1.1 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index b0e7276772..cb9a68b8f4 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.0' + '+1.1.1' end def to_a From b127fa6d36fd7a9fd6308aa133488506da45e1d3 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 1 Nov 2022 11:54:45 +0100 Subject: [PATCH 074/166] Assign lowest possible scheduling priorities to Sidekiq --- dist/mastodon-sidekiq.service | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dist/mastodon-sidekiq.service b/dist/mastodon-sidekiq.service index 7ff8a6a8ae..5150121960 100644 --- a/dist/mastodon-sidekiq.service +++ b/dist/mastodon-sidekiq.service @@ -47,6 +47,11 @@ SystemCallFilter=~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileg SystemCallFilter=@chown SystemCallFilter=pipe SystemCallFilter=pipe2 +Nice=19 +CPUSchedulingPolicy=idle +CPUSchedulingPriority=1 +IOSchedulingClass=idle +IOSchedulingPriority=7 ReadWritePaths=/home/mastodon/live [Install] From e1d17e6f34dd0adad95d0589979ae437b02b0e3f Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 1 Nov 2022 12:05:14 +0100 Subject: [PATCH 075/166] Update README.md Editing is now enabled in vanilla and glitch-soc --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 80fedb8c44..646f67f217 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ I highly suggest only ever running the `main` branch in production! - sounds/boop.ogg
You might want to revert these to the upstream files (or your own versions!) if you decide to use this fork for your own instance. - The web frontend emoji picker is a blobcat instead of the joy emoji. -- Editing posts is enabled in the web frontend (thanks, meave, [for the hint](https://toot.site/@meave/108515761669028663)). - The rate limits for authenticated users have been relaxed a bit. - The API endpoint `/api/v1/custom_emojis` is no longer affected by AUTHORIZED_FETCH, allowing anyone to copy custom emojis. - Allow higher resolution images. (4096x4096 instead of the previous limit of 1920x1080) From c3bffb95b62eb2ce454729dc4db9cdb3aa276b94 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 2 Nov 2022 21:16:10 +0100 Subject: [PATCH 076/166] Vanilla flavor: Reduce amount of data stored in initial state for min_options on polls --- .../mastodon/features/compose/components/poll_form.js | 2 +- app/javascript/mastodon/initial_state.js | 2 +- app/javascript/mastodon/reducers/compose.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js index 69aee9d981..1f3764e65d 100644 --- a/app/javascript/mastodon/features/compose/components/poll_form.js +++ b/app/javascript/mastodon/features/compose/components/poll_form.js @@ -103,7 +103,7 @@ class Option extends React.PureComponent {
- +
); diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index dc72be14d0..6508bfdb78 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -133,6 +133,6 @@ export const languages = initialState?.languages; export const maxChars = (initialState && initialState.max_toot_chars) || 500; // CatCatNya~ specific settings -export const pollLimits = (initialState && initialState.poll_limits); +export const pollMinOptions = (initialState && initialState.poll_limits && initialState.poll_limits.min_options) || 2; export default initialState; diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 82bc64a8ba..4d4aaf112f 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -95,7 +95,7 @@ const initialState = ImmutableMap({ }); const initialPoll = ImmutableMap({ - options: ImmutableList(new Array(pollLimits.min_options).fill('')), + options: ImmutableList(new Array(pollMinOptions).fill('')), expires_in: 24 * 3600, multiple: false, }); From 3bc0621c3c9ea14a3a1219dc2712c54b68b28f8f Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Wed, 2 Nov 2022 21:16:39 +0100 Subject: [PATCH 077/166] v3.5.3+1.1.2 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index cb9a68b8f4..38453cb032 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.1' + '+1.1.2' end def to_a From f0422fb4168440693860ee714748125e5bb7aeb8 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 5 Nov 2022 12:26:41 +0100 Subject: [PATCH 078/166] v3.5.3+1.1.3 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 38453cb032..d7c6f1170a 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.2' + '+1.1.3' end def to_a From 41f1a81ab72bde1baaef528f1b19d3ff34faeafe Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 00:39:33 +0100 Subject: [PATCH 079/166] Change AUTHORIZED_FETCH to not block unauthenticated REST API access Port of vanilla commit: https://github.com/mastodon/mastodon/pull/19803 --- app/controllers/api/base_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index c46fde65b2..3f3e1ca7bd 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -133,7 +133,7 @@ class Api::BaseController < ApplicationController end def disallow_unauthenticated_api_access? - authorized_fetch_mode? + ENV['DISALLOW_UNAUTHENTICATED_API_ACCESS'] == 'true' || Rails.configuration.x.whitelist_mode end private From da8c3bed5cb3f9515683b53f03701e076399cf6a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:13:19 +0100 Subject: [PATCH 080/166] Don't show trending statuses in public web UI --- app/controllers/api/v1/trends/statuses_controller.rb | 1 + .../glitch/features/ui/components/navigation_panel.js | 4 ++-- .../mastodon/features/ui/components/navigation_panel.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb index c275d5fc81..5a7a41e3a7 100644 --- a/app/controllers/api/v1/trends/statuses_controller.rb +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Api::V1::Trends::StatusesController < Api::BaseController + before_action :require_user!, only: [:show], if: :require_auth? before_action :set_statuses after_action :insert_pagination_headers diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js index affb5ec478..bb444fa654 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js @@ -55,9 +55,9 @@ class NavigationPanel extends React.Component { )} {showTrends ? ( - + (signedIn || timelinePreview) ? : <> ) : ( - + (signedIn || timelinePreview) ? : <> )} {(signedIn || timelinePreview) && ( diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js index 4e9e39e2f5..2959cfcfed 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.js +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js @@ -59,9 +59,9 @@ class NavigationPanel extends React.Component { )} {showTrends ? ( - + (signedIn || timelinePreview) ? : <> ) : ( - + (signedIn || timelinePreview) ? : <> )} {(signedIn || timelinePreview) && ( From 7e0108bccd8b7956a7f6736fde9ddf5efcf672f4 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:18:29 +0100 Subject: [PATCH 081/166] Fix wrong "only" target in previous commit --- app/controllers/api/v1/trends/statuses_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb index 5a7a41e3a7..b88fad8811 100644 --- a/app/controllers/api/v1/trends/statuses_controller.rb +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Trends::StatusesController < Api::BaseController - before_action :require_user!, only: [:show], if: :require_auth? + before_action :require_user!, only: [:index], if: :require_auth? before_action :set_statuses after_action :insert_pagination_headers From b0be4764b4fb8f6adaafbb2d583c74ab96354600 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:30:46 +0100 Subject: [PATCH 082/166] Keep showing explore tab, only statuses are hidden --- .../glitch/features/ui/components/navigation_panel.js | 4 ++-- .../mastodon/features/ui/components/navigation_panel.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js index bb444fa654..e566563dde 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js @@ -55,9 +55,9 @@ class NavigationPanel extends React.Component { )} {showTrends ? ( - (signedIn || timelinePreview) ? : <> + ) : ( - (signedIn || timelinePreview) ? : <> + )} {(signedIn || timelinePreview) && ( diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js index 2959cfcfed..6e435dfbad 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.js +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js @@ -59,9 +59,9 @@ class NavigationPanel extends React.Component { )} {showTrends ? ( - (signedIn || timelinePreview) ? : <> + ) : ( - (signedIn || timelinePreview) ? : <> + )} {(signedIn || timelinePreview) && ( From 5ab1ac972b6cdad865ec474afe228a34bd48bba2 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:41:55 +0100 Subject: [PATCH 083/166] Show about tab if explore tab wouldn't show any statuses --- app/javascript/flavours/glitch/features/ui/index.js | 3 ++- app/javascript/mastodon/features/ui/index.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 3d385eee2b..f7fc231740 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -65,6 +65,7 @@ import Header from './components/header'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '../../../glitch/components/status'; +import { timelinePreview } from 'flavours/glitch/initial_state'; const messages = defineMessages({ beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' }, @@ -177,7 +178,7 @@ class SwitchingColumnsArea extends React.PureComponent { } } else if (singleUserMode && owner && initialState?.accounts[owner]) { redirect = ; - } else if (showTrends) { + } else if (showTrends && (signedIn || timelinePreview)) { redirect = ; } else { redirect = ; diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 298f2111de..bfe6b5c15f 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -54,7 +54,7 @@ import { About, PrivacyPolicy, } from './util/async-components'; -import initialState, { me, owner, singleUserMode, showTrends } from '../../initial_state'; +import initialState, { me, owner, singleUserMode, showTrends, timelinePreview } from '../../initial_state'; import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import Header from './components/header'; @@ -163,7 +163,7 @@ class SwitchingColumnsArea extends React.PureComponent { } } else if (singleUserMode && owner && initialState?.accounts[owner]) { redirect = ; - } else if (showTrends) { + } else if (showTrends && (signedIn || timelinePreview)) { redirect = ; } else { redirect = ; From 9dafedaecd0e9f08b6a83cc97c14c90dd2adb066 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:42:17 +0100 Subject: [PATCH 084/166] Remove misc duplicate declarations --- .../flavours/glitch/features/getting_started/index.js | 1 - .../flavours/glitch/features/getting_started_misc/index.js | 1 - app/javascript/flavours/glitch/features/ui/index.js | 1 - lib/mastodon/version.rb | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js index f9d79013b5..cfa430e090 100644 --- a/app/javascript/flavours/glitch/features/getting_started/index.js +++ b/app/javascript/flavours/glitch/features/getting_started/index.js @@ -35,7 +35,6 @@ const messages = defineMessages({ follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' }, - lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists_subheading: { id: 'column_subheading.lists', defaultMessage: 'Lists' }, misc: { id: 'navigation_bar.misc', defaultMessage: 'Misc' }, menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.js index de354d6b11..735116cfc3 100644 --- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js +++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.js @@ -19,7 +19,6 @@ const messages = defineMessages({ info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, show_me_around: { id: 'getting_started.onboarding', defaultMessage: 'Show me around' }, pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' }, - info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' }, featured_users: { id: 'navigation_bar.featured_users', defaultMessage: 'Featured users' }, }); diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index f7fc231740..0da444ef78 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -76,7 +76,6 @@ const mapStateToProps = state => ({ hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0, hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0, canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4, - layout: state.getIn(['meta', 'layout']), layout_local_setting: state.getIn(['local_settings', 'layout']), isWide: state.getIn(['local_settings', 'stretch']), navbarUnder: state.getIn(['local_settings', 'navbar_under']), diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index d7c6f1170a..863c17a363 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.3' + '+1.1.4' end def to_a From 52f9449b3d97d0e6cd8e4bf5f1b5630908ae057a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:50:59 +0100 Subject: [PATCH 085/166] Update outdated link to source --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 863c17a363..21d6a79541 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -37,7 +37,7 @@ module Mastodon end def repository - ENV.fetch('GITHUB_REPOSITORY', 'kescherCode/catstodon') + ENV.fetch('GITHUB_REPOSITORY', 'CatCatNya/catstodon') end def source_base_url From e326d44f2973f3f1a393898ef3c4c7b54c65175d Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 01:56:57 +0100 Subject: [PATCH 086/166] Add missing require_auth? in StatusController --- app/controllers/api/v1/trends/statuses_controller.rb | 4 ++++ lib/mastodon/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb index b88fad8811..ef36f906d6 100644 --- a/app/controllers/api/v1/trends/statuses_controller.rb +++ b/app/controllers/api/v1/trends/statuses_controller.rb @@ -55,4 +55,8 @@ class Api::V1::Trends::StatusesController < Api::BaseController def records_continue? @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT) end + + def require_auth? + !Setting.timeline_preview + end end diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 21d6a79541..cf4db6ee5b 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.4' + '+1.1.5' end def to_a From c2e83b13545272495dcee26e3358b875cddfbc68 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 12:48:29 +0100 Subject: [PATCH 087/166] Add null check on application in dispute viewer --- app/views/disputes/strikes/show.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml index 1be50331a4..2a0b0af1d9 100644 --- a/app/views/disputes/strikes/show.html.haml +++ b/app/views/disputes/strikes/show.html.haml @@ -59,8 +59,9 @@ = media_attachment.file_file_name .strike-card__statuses-list__item__meta %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) - · - = status.application.name + - if status.application != nil + · + = status.application.name - else .one-liner= t('disputes.strikes.status', id: status_id) .strike-card__statuses-list__item__meta From 996267fc8bb76ed73e7e6601272f3f6249fe655e Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 13:02:47 +0100 Subject: [PATCH 088/166] Fix vanilla flavor being broken due to incorrect imports --- .../mastodon/features/compose/components/poll_form.js | 2 +- app/javascript/mastodon/reducers/compose.js | 2 +- lib/mastodon/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js index 1f3764e65d..d69bbfae92 100644 --- a/app/javascript/mastodon/features/compose/components/poll_form.js +++ b/app/javascript/mastodon/features/compose/components/poll_form.js @@ -7,7 +7,7 @@ import IconButton from 'mastodon/components/icon_button'; import Icon from 'mastodon/components/icon'; import AutosuggestInput from 'mastodon/components/autosuggest_input'; import classNames from 'classnames'; -import { pollLimits } from 'mastodon/initial_state'; +import { pollMinOptions } from 'mastodon/initial_state'; const messages = defineMessages({ option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' }, diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 4d4aaf112f..9a6de4e25e 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -52,7 +52,7 @@ import { STORE_HYDRATE } from '../actions/store'; import { REDRAFT } from '../actions/statuses'; import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import uuid from '../uuid'; -import { me, pollLimits } from '../initial_state'; +import { me, pollMinOptions } from '../initial_state'; import { unescapeHTML } from '../utils/html'; const initialState = ImmutableMap({ diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index cf4db6ee5b..823ad49700 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.5' + '+1.1.6' end def to_a From d869072fcd76cf431e6ff42038ec720d0a78bd72 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 14:27:01 +0100 Subject: [PATCH 089/166] change null check in dispute viewer to be unless --- app/views/disputes/strikes/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml index 2a0b0af1d9..4a3005f72a 100644 --- a/app/views/disputes/strikes/show.html.haml +++ b/app/views/disputes/strikes/show.html.haml @@ -59,7 +59,7 @@ = media_attachment.file_file_name .strike-card__statuses-list__item__meta %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) - - if status.application != nil + - unless status.application.nil? · = status.application.name - else From cf746c6dedde61c6c215180e444fd02d44751e2a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 6 Nov 2022 21:02:57 +0100 Subject: [PATCH 090/166] Bump to v3.5.3+1.2.0 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 823ad49700..08b43df75b 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.1.6' + '+1.2.0' end def to_a From 5ee53d06a2de05f3b898ee09e5c443575b8074b9 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 8 Nov 2022 07:30:27 +0100 Subject: [PATCH 091/166] Bump to v3.5.3+1.2.1 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 08b43df75b..11dd079b15 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.2.0' + '+1.2.1' end def to_a From 1eadfddbd21045960fb5ab5871fc68e232445409 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 10 Nov 2022 21:11:44 +0100 Subject: [PATCH 092/166] Bump to v3.5.3+1.2.2 --- dist/nginx.conf | 8 -------- lib/mastodon/version.rb | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/dist/nginx.conf b/dist/nginx.conf index 4abcf7ff03..5bc960e256 100644 --- a/dist/nginx.conf +++ b/dist/nginx.conf @@ -56,14 +56,6 @@ server { try_files $uri @proxy; } - location /web { - return 301 /$is_args$args; - } - - location ~ ^/web(\/.*) { - return 301 $1$is_args$args; - } - # If Docker is used for deployment and Rails serves static files, # then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`. location = /sw.js { diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 11dd079b15..86d78f038e 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.2.1' + '+1.2.2' end def to_a From 5a70c18ad7c0f62222f3020ea287f188c8cca905 Mon Sep 17 00:00:00 2001 From: fef Date: Fri, 11 Nov 2022 01:10:25 +0000 Subject: [PATCH 093/166] add homogay flavour --- .../flavours/glitch/styles/homogay.scss | 375 ++++++++++++++++++ .../skins/glitch/homogay/common.scss | 1 + app/javascript/skins/glitch/homogay/names.yml | 12 + 3 files changed, 388 insertions(+) create mode 100644 app/javascript/flavours/glitch/styles/homogay.scss create mode 100644 app/javascript/skins/glitch/homogay/common.scss create mode 100644 app/javascript/skins/glitch/homogay/names.yml diff --git a/app/javascript/flavours/glitch/styles/homogay.scss b/app/javascript/flavours/glitch/styles/homogay.scss new file mode 100644 index 0000000000..3b89c5d7b7 --- /dev/null +++ b/app/javascript/flavours/glitch/styles/homogay.scss @@ -0,0 +1,375 @@ +@import 'index'; + +/* customize ya stuff */ +:root { + --border-radius: 8px; + + /* rgb for transparency to work */ + --text-color: 217, 225, 232; + --text-color-secondary: 96, 105, 132; /* less bright, for unimportant bits */ + + --background-color: 15, 11, 23; + --background-color-brighter: 22, 17, 33; + --app-background-color: 10, 5, 15; /* used only for the VERY background in the back */ + + --accent-color: 222, 24, 163; + --accent-color-secondary: 110, 69, 97; /* less saturated ver of --accent-color */ + --accent-color-bright: 255, 64, 198; +} + +.account__avatar-overlay-base, .account__avatar-overlay-overlay, .account__avatar { + border-radius: var(--border-radius); +} + +/* roundening shenanigans */ + +.drawer > div, nav, .search, .drawer__header a, .drawer--header a, .search__input { + border-radius: var(--border-radius) !important; +} + +.column-header, .column-back-button, .navigation-panel .column-link:nth-child(1), .navigation-panel .column-link:nth-child(11), .navigation-bar { + border-radius: var(--border-radius) var(--border-radius) 0 0; +} +.column > .scrollable, .getting-started, .navigation-panel .column-link:nth-child(9), .navigation-panel .column-link:nth-child(12) { + border-radius: 0 0 var(--border-radius) var(--border-radius); +} + +.button { + border-radius: var(--border-radius); +} + +/* standard fg/bg color changes */ + +.drawer__inner, .drawer__inner__mastodon, .drawer__header, .drawer--header, .actions-modal, .block-modal, .boost-modal, .confirmation-modal, .mute-modal, .report-modal, article, .getting-started, .column-subheading, .column-link, .column-subheading, .column-link, .emoji-mart-scroll, .emoji-mart-search, .emoji-mart-category-label > span, .emoji-picker-dropdown__menu, .scrollable, .empty-column-indicator, .column-inline-form, .dropdown-menu, .dropdown-menu__item a, .account__header__fields dt, .search-popout, .confirmation-modal__action-bar, .reactions-bar__item, .emoji-picker-dropdown__modifiers__menu, .content-wrapper, .sidebar-wrapper--empty, .regeneration-indicator, .tabs-bar, .navigation-bar, .trends__header, .modal-layout { + background-color: rgb(var(--background-color)) !important; + color: rgb(var(--text-color)) !important; +} +.glitch.local-settings__navigation, .glitch.local-settings__navigation__item, .glitch.local-settings__page, .glitch.local-settings { + background-color: rgb(var(--background-color)); + color: rgb(var(--text-color)); +} + +.modal-layout, .modal-layout__mastodon > * { + background-image: none; +} + +.account__section-headline a.active::after, .account__section-headline button.active::after, .notification__filter-bar a.active::after, .notification__filter-bar button.active::after, .account__section-headline a.active::after, .account__section-headline a.active::before, .account__section-headline button.active::after, .account__section-headline button.active::before, .notification__filter-bar a.active::after, .notification__filter-bar a.active::before, .notification__filter-bar button.active::after, .notification__filter-bar button.active::before { + border-color: transparent transparent rgb(var(--background-color)); +} + +.dropdown-menu__arrow { + border-bottom-color: rgb(var(--background-color)) !important; +} +.dropdown-menu__arrow.top { + border-top-color: rgb(var(--background-color)) !important; +} + +.reply-indicator__content, .status__content, .reply-indicator__display-name, .privacy-dropdown__option__icon, .composer--options--dropdown--content--item .icon, .composer--reply > .content { + color: rgb(var(--text-color)) !important; +} + +html { + scrollbar-color: rgb(var(--background-color-brighter)) rgba(0,0,0,0.1); +} + +.tabs-bar__wrapper { + background: rgb(var(--app-background-color)); +} + +.column-header, .column-header__button, .account__section-headline, .notification__filter-bar > button, .emoji-mart-bar, .column-back-button, .column-header__back-button, .announcements, .column-header__collapsible-inner, .status.status-direct:not(.read), .notification__filter-bar, .glitch.local-settings__page { + background-color: rgb(var(--background-color-brighter)) !important; + border-bottom: none; +} + +.reply-indicator, .emoji-picker-dropdown__modifiers__menu button:hover, .compose-form .compose-form__buttons-wrapper, .composer--options-wrapper, .compose-form__poll-wrapper select, .flash-message, .card__bar, .card > a:hover .card__bar, .glitch.local-settings__navigation__item:hover { + background-color: rgb(var(--background-color-brighter)); +} + +.columns-area, .app-body, .getting-started__wrapper { + background-color: rgb(var(--app-background-color)); +} + +.privacy-dropdown__option__content strong, .composer--options--dropdown--content strong, .character-counter, .report-modal__comment .setting-text-label, .compose-form__poll-wrapper select { + color: rgb(var(--text-color)) !important; +} + +input, textarea, .compose-form__modifiers, .privacy-dropdown__dropdown, .composer--options--dropdown--content, .privacy-dropdown__value { + background-color: rgb(var(--background-color-brighter)) !important; + color: rgb(var(--text-color)) !important; +} + +.compose-form__buttons-wrapper, .admin-wrapper .sidebar-wrapper__inner, .admin-wrapper .sidebar ul a:hover, .admin-wrapper .sidebar ul a, .admin-wrapper .sidebar ul a.selected, .account__disclaimer, .account__action-bar-links { + background-color: rgb(var(--background-color-brighter)); +} + +.compose-form .compose-form__autosuggest-wrapper { + border-top-left-radius: var(--border-radius); + border-top-right-radius: var(--border-radius); +} + +.compose-form .compose-form__buttons-wrapper { + border-bottom-left-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); +} + +.compose-form .spoiler-input__input { + border-radius: var(--border-radius); +} + +a.status-card { + border-radius: var(--border-radius); +} + +.detailed-status, .detailed-status__action-bar, .account__header__bar, .focusable:focus { + background-color: rgb(var(--background-color-brighter)) !important; + border-bottom: none; + border-top: none; +} + +.status.collapsed .status__content::after { + background: linear-gradient(rgba(var(--background-color),0), rgba(var(--background-color),0)) !important; +} + +.dismissable-banner { + /*border-top: 1px solid #393f4f;*/ + background-color: rgb(var(--background-color-brighter)); +} + +.status-card__image { + background-color: rgb(var(--background-color-brighter)); +} + +/* accent color changes */ + +.button, .react-toggle--checked .react-toggle-track, .react-toggle--checked:hover .react-toggle-track, .react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track, .button.logo-button, .emoji-mart-anchor-bar, .loading-bar, .icon-with-badge__badge, .video-player__volume__current, .video-player__volume__handle, .upload-progress__tracker, .video-player__seek__buffer, .video-player__seek__progress, .floating-action-button { + background-color: rgb(var(--accent-color)); +} +.react-toggle--checked .react-toggle-thumb, .compose-form__sensitive-button .checkbox, .filters .filter-subset a.selected, .account__action-bar__tab.active, .tabs-bar__link.active, .notification.unread::before, .status.unread::before { + border-color: rgb(var(--accent-color)); +} + +.text-icon-button, .icon-button.inverted, button.inverted:hover, .icon-button, .icon-button:hover, .status__action-bar__counter__label, .text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .icon-button.disabled, .composer--options--dropdown.open > .value { + color: rgb(var(--accent-color-secondary)); +} +.status__info__icons i { + color: rgb(var(--accent-color-secondary)) !important; +} + +.status__content__spoiler-link { + background-color: rgb(var(--accent-color-secondary)) !important; +} +.column-header__wrapper.active::before { + background: radial-gradient(ellipse, rgba(var(--accent-color),.23) 0%, rgba(var(--accent-color),0) 60%); +} +.column-header__wrapper.active { + box-shadow: 0 1px 0 rgba(var(--accent-color),.3); +} + +.compose-form__sensitive-button .checkbox.active, .poll__chart.leading { + border-color: rgb(var(--accent-color)); + background-color: rgb(var(--accent-color)); +} + +.poll__chart { + background-color: rgb(var(--accent-color-secondary)); +} + +.column-header.active .column-header__icon { + text-shadow: 0 0 10px rgba(var(--accent-color),.4); +} + +.text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .drawer__header a:hover, .drawer--header a:hover, .drawer--header a:focus, .icon-button:hover, .reactions-bar__item:hover { + background-color: rgba(var(--accent-color-secondary), .1); +} + +.icon-button.inverted:active, .icon-button.inverted:focus, .icon-button.inverted:hover, .reactions-bar__item.active { + background-color: rgba(var(--accent-color-secondary), .25); + color: rgb(var(--accent-color-secondary)); +} + +.button:active, .button:focus, .button:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a:hover, .simple_form .block-button:hover, .simple_form .button:hover, .simple_form button:hover, .button.logo-button:active, .button.logo-button:focus, .button.logo-button:hover, .floating-action-button:hover, .glitch.local-settings__navigation__item.active:hover { + background-color: rgb(var(--accent-color-bright)); +} + +.privacy-dropdown__option.active, .composer--options--dropdown--content--item.active, .privacy-dropdown__option:hover, .composer--options--dropdown--content--item:hover, .privacy-dropdown__option.active:hover, .composer--options--dropdown--content.active:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a, .simple_form .block-button, .simple_form .button, .simple_form button, .simple_form .block-button:active, .simple_form .block-button:focus, .simple_form .button:active, .simple_form .button:focus, .simple_form button:active, .simple_form button:focus, .composer--options--dropdown.open > .value, .glitch.local-settings__navigation__item.active { + background-color: rgb(var(--accent-color)); +} + +.status__info__icons .icon-button.active i, .tabs-bar__link.active, .status__content a { + color: rgb(var(--accent-color)) !important; +} + + +.trends__item__sparkline path:last-child { + stroke: rgb(var(--accent-color)) !important; +} +.trends__item__sparkline path:first-child { + fill: rgb(var(--accent-color-secondary)) !important; +} + +a.u-url, .status-link, .column-header__back-button, .status__content__read-more-button, .column-header.active .column-header__icon, .column-link.active, .account__section-headline a.active, .account__section-headline button.active, .notification__filter-bar a.active, .notification__filter-bar button.active, .account__header__content a, .account__header__bio .account__header__fields a, .reactions-bar__item.active .reactions-bar__item__count, .emoji-mart-anchor-selected, .reply-indicator__content a, .compose-form .compose-form__warning a, .text-icon-button.active, .icon-button.inverted.active, .drawer__tab:hover, .icon-button.active, .column-back-button, .filters .filter-subset a.selected, .admin-wrapper .content .muted-hint a, body .muted-hint a, .table a, .notification__message .fa, .drawer--header a:hover, .drawer--header a:focus { + color: rgb(var(--accent-color)) !important; +} + +.language-dropdown__dropdown__results__item.active { + background: rgb(var(--accent-color)); +} +.language-dropdown__dropdown__results__item.active:hover, .language-dropdown__dropdown__results__item:hover { + background: rgb(var(--accent-color-bright)); +} + +.emoji-mart-search > input { + border-color: rgba(255, 255, 255, 0.1); +} + +/* fixes */ + +/* boost hack, v2 */ +/* https://codepen.io/sosuke/pen/Pjoqqp */ +button.icon-button i.fa-retweet { + filter: brightness(0) invert(38%) sepia(10%) saturate(1594%) hue-rotate(266deg) brightness(97%) contrast(89%); /* accent-color-secondary */ + color: transparent !important; +} +button.icon-button.active i.fa-retweet { + filter: invert(19%) sepia(75%) saturate(7362%) hue-rotate(308deg) brightness(98%) contrast(88%); /* accent-color */ +} +button.icon-button.disabled i.fa-retweet, button.icon-button.disabled i.fa-lock { + filter: invert(38%) sepia(10%) saturate(1594%) hue-rotate(266deg) brightness(50%) contrast(89%); /* accent-color-secondary with brightness set to 50% */ +} + +.load-more:hover, .mbstobon-2 .drawer__inner__mastodon, .mbstobon-1 .drawer__inner__mastodon, .mbstobon-0 .drawer__inner__mastodon { + background: inherit; +} + +.account__action-bar__tab, .account__action-bar { + border: none; +} + +.notification__filter-bar, .account__header__bar, .admin-wrapper .content-heading, .admin-wrapper .content h4, .tabs-bar__link:not(.active) { + border-bottom: none; +} + +.dropdown-menu__separator, hr { + opacity: 0; +} +.compose-form .autosuggest-textarea__textarea, .compose-form .spoiler-input__input, .compose-panel .compose-form__autosuggest-wrapper, .mbstobon-3 .drawer__inner__mastodon { + background: transparent; +} +.status, .account, .account__header__fields dl, .account__header__fields, .account__header__bio .account__header__fields, .glitch.local-settings__navigation__item { + border-top: none; + border-bottom: none; +} + +.report-modal__container, .report-modal__comment, .report-modal__comment .setting-text__wrapper { + border-color: rgba(0, 0, 0, 0) !important; +} + +.drawer__inner__mastodon { + background: inherit; +} + +/* misc */ +.column-link:hover, .dropdown-menu__item a:active, .dropdown-menu__item a:focus, .dropdown-menu__item a:hover { + color: rgb(var(--accent-color-bright)) !important; +} + +.notification__filter-bar button.active, .account__section-headline .active { + border-bottom: 3px solid rgb(var(--accent-color)); +} +.notification__filter-bar button:not(.active):hover { + top: -3px; +} +.account__section-headline a.active::after, .account__section-headline a.active::before { + display: none; +} + +.account__header__extra__links a:hover { + text-decoration: underline; +} +.account__section-headline a:hover, .confirmation-modal__cancel-button span { + color: #fff; +} + +.notification__filter-bar button.active::after { + opacity: 0; +} +.notification__filter-bar button.active::before { + opacity: 0; +} + +.column-link__badge, .column-subheading { + background-color: rgb(var(--accent-color)); + animation-name: flash; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-direction: alternate-reverse; +} + +@keyframes flash { + from {background-color: rgb(var(--accent-color));} + to {background-color: rgb(var(--accent-color-secondary));} +} + +.reply-indicator { + max-height: 38px; + overflow-y: hidden; + transition: max-height 1s; +} +.reply-indicator:hover { + max-height: 100%; +} +.reply-indicator:before { + content: 'Replying to:'; + font-size: 12px; + color: rgb(var(--text-color-secondary)); +} + +.link-footer p:last-of-type:after { + content: '\A be gay do crime uwu'; + white-space: pre; +} + + +/* public/static css */ +/* for pages like /@username */ + +.public-layout .public-account-header__tabs__tabs .counter.active::after { + border-bottom-color: rgb(var(--accent-color)); +} +.public-layout .public-account-bio .account__header__fields a { + color: rgb(var(--accent-color)); +} +.public-layout .header .nav-button { + color: #fff; + background-color: rgb(var(--accent-color)); +} +.public-layout .header .nav-button:active, .public-layout .header .nav-button:focus, .public-layout .header .nav-button:hover { + background-color: rgb(var(--accent-color-bright)) +} +.public-layout .activity-stream .entry, .hero-widget__text, .table-of-contents { + background-color: rgb(var(--background-color)); +} +body { + background-color: rgb(var(--app-background-color)); +} +.public-layout .public-account-header__tabs__tabs .counter { + border-right: none; +} +.public-layout .public-account-bio, .public-layout .public-account-header__bar::before, .public-layout .header, .directory__tag > a, .directory__tag > div, .directory__tag > a:active, .directory__tag > a:focus, .directory__tag > a:hover, .public-layout .header .brand:hover, .landing-page__call-to-action, .box-widget { + background-color: rgb(var(--background-color-brighter)); +} +.public-layout .display-name, .status__relative-time time, .status__relative-time { + color: rgb(var(--text-color-secondary)); +} +.rich-formatting, .rich-formatting p { + color: rgb(var(--text-color)); +} +.rich-formatting table tbody tr, .rich-formatting table thead tr, .notification-follow, .notification-follow-request { + border-bottom: none; +} + +// https://types.pl/@haskal/106569437674907815 +.search-popout em { + color: rgb(var(--accent-color)) !important; +} + diff --git a/app/javascript/skins/glitch/homogay/common.scss b/app/javascript/skins/glitch/homogay/common.scss new file mode 100644 index 0000000000..4dfd68d267 --- /dev/null +++ b/app/javascript/skins/glitch/homogay/common.scss @@ -0,0 +1 @@ +@import 'flavours/glitch/styles/homogay'; diff --git a/app/javascript/skins/glitch/homogay/names.yml b/app/javascript/skins/glitch/homogay/names.yml new file mode 100644 index 0000000000..0b29f568d9 --- /dev/null +++ b/app/javascript/skins/glitch/homogay/names.yml @@ -0,0 +1,12 @@ +de: + skins: + glitch: + homogay: homogay (von @anna@girldick.gay) +en: + skins: + glitch: + homogay: homogay (by @anna@girldick.gay) +es: + skins: + glitch: + homogay: homogay (por @anna@girldick.gay) From 0caa35c1f9fbc97a751d1019d7b00e683cc6e084 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 11 Nov 2022 21:19:56 +0100 Subject: [PATCH 094/166] Apparently, the segfaults were not jemalloc's fault. --- dist/mastodon-sidekiq.service | 2 +- dist/mastodon-streaming.service | 1 + dist/mastodon-web.service | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dist/mastodon-sidekiq.service b/dist/mastodon-sidekiq.service index 5150121960..3f4c806048 100644 --- a/dist/mastodon-sidekiq.service +++ b/dist/mastodon-sidekiq.service @@ -9,7 +9,7 @@ WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="DB_POOL=25" Environment="MALLOC_ARENA_MAX=2" -Environment="LD_PRELOAD=libmimalloc.so" +Environment="LD_PRELOAD=libjemalloc.so" ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25 TimeoutSec=15 Restart=always diff --git a/dist/mastodon-streaming.service b/dist/mastodon-streaming.service index cf1f2d8bc6..fdd280d8e1 100644 --- a/dist/mastodon-streaming.service +++ b/dist/mastodon-streaming.service @@ -9,6 +9,7 @@ WorkingDirectory=/home/mastodon/live Environment="NODE_ENV=production" Environment="PORT=4000" Environment="STREAMING_CLUSTER_NUM=1" +Environment="LD_PRELOAD=libjemalloc.so" ExecStart=/usr/bin/node ./streaming TimeoutSec=15 Restart=always diff --git a/dist/mastodon-web.service b/dist/mastodon-web.service index bc96c0accb..a5598fdaa8 100644 --- a/dist/mastodon-web.service +++ b/dist/mastodon-web.service @@ -8,7 +8,7 @@ User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="PORT=3000" -Environment="LD_PRELOAD=libmimalloc.so" +Environment="LD_PRELOAD=libjemalloc.so" ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb ExecReload=/bin/kill -SIGUSR1 $MAINPID TimeoutSec=15 From 1f2f01370b6a7ea472124bedddc35236dd17b6d8 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Fri, 11 Nov 2022 21:24:58 +0100 Subject: [PATCH 095/166] blurhash_transcoder: prevent out-of-bound reads with <8bpp images Backport from vanilla: 36bc90e8aaf89b5cf64636b404611ff1809ad6f0 The Blurhash library used by Mastodon requires an input encoded as 24 bits raw RGB data. The conversion to raw RGB using Imagemagick did not previously specify the desired bit depth. In some situations, this leads Imagemagick to output in a pixel format using less bpp than expected. This then manifested as segfaults of the Sidekiq process due to out-of-bounds read, or potentially a (highly noisy) memory infoleak. --- lib/paperclip/blurhash_transcoder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/paperclip/blurhash_transcoder.rb b/lib/paperclip/blurhash_transcoder.rb index 1c3a6df025..c22c20c57a 100644 --- a/lib/paperclip/blurhash_transcoder.rb +++ b/lib/paperclip/blurhash_transcoder.rb @@ -5,7 +5,7 @@ module Paperclip def make return @file unless options[:style] == :small || options[:blurhash] - pixels = convert(':source RGB:-', source: File.expand_path(@file.path)).unpack('C*') + pixels = convert(':source -depth 8 RGB:-', source: File.expand_path(@file.path)).unpack('C*') geometry = options.fetch(:file_geometry_parser).from_file(@file) attachment.instance.blurhash = Blurhash.encode(geometry.width, geometry.height, pixels, **(options[:blurhash] || {})) From 92ff7b0e3e274857b66d92d11b1934d8f5f77c2a Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 12 Nov 2022 14:43:21 +0100 Subject: [PATCH 096/166] Make deleting / unboosting slightly less strict as well --- config/initializers/rack_attack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 3fb22c6321..456c575cb3 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -77,7 +77,7 @@ class Rack::Attack API_DELETE_REBLOG_REGEX = /\A\/api\/v1\/statuses\/[\d]+\/unreblog/.freeze API_DELETE_STATUS_REGEX = /\A\/api\/v1\/statuses\/[\d]+/.freeze - throttle('throttle_api_delete', limit: 30, period: 5.minutes) do |req| + throttle('throttle_api_delete', limit: 60, period: 5.minutes) do |req| req.authenticated_user_id if (req.post? && req.path.match?(API_DELETE_REBLOG_REGEX)) || (req.delete? && req.path.match?(API_DELETE_STATUS_REGEX)) end From db16d7d9de60b2d170a5855dbda6750d3f743f67 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 14 Nov 2022 22:14:55 +0100 Subject: [PATCH 097/166] Fix nodes order being sometimes mangled when rewriting emoji (#20677) * Fix front-end emoji tests * Fix nodes order being sometimes mangled when rewriting emoji --- .../features/emoji/__tests__/emoji-test.js | 42 +++++++++---------- .../mastodon/features/emoji/emoji.js | 15 +++++-- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js index 07b3d8c53d..2f19aab7e4 100644 --- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js +++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js @@ -11,8 +11,8 @@ describe('emoji', () => { }); it('works with unclosed tags', () => { - expect(emojify('hello>')).toEqual('hello>'); - expect(emojify('')).toEqual('hello>'); + expect(emojify(' { @@ -22,23 +22,23 @@ describe('emoji', () => { it('does unicode', () => { expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual( - '👩‍👩‍👦‍👦'); + '👩‍👩‍👦‍👦'); expect(emojify('👨‍👩‍👧‍👧')).toEqual( - '👨‍👩‍👧‍👧'); - expect(emojify('👩‍👩‍👦')).toEqual('👩‍👩‍👦'); + '👨‍👩‍👧‍👧'); + expect(emojify('👩‍👩‍👦')).toEqual('👩‍👩‍👦'); expect(emojify('\u2757')).toEqual( - '❗'); + '❗'); }); it('does multiple unicode', () => { expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual( - '❗ #️⃣'); + '❗ #️⃣'); expect(emojify('\u2757#\uFE0F\u20E3')).toEqual( - '❗#️⃣'); + '❗#️⃣'); expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual( - '❗ #️⃣ ❗'); + '❗ #️⃣ ❗'); expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual( - 'foo ❗ #️⃣ bar'); + 'foo ❗ #️⃣ bar'); }); it('ignores unicode inside of tags', () => { @@ -46,16 +46,16 @@ describe('emoji', () => { }); it('does multiple emoji properly (issue 5188)', () => { - expect(emojify('👌🌈💕')).toEqual('👌🌈💕'); - expect(emojify('👌 🌈 💕')).toEqual('👌 🌈 💕'); + expect(emojify('👌🌈💕')).toEqual('👌🌈💕'); + expect(emojify('👌 🌈 💕')).toEqual('👌 🌈 💕'); }); it('does an emoji that has no shortcode', () => { - expect(emojify('👁‍🗨')).toEqual('👁‍🗨'); + expect(emojify('👁‍🗨')).toEqual('👁‍🗨'); }); it('does an emoji whose filename is irregular', () => { - expect(emojify('↙️')).toEqual('↙️'); + expect(emojify('↙️')).toEqual('↙️'); }); it('avoid emojifying on invisible text', () => { @@ -67,26 +67,26 @@ describe('emoji', () => { it('avoid emojifying on invisible text with nested tags', () => { expect(emojify('😇')) - .toEqual('😇'); + .toEqual('😇'); expect(emojify('😇')) - .toEqual('😇'); - expect(emojify('😇')) - .toEqual('😇'); + .toEqual('😇'); + expect(emojify('😇')) + .toEqual('😇'); }); it('skips the textual presentation VS15 character', () => { expect(emojify('✴︎')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15 - .toEqual('✴'); + .toEqual('✴'); }); it('does an simple emoji properly', () => { expect(emojify('♀♂')) - .toEqual('♀♂'); + .toEqual('♀♂'); }); it('does an emoji containing ZWJ properly', () => { expect(emojify('💂‍♀️💂‍♂️')) - .toEqual('💂\u200D♀️💂\u200D♂️'); + .toEqual('💂\u200D♀️💂\u200D♂️'); }); }); }); diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js index 0ab32767ae..52a8458fbb 100644 --- a/app/javascript/mastodon/features/emoji/emoji.js +++ b/app/javascript/mastodon/features/emoji/emoji.js @@ -19,10 +19,13 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; +const domParser = new DOMParser(); + const emojifyTextNode = (node, customEmojis) => { - const parentElement = node.parentElement; let str = node.textContent; + const fragment = new DocumentFragment(); + for (;;) { let match, i = 0; @@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => { } } + fragment.append(document.createTextNode(str.slice(0, i))); + if (replacement) { + fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); + } node.textContent = str.slice(0, i); - parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); - node = document.createTextNode(str); - parentElement.append(node); } + + fragment.append(document.createTextNode(str)); + node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { From ec15e58efad1c961a21d66163a7cf7c8ac684b97 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 14 Nov 2022 22:34:38 +0100 Subject: [PATCH 098/166] 4.0.1+1.0.0 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index f7b697f2c2..8e3727d777 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 0 + 1 end def flags From 06faf2f977f56d939b725047c962e13877bf3dc3 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 14 Nov 2022 22:40:37 +0100 Subject: [PATCH 099/166] [Glitch] Fix nodes order being sometimes mangled when rewriting emoji backport https://github.com/mastodon/mastodon/commit/ccbca50a2502108884f7f3c9ea24df8b2129731d by Claire --- .../flavours/glitch/features/emoji/emoji.js | 15 +++++++++++---- lib/mastodon/version.rb | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js index 6138164e05..880f9401da 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji.js +++ b/app/javascript/flavours/glitch/features/emoji/emoji.js @@ -19,10 +19,13 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; +const domParser = new DOMParser(); + const emojifyTextNode = (node, customEmojis) => { - const parentElement = node.parentElement; let str = node.textContent; + const fragment = new DocumentFragment(); + for (;;) { let match, i = 0; @@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => { } } + fragment.append(document.createTextNode(str.slice(0, i))); + if (replacement) { + fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); + } node.textContent = str.slice(0, i); - parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); - node = document.createTextNode(str); - parentElement.append(node); } + + fragment.append(document.createTextNode(str)); + node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 8e3727d777..d5809a0ca4 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.0' + '+1.0.1' end def to_a From 14ef153c69cba0970665a0e8360e73c67039d7d5 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 14 Nov 2022 22:50:45 +0100 Subject: [PATCH 100/166] Revert "[Glitch] Fix nodes order being sometimes mangled when rewriting emoji" This reverts commit 06faf2f977f56d939b725047c962e13877bf3dc3. --- .../flavours/glitch/features/emoji/emoji.js | 15 ++++----------- lib/mastodon/version.rb | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js index 880f9401da..6138164e05 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji.js +++ b/app/javascript/flavours/glitch/features/emoji/emoji.js @@ -19,13 +19,10 @@ const emojiFilename = (filename) => { return borderedEmoji.includes(filename) ? (filename + '_border') : filename; }; -const domParser = new DOMParser(); - const emojifyTextNode = (node, customEmojis) => { + const parentElement = node.parentElement; let str = node.textContent; - const fragment = new DocumentFragment(); - for (;;) { let match, i = 0; @@ -67,16 +64,12 @@ const emojifyTextNode = (node, customEmojis) => { } } - fragment.append(document.createTextNode(str.slice(0, i))); - if (replacement) { - fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]); - } node.textContent = str.slice(0, i); + parentElement.insertAdjacentHTML('beforeend', replacement); str = str.slice(rend); + node = document.createTextNode(str); + parentElement.append(node); } - - fragment.append(document.createTextNode(str)); - node.parentElement.replaceChild(fragment, node); }; const emojifyNode = (node, customEmojis) => { diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index d5809a0ca4..8e3727d777 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.1' + '+1.0.0' end def to_a From 0005e01aa675305ba8f4f32682d28105c0515dac Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Mon, 14 Nov 2022 22:52:11 +0100 Subject: [PATCH 101/166] 4.0.1+1.0.2 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 8e3727d777..06d603b724 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.0' + '+1.0.2' end def to_a From 6d5a8d89d140f0c0a053b9af3e55a35d22579691 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Tue, 15 Nov 2022 08:04:40 +0100 Subject: [PATCH 102/166] 4.0.1+1.0.4 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index bae536c267..b6a3f33120 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.3' + '+1.0.4' end def to_a From 59affb893379f0635887259c061dea294a51e1fb Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 01:20:35 +0000 Subject: [PATCH 103/166] relabel post button --- app/javascript/mastodon/locales/de.json | 2 +- app/javascript/mastodon/locales/en-GB.json | 3 ++- app/javascript/mastodon/locales/en.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 223e689129..316813fa26 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -137,7 +137,7 @@ "compose_form.poll.remove_option": "Auswahlfeld entfernen", "compose_form.poll.switch_to_multiple": "Mehrfachauswahl erlauben", "compose_form.poll.switch_to_single": "Nur Einzelauswahl erlauben", - "compose_form.publish": "Veröffentlichen", + "compose_form.publish": "Boop", "compose_form.publish_loud": "{publish}!", "compose_form.save_changes": "Änderungen speichern", "compose_form.sensitive.hide": "{count, plural, one {Mit einer Inhaltswarnung versehen} other {Mit einer Inhaltswarnung versehen}}", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index b005f80905..2df4bcc268 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -17,6 +17,7 @@ "account.badges.group": "Group", "account.block": "Block @{name}", "account.block_domain": "Block domain {domain}", + "account.block_domain": "Block domain {domain}", "account.blocked": "Blocked", "account.browse_more_on_origin_server": "Browse more on the original profile", "account.cancel_follow_request": "Withdraw follow request", @@ -137,7 +138,7 @@ "compose_form.poll.remove_option": "Remove this choice", "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices", "compose_form.poll.switch_to_single": "Change poll to allow for a single choice", - "compose_form.publish": "Publish", + "compose_form.publish": "Boop", "compose_form.publish_loud": "{publish}!", "compose_form.save_changes": "Save changes", "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 9dc00fbf1b..2e7d748fa1 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -141,7 +141,7 @@ "compose_form.poll.remove_option": "Remove this choice", "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices", "compose_form.poll.switch_to_single": "Change poll to allow for a single choice", - "compose_form.publish": "Publish", + "compose_form.publish": "Boop", "compose_form.publish_loud": "{publish}!", "compose_form.save_changes": "Save changes", "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}", From baab85fac7e33e935dc64f9c1770bf7daadd29e5 Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 01:27:48 +0000 Subject: [PATCH 104/166] add post suffix version --- lib/mastodon/version.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index b6a3f33120..30c107bafb 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -28,12 +28,20 @@ module Mastodon '+1.0.4' end + def post_suffix + '+nya' + end + + def post_suffix_version + '-1.0.1' + end + def to_a [major, minor, patch].compact end def to_s - [to_a.join('.'), flags, suffix, suffix_version].join + [to_a.join('.'), flags, suffix, suffix_version, post_suffix, post_suffix_version].join end def repository From be733ade6bd25e915ee527680f1f0b92ca42ce2a Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 02:40:44 +0000 Subject: [PATCH 105/166] add nyastodon branding --- .../flavours/glitch/features/ui/components/link_footer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index 8e07ab9fb0..9af082130a 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -75,7 +75,7 @@ class LinkFooter extends React.PureComponent {

- Catstodon: +change nyastodon: {' '} {' · '} @@ -85,6 +85,8 @@ class LinkFooter extends React.PureComponent { {' · '} v{version}

+ +

be gay do crime uwu

); } From 41c844ed6d32beae696a35a235f18acb0d3f6a8b Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 03:15:06 +0000 Subject: [PATCH 106/166] remove accidental clipboard paste --- .../flavours/glitch/features/ui/components/link_footer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index 9af082130a..deef200ee5 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -75,7 +75,7 @@ class LinkFooter extends React.PureComponent {

-change nyastodon: + nyastodon: {' '} {' · '} From b719ce1a976685b60f06c4d8097921ea24e00fa5 Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 03:52:43 +0000 Subject: [PATCH 107/166] add even more nyastodon branding --- config/settings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.yml b/config/settings.yml index 2a4cf06e6a..b8959df12b 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -2,7 +2,7 @@ # important settings can be changed from the admin interface. defaults: &defaults - site_title: 'Catstodon' + site_title: 'nyastodon' site_short_description: '' site_description: '' site_extended_description: '' From 16d6c4a1459c52bf652d8de8afb1f7fb489cfbb6 Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 03:55:56 +0000 Subject: [PATCH 108/166] rewrite homogay flavour this version attempts to hack more directly into the whole theming stuff, rather than build on top of it like the previous one. --- .../flavours/glitch/styles/homogay.scss | 375 +----------------- .../flavours/glitch/styles/homogay/diff.scss | 73 ++++ .../glitch/styles/homogay/variables.scss | 51 +++ .../flavours/glitch/styles/variables.scss | 10 +- app/javascript/skins/glitch/homogay/names.yml | 4 + 5 files changed, 135 insertions(+), 378 deletions(-) create mode 100644 app/javascript/flavours/glitch/styles/homogay/diff.scss create mode 100644 app/javascript/flavours/glitch/styles/homogay/variables.scss diff --git a/app/javascript/flavours/glitch/styles/homogay.scss b/app/javascript/flavours/glitch/styles/homogay.scss index 3b89c5d7b7..481c6492ee 100644 --- a/app/javascript/flavours/glitch/styles/homogay.scss +++ b/app/javascript/flavours/glitch/styles/homogay.scss @@ -1,375 +1,4 @@ +@import 'homogay/variables'; @import 'index'; - -/* customize ya stuff */ -:root { - --border-radius: 8px; - - /* rgb for transparency to work */ - --text-color: 217, 225, 232; - --text-color-secondary: 96, 105, 132; /* less bright, for unimportant bits */ - - --background-color: 15, 11, 23; - --background-color-brighter: 22, 17, 33; - --app-background-color: 10, 5, 15; /* used only for the VERY background in the back */ - - --accent-color: 222, 24, 163; - --accent-color-secondary: 110, 69, 97; /* less saturated ver of --accent-color */ - --accent-color-bright: 255, 64, 198; -} - -.account__avatar-overlay-base, .account__avatar-overlay-overlay, .account__avatar { - border-radius: var(--border-radius); -} - -/* roundening shenanigans */ - -.drawer > div, nav, .search, .drawer__header a, .drawer--header a, .search__input { - border-radius: var(--border-radius) !important; -} - -.column-header, .column-back-button, .navigation-panel .column-link:nth-child(1), .navigation-panel .column-link:nth-child(11), .navigation-bar { - border-radius: var(--border-radius) var(--border-radius) 0 0; -} -.column > .scrollable, .getting-started, .navigation-panel .column-link:nth-child(9), .navigation-panel .column-link:nth-child(12) { - border-radius: 0 0 var(--border-radius) var(--border-radius); -} - -.button { - border-radius: var(--border-radius); -} - -/* standard fg/bg color changes */ - -.drawer__inner, .drawer__inner__mastodon, .drawer__header, .drawer--header, .actions-modal, .block-modal, .boost-modal, .confirmation-modal, .mute-modal, .report-modal, article, .getting-started, .column-subheading, .column-link, .column-subheading, .column-link, .emoji-mart-scroll, .emoji-mart-search, .emoji-mart-category-label > span, .emoji-picker-dropdown__menu, .scrollable, .empty-column-indicator, .column-inline-form, .dropdown-menu, .dropdown-menu__item a, .account__header__fields dt, .search-popout, .confirmation-modal__action-bar, .reactions-bar__item, .emoji-picker-dropdown__modifiers__menu, .content-wrapper, .sidebar-wrapper--empty, .regeneration-indicator, .tabs-bar, .navigation-bar, .trends__header, .modal-layout { - background-color: rgb(var(--background-color)) !important; - color: rgb(var(--text-color)) !important; -} -.glitch.local-settings__navigation, .glitch.local-settings__navigation__item, .glitch.local-settings__page, .glitch.local-settings { - background-color: rgb(var(--background-color)); - color: rgb(var(--text-color)); -} - -.modal-layout, .modal-layout__mastodon > * { - background-image: none; -} - -.account__section-headline a.active::after, .account__section-headline button.active::after, .notification__filter-bar a.active::after, .notification__filter-bar button.active::after, .account__section-headline a.active::after, .account__section-headline a.active::before, .account__section-headline button.active::after, .account__section-headline button.active::before, .notification__filter-bar a.active::after, .notification__filter-bar a.active::before, .notification__filter-bar button.active::after, .notification__filter-bar button.active::before { - border-color: transparent transparent rgb(var(--background-color)); -} - -.dropdown-menu__arrow { - border-bottom-color: rgb(var(--background-color)) !important; -} -.dropdown-menu__arrow.top { - border-top-color: rgb(var(--background-color)) !important; -} - -.reply-indicator__content, .status__content, .reply-indicator__display-name, .privacy-dropdown__option__icon, .composer--options--dropdown--content--item .icon, .composer--reply > .content { - color: rgb(var(--text-color)) !important; -} - -html { - scrollbar-color: rgb(var(--background-color-brighter)) rgba(0,0,0,0.1); -} - -.tabs-bar__wrapper { - background: rgb(var(--app-background-color)); -} - -.column-header, .column-header__button, .account__section-headline, .notification__filter-bar > button, .emoji-mart-bar, .column-back-button, .column-header__back-button, .announcements, .column-header__collapsible-inner, .status.status-direct:not(.read), .notification__filter-bar, .glitch.local-settings__page { - background-color: rgb(var(--background-color-brighter)) !important; - border-bottom: none; -} - -.reply-indicator, .emoji-picker-dropdown__modifiers__menu button:hover, .compose-form .compose-form__buttons-wrapper, .composer--options-wrapper, .compose-form__poll-wrapper select, .flash-message, .card__bar, .card > a:hover .card__bar, .glitch.local-settings__navigation__item:hover { - background-color: rgb(var(--background-color-brighter)); -} - -.columns-area, .app-body, .getting-started__wrapper { - background-color: rgb(var(--app-background-color)); -} - -.privacy-dropdown__option__content strong, .composer--options--dropdown--content strong, .character-counter, .report-modal__comment .setting-text-label, .compose-form__poll-wrapper select { - color: rgb(var(--text-color)) !important; -} - -input, textarea, .compose-form__modifiers, .privacy-dropdown__dropdown, .composer--options--dropdown--content, .privacy-dropdown__value { - background-color: rgb(var(--background-color-brighter)) !important; - color: rgb(var(--text-color)) !important; -} - -.compose-form__buttons-wrapper, .admin-wrapper .sidebar-wrapper__inner, .admin-wrapper .sidebar ul a:hover, .admin-wrapper .sidebar ul a, .admin-wrapper .sidebar ul a.selected, .account__disclaimer, .account__action-bar-links { - background-color: rgb(var(--background-color-brighter)); -} - -.compose-form .compose-form__autosuggest-wrapper { - border-top-left-radius: var(--border-radius); - border-top-right-radius: var(--border-radius); -} - -.compose-form .compose-form__buttons-wrapper { - border-bottom-left-radius: var(--border-radius); - border-bottom-right-radius: var(--border-radius); -} - -.compose-form .spoiler-input__input { - border-radius: var(--border-radius); -} - -a.status-card { - border-radius: var(--border-radius); -} - -.detailed-status, .detailed-status__action-bar, .account__header__bar, .focusable:focus { - background-color: rgb(var(--background-color-brighter)) !important; - border-bottom: none; - border-top: none; -} - -.status.collapsed .status__content::after { - background: linear-gradient(rgba(var(--background-color),0), rgba(var(--background-color),0)) !important; -} - -.dismissable-banner { - /*border-top: 1px solid #393f4f;*/ - background-color: rgb(var(--background-color-brighter)); -} - -.status-card__image { - background-color: rgb(var(--background-color-brighter)); -} - -/* accent color changes */ - -.button, .react-toggle--checked .react-toggle-track, .react-toggle--checked:hover .react-toggle-track, .react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track, .button.logo-button, .emoji-mart-anchor-bar, .loading-bar, .icon-with-badge__badge, .video-player__volume__current, .video-player__volume__handle, .upload-progress__tracker, .video-player__seek__buffer, .video-player__seek__progress, .floating-action-button { - background-color: rgb(var(--accent-color)); -} -.react-toggle--checked .react-toggle-thumb, .compose-form__sensitive-button .checkbox, .filters .filter-subset a.selected, .account__action-bar__tab.active, .tabs-bar__link.active, .notification.unread::before, .status.unread::before { - border-color: rgb(var(--accent-color)); -} - -.text-icon-button, .icon-button.inverted, button.inverted:hover, .icon-button, .icon-button:hover, .status__action-bar__counter__label, .text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .icon-button.disabled, .composer--options--dropdown.open > .value { - color: rgb(var(--accent-color-secondary)); -} -.status__info__icons i { - color: rgb(var(--accent-color-secondary)) !important; -} - -.status__content__spoiler-link { - background-color: rgb(var(--accent-color-secondary)) !important; -} -.column-header__wrapper.active::before { - background: radial-gradient(ellipse, rgba(var(--accent-color),.23) 0%, rgba(var(--accent-color),0) 60%); -} -.column-header__wrapper.active { - box-shadow: 0 1px 0 rgba(var(--accent-color),.3); -} - -.compose-form__sensitive-button .checkbox.active, .poll__chart.leading { - border-color: rgb(var(--accent-color)); - background-color: rgb(var(--accent-color)); -} - -.poll__chart { - background-color: rgb(var(--accent-color-secondary)); -} - -.column-header.active .column-header__icon { - text-shadow: 0 0 10px rgba(var(--accent-color),.4); -} - -.text-icon-button:active, .text-icon-button:focus, .text-icon-button:hover, .drawer__header a:hover, .drawer--header a:hover, .drawer--header a:focus, .icon-button:hover, .reactions-bar__item:hover { - background-color: rgba(var(--accent-color-secondary), .1); -} - -.icon-button.inverted:active, .icon-button.inverted:focus, .icon-button.inverted:hover, .reactions-bar__item.active { - background-color: rgba(var(--accent-color-secondary), .25); - color: rgb(var(--accent-color-secondary)); -} - -.button:active, .button:focus, .button:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a:hover, .simple_form .block-button:hover, .simple_form .button:hover, .simple_form button:hover, .button.logo-button:active, .button.logo-button:focus, .button.logo-button:hover, .floating-action-button:hover, .glitch.local-settings__navigation__item.active:hover { - background-color: rgb(var(--accent-color-bright)); -} - -.privacy-dropdown__option.active, .composer--options--dropdown--content--item.active, .privacy-dropdown__option:hover, .composer--options--dropdown--content--item:hover, .privacy-dropdown__option.active:hover, .composer--options--dropdown--content.active:hover, .admin-wrapper .sidebar ul .simple-navigation-active-leaf a, .simple_form .block-button, .simple_form .button, .simple_form button, .simple_form .block-button:active, .simple_form .block-button:focus, .simple_form .button:active, .simple_form .button:focus, .simple_form button:active, .simple_form button:focus, .composer--options--dropdown.open > .value, .glitch.local-settings__navigation__item.active { - background-color: rgb(var(--accent-color)); -} - -.status__info__icons .icon-button.active i, .tabs-bar__link.active, .status__content a { - color: rgb(var(--accent-color)) !important; -} - - -.trends__item__sparkline path:last-child { - stroke: rgb(var(--accent-color)) !important; -} -.trends__item__sparkline path:first-child { - fill: rgb(var(--accent-color-secondary)) !important; -} - -a.u-url, .status-link, .column-header__back-button, .status__content__read-more-button, .column-header.active .column-header__icon, .column-link.active, .account__section-headline a.active, .account__section-headline button.active, .notification__filter-bar a.active, .notification__filter-bar button.active, .account__header__content a, .account__header__bio .account__header__fields a, .reactions-bar__item.active .reactions-bar__item__count, .emoji-mart-anchor-selected, .reply-indicator__content a, .compose-form .compose-form__warning a, .text-icon-button.active, .icon-button.inverted.active, .drawer__tab:hover, .icon-button.active, .column-back-button, .filters .filter-subset a.selected, .admin-wrapper .content .muted-hint a, body .muted-hint a, .table a, .notification__message .fa, .drawer--header a:hover, .drawer--header a:focus { - color: rgb(var(--accent-color)) !important; -} - -.language-dropdown__dropdown__results__item.active { - background: rgb(var(--accent-color)); -} -.language-dropdown__dropdown__results__item.active:hover, .language-dropdown__dropdown__results__item:hover { - background: rgb(var(--accent-color-bright)); -} - -.emoji-mart-search > input { - border-color: rgba(255, 255, 255, 0.1); -} - -/* fixes */ - -/* boost hack, v2 */ -/* https://codepen.io/sosuke/pen/Pjoqqp */ -button.icon-button i.fa-retweet { - filter: brightness(0) invert(38%) sepia(10%) saturate(1594%) hue-rotate(266deg) brightness(97%) contrast(89%); /* accent-color-secondary */ - color: transparent !important; -} -button.icon-button.active i.fa-retweet { - filter: invert(19%) sepia(75%) saturate(7362%) hue-rotate(308deg) brightness(98%) contrast(88%); /* accent-color */ -} -button.icon-button.disabled i.fa-retweet, button.icon-button.disabled i.fa-lock { - filter: invert(38%) sepia(10%) saturate(1594%) hue-rotate(266deg) brightness(50%) contrast(89%); /* accent-color-secondary with brightness set to 50% */ -} - -.load-more:hover, .mbstobon-2 .drawer__inner__mastodon, .mbstobon-1 .drawer__inner__mastodon, .mbstobon-0 .drawer__inner__mastodon { - background: inherit; -} - -.account__action-bar__tab, .account__action-bar { - border: none; -} - -.notification__filter-bar, .account__header__bar, .admin-wrapper .content-heading, .admin-wrapper .content h4, .tabs-bar__link:not(.active) { - border-bottom: none; -} - -.dropdown-menu__separator, hr { - opacity: 0; -} -.compose-form .autosuggest-textarea__textarea, .compose-form .spoiler-input__input, .compose-panel .compose-form__autosuggest-wrapper, .mbstobon-3 .drawer__inner__mastodon { - background: transparent; -} -.status, .account, .account__header__fields dl, .account__header__fields, .account__header__bio .account__header__fields, .glitch.local-settings__navigation__item { - border-top: none; - border-bottom: none; -} - -.report-modal__container, .report-modal__comment, .report-modal__comment .setting-text__wrapper { - border-color: rgba(0, 0, 0, 0) !important; -} - -.drawer__inner__mastodon { - background: inherit; -} - -/* misc */ -.column-link:hover, .dropdown-menu__item a:active, .dropdown-menu__item a:focus, .dropdown-menu__item a:hover { - color: rgb(var(--accent-color-bright)) !important; -} - -.notification__filter-bar button.active, .account__section-headline .active { - border-bottom: 3px solid rgb(var(--accent-color)); -} -.notification__filter-bar button:not(.active):hover { - top: -3px; -} -.account__section-headline a.active::after, .account__section-headline a.active::before { - display: none; -} - -.account__header__extra__links a:hover { - text-decoration: underline; -} -.account__section-headline a:hover, .confirmation-modal__cancel-button span { - color: #fff; -} - -.notification__filter-bar button.active::after { - opacity: 0; -} -.notification__filter-bar button.active::before { - opacity: 0; -} - -.column-link__badge, .column-subheading { - background-color: rgb(var(--accent-color)); - animation-name: flash; - animation-duration: 1s; - animation-iteration-count: infinite; - animation-direction: alternate-reverse; -} - -@keyframes flash { - from {background-color: rgb(var(--accent-color));} - to {background-color: rgb(var(--accent-color-secondary));} -} - -.reply-indicator { - max-height: 38px; - overflow-y: hidden; - transition: max-height 1s; -} -.reply-indicator:hover { - max-height: 100%; -} -.reply-indicator:before { - content: 'Replying to:'; - font-size: 12px; - color: rgb(var(--text-color-secondary)); -} - -.link-footer p:last-of-type:after { - content: '\A be gay do crime uwu'; - white-space: pre; -} - - -/* public/static css */ -/* for pages like /@username */ - -.public-layout .public-account-header__tabs__tabs .counter.active::after { - border-bottom-color: rgb(var(--accent-color)); -} -.public-layout .public-account-bio .account__header__fields a { - color: rgb(var(--accent-color)); -} -.public-layout .header .nav-button { - color: #fff; - background-color: rgb(var(--accent-color)); -} -.public-layout .header .nav-button:active, .public-layout .header .nav-button:focus, .public-layout .header .nav-button:hover { - background-color: rgb(var(--accent-color-bright)) -} -.public-layout .activity-stream .entry, .hero-widget__text, .table-of-contents { - background-color: rgb(var(--background-color)); -} -body { - background-color: rgb(var(--app-background-color)); -} -.public-layout .public-account-header__tabs__tabs .counter { - border-right: none; -} -.public-layout .public-account-bio, .public-layout .public-account-header__bar::before, .public-layout .header, .directory__tag > a, .directory__tag > div, .directory__tag > a:active, .directory__tag > a:focus, .directory__tag > a:hover, .public-layout .header .brand:hover, .landing-page__call-to-action, .box-widget { - background-color: rgb(var(--background-color-brighter)); -} -.public-layout .display-name, .status__relative-time time, .status__relative-time { - color: rgb(var(--text-color-secondary)); -} -.rich-formatting, .rich-formatting p { - color: rgb(var(--text-color)); -} -.rich-formatting table tbody tr, .rich-formatting table thead tr, .notification-follow, .notification-follow-request { - border-bottom: none; -} - -// https://types.pl/@haskal/106569437674907815 -.search-popout em { - color: rgb(var(--accent-color)) !important; -} +@import 'homogay/diff'; diff --git a/app/javascript/flavours/glitch/styles/homogay/diff.scss b/app/javascript/flavours/glitch/styles/homogay/diff.scss new file mode 100644 index 0000000000..99f5fe232e --- /dev/null +++ b/app/javascript/flavours/glitch/styles/homogay/diff.scss @@ -0,0 +1,73 @@ +body { + background: darken($ui-base-color, 3%); +} + +// hashtags in primary color +.status__content a { + color: $highlight-text-color; +} + +// compose panel +.compose-panel .autosuggest-textarea label .autosuggest-textarea__textarea, +.compose-form .autosuggest-input label .autosuggest-textarea__textarea, +.compose-form__buttons-wrapper, +.spoiler-input input, +.compose-form__modifiers, +.reply-indicator { + background: $ui-base-color; + color: $primary-text-color; +} +.compose-panel .compose-form__autosuggest-wrapper { + background-color: #fff0; // transparent background so it doesn't mess with border-radius +} +.poll__option input[type="text"], +.compose-form__poll-wrapper select { + border-color: $ui-base-lighter-color; +} + +.emoji-mart-bar:first-child { + background: lighten($ui-base-color, 7%); + border-bottom-color: #fff0; +} +.emoji-mart-anchor:not(.emoji-mart-anchor-selected) { + color: $ui-base-lighter-color; +} + +// dropdowns +.dropdown-menu, +.dropdown-menu__item a, +.dropdown-menu__item button { + background: $ui-base-lighter-color; +} +.dropdown-menu__arrow.top { + border-top-color: $ui-base-lighter-color; +} +.dropdown-menu__arrow.bottom { + border-bottom-color: $ui-base-lighter-color; +} + +// general modals +.modal-root__modal { + background: $ui-base-color; +} +.report-modal__container { + border-top-color: $ui-base-lighter-color; +} +.report-modal__comment { + border-right-color: $ui-base-lighter-color; +} +.report-modal__comment .setting-text { + background: $ui-base-color; +} + +// app settings modal +.glitch.local-settings { + background: $ui-base-color; +} +.glitch.local-settings__navigation, +.glitch.local-settings__navigation__item { + background: lighten($ui-base-color, 5%); +} +.glitch.local-settings__navigation__item { + border-bottom-color: $ui-base-lighter-color; +} diff --git a/app/javascript/flavours/glitch/styles/homogay/variables.scss b/app/javascript/flavours/glitch/styles/homogay/variables.scss new file mode 100644 index 0000000000..335a22349f --- /dev/null +++ b/app/javascript/flavours/glitch/styles/homogay/variables.scss @@ -0,0 +1,51 @@ +// Commonly used web colors +$black: #000000; +$white: #ffffff; +$success-green: #5fe43d; +$error-red: #c9343b; +$warning-red: #c96932; +$gold-star: #e4ba3d; + +$red-bookmark: $success-green; + +// Values from the classic Mastodon UI +$classic-base-color: #0f0b17; +$classic-primary-color: #d4b6cb; +$classic-secondary-color: #eaddf4; +$classic-highlight-color: #de18a3; + +// Variables for defaults in UI +$base-shadow-color: $black !default; +$base-overlay-background: $black !default; +$base-border-color: $white !default; +$simple-background-color: lighten($classic-base-color, 7%) !default; +$valid-value-color: $success-green !default; +$error-value-color: $error-red !default; + +// Tell UI to use selected colors +$ui-base-color: $classic-base-color !default; +$ui-base-lighter-color: #5f4a6e !default; +$ui-primary-color: $classic-primary-color !default; +$ui-secondary-color: $classic-secondary-color !default; +$ui-highlight-color: $classic-highlight-color !default; + +// Variables for texts +$primary-text-color: $classic-secondary-color !default; +$darker-text-color: $ui-primary-color !default; +$dark-text-color: $ui-base-lighter-color !default; +$secondary-text-color: $ui-secondary-color !default; +$highlight-text-color: lighten($ui-highlight-color, 8%) !default; +$action-button-color: $ui-base-lighter-color !default; +$passive-text-color: $gold-star !default; +$active-passive-text-color: $success-green !default; + +// For texts on inverted backgrounds +$inverted-text-color: $primary-text-color !default; // we don't do inverted backgrounds +$lighter-text-color: $ui-base-lighter-color !default; +$light-text-color: $ui-primary-color !default; + +// Variables for components +$media-modal-media-max-width: 100%; + +// put margins on top and bottom of image to avoid the screen covered by image. +$media-modal-media-max-height: 80%; diff --git a/app/javascript/flavours/glitch/styles/variables.scss b/app/javascript/flavours/glitch/styles/variables.scss index b865b5a2d1..eaec208468 100644 --- a/app/javascript/flavours/glitch/styles/variables.scss +++ b/app/javascript/flavours/glitch/styles/variables.scss @@ -1,12 +1,12 @@ // Commonly used web colors $black: #000000; // Black $white: #ffffff; // White -$success-green: #79bd9a; // Padua -$error-red: #df405a; // Cerise -$warning-red: #ff5050; // Sunset Orange -$gold-star: #ca8f04; // Dark Goldenrod +$success-green: #79bd9a !default; // Padua +$error-red: #df405a !default; // Cerise +$warning-red: #ff5050 !default; // Sunset Orange +$gold-star: #ca8f04 !default; // Dark Goldenrod -$red-bookmark: $warning-red; +$red-bookmark: $warning-red !default; // Values from the classic Mastodon UI $classic-base-color: #282c37; // Midnight Express diff --git a/app/javascript/skins/glitch/homogay/names.yml b/app/javascript/skins/glitch/homogay/names.yml index 0b29f568d9..968051de57 100644 --- a/app/javascript/skins/glitch/homogay/names.yml +++ b/app/javascript/skins/glitch/homogay/names.yml @@ -10,3 +10,7 @@ es: skins: glitch: homogay: homogay (por @anna@girldick.gay) +fr: + skins: + glitch: + homogay: homogay (par @anna@girldick.gay) From c32515a620a1a4d884a97b59a08eb15a712235f5 Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 16 Nov 2022 04:29:56 +0000 Subject: [PATCH 109/166] bring back wider border radius --- .../flavours/glitch/styles/homogay/diff.scss | 49 +++++++++++++++++++ .../glitch/styles/homogay/variables.scss | 2 + 2 files changed, 51 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/homogay/diff.scss b/app/javascript/flavours/glitch/styles/homogay/diff.scss index 99f5fe232e..b628f70e4e 100644 --- a/app/javascript/flavours/glitch/styles/homogay/diff.scss +++ b/app/javascript/flavours/glitch/styles/homogay/diff.scss @@ -17,6 +17,14 @@ body { background: $ui-base-color; color: $primary-text-color; } +.compose-form .autosuggest-textarea label .autosuggest-textarea__textarea { + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; +} +.compose-form__buttons-wrapper { + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; +} .compose-panel .compose-form__autosuggest-wrapper { background-color: #fff0; // transparent background so it doesn't mess with border-radius } @@ -28,6 +36,8 @@ body { .emoji-mart-bar:first-child { background: lighten($ui-base-color, 7%); border-bottom-color: #fff0; + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; } .emoji-mart-anchor:not(.emoji-mart-anchor-selected) { color: $ui-base-lighter-color; @@ -71,3 +81,42 @@ body { .glitch.local-settings__navigation__item { border-bottom-color: $ui-base-lighter-color; } + +// border radius +.button, +.dropdown-menu, +.account__avatar, +.search__input, +.spoiler-input input, +.language-dropdown__dropdown, +.privacy-dropdown__dropdown, +.poll__option input[type="text"], +.compose-form__poll-wrapper select, +.account__header__tabs__buttons .icon-button, +.emoji-picker-dropdown__menu, +.emoji-mart-search input, +.boost-modal, +.report-modal, +.block-modal, +.confirmation-modal, +.actions-modal, +.mute-modal, +.compare-history-modal { + border-radius: $border-radius; +} +.dropdown-menu { + padding-top: $border-radius; + padding-bottom: $border-radius; +} +.emoji-mart-scroll { + margin-bottom: $border-radius; + padding-bottom: 0; +} +.column-header { + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; +} +.column > .scrollable { + border-bottom-left-radius: $border-radius; + border-bottom-right-radius: $border-radius; +} diff --git a/app/javascript/flavours/glitch/styles/homogay/variables.scss b/app/javascript/flavours/glitch/styles/homogay/variables.scss index 335a22349f..e1304a526a 100644 --- a/app/javascript/flavours/glitch/styles/homogay/variables.scss +++ b/app/javascript/flavours/glitch/styles/homogay/variables.scss @@ -49,3 +49,5 @@ $media-modal-media-max-width: 100%; // put margins on top and bottom of image to avoid the screen covered by image. $media-modal-media-max-height: 80%; + +$border-radius: 8px; From 874d4a2585a27caf8dec71e5516bad99aa1b9396 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Thu, 17 Nov 2022 23:40:56 +0100 Subject: [PATCH 110/166] v4.0.2+1.0.1 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 7733bb2d8d..d980438e84 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.0' + '+1.0.1' end def to_a From 860fa1e79d1ad3f09082e3877487b918165d8287 Mon Sep 17 00:00:00 2001 From: fef Date: Sat, 19 Nov 2022 05:41:39 +0000 Subject: [PATCH 111/166] tweak and fix some colors --- .../flavours/glitch/styles/homogay/diff.scss | 20 +++++++++++++------ .../glitch/styles/homogay/variables.scss | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/javascript/flavours/glitch/styles/homogay/diff.scss b/app/javascript/flavours/glitch/styles/homogay/diff.scss index b628f70e4e..9cbfeb2718 100644 --- a/app/javascript/flavours/glitch/styles/homogay/diff.scss +++ b/app/javascript/flavours/glitch/styles/homogay/diff.scss @@ -1,7 +1,3 @@ -body { - background: darken($ui-base-color, 3%); -} - // hashtags in primary color .status__content a { color: $highlight-text-color; @@ -11,10 +7,11 @@ body { .compose-panel .autosuggest-textarea label .autosuggest-textarea__textarea, .compose-form .autosuggest-input label .autosuggest-textarea__textarea, .compose-form__buttons-wrapper, +.compose-form__warning, .spoiler-input input, .compose-form__modifiers, .reply-indicator { - background: $ui-base-color; + background: lighten($ui-base-color, 4%); color: $primary-text-color; } .compose-form .autosuggest-textarea label .autosuggest-textarea__textarea { @@ -43,6 +40,10 @@ body { color: $ui-base-lighter-color; } +.search__input { + background: lighten($ui-base-color, 4%); +} + // dropdowns .dropdown-menu, .dropdown-menu__item a, @@ -76,7 +77,7 @@ body { } .glitch.local-settings__navigation, .glitch.local-settings__navigation__item { - background: lighten($ui-base-color, 5%); + background: lighten($ui-base-color, 4%); } .glitch.local-settings__navigation__item { border-bottom-color: $ui-base-lighter-color; @@ -88,6 +89,7 @@ body { .account__avatar, .search__input, .spoiler-input input, +.status-card, .language-dropdown__dropdown, .privacy-dropdown__dropdown, .poll__option input[type="text"], @@ -120,3 +122,9 @@ body { border-bottom-left-radius: $border-radius; border-bottom-right-radius: $border-radius; } + +// no separators between posts +.status { + border-bottom: 0; + margin-bottom: 10px; +} diff --git a/app/javascript/flavours/glitch/styles/homogay/variables.scss b/app/javascript/flavours/glitch/styles/homogay/variables.scss index e1304a526a..db695529d6 100644 --- a/app/javascript/flavours/glitch/styles/homogay/variables.scss +++ b/app/javascript/flavours/glitch/styles/homogay/variables.scss @@ -9,7 +9,7 @@ $gold-star: #e4ba3d; $red-bookmark: $success-green; // Values from the classic Mastodon UI -$classic-base-color: #0f0b17; +$classic-base-color: #150f21; $classic-primary-color: #d4b6cb; $classic-secondary-color: #eaddf4; $classic-highlight-color: #de18a3; From 9d140856072e343963d67ea0e0d50c621364540b Mon Sep 17 00:00:00 2001 From: fef Date: Sat, 19 Nov 2022 05:43:03 +0000 Subject: [PATCH 112/166] bump to nyastodon 1.0.2 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 18d564bd3b..fc7ac43272 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ module Mastodon end def post_suffix_version - '-1.0.1' + '-1.0.2' end def to_a From e17b23f0b65a4f117113047156cebf5025c0cb66 Mon Sep 17 00:00:00 2001 From: fef Date: Sun, 20 Nov 2022 22:44:48 +0000 Subject: [PATCH 113/166] fix some more colors --- .../flavours/glitch/styles/homogay/diff.scss | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/javascript/flavours/glitch/styles/homogay/diff.scss b/app/javascript/flavours/glitch/styles/homogay/diff.scss index 9cbfeb2718..59731d8bc9 100644 --- a/app/javascript/flavours/glitch/styles/homogay/diff.scss +++ b/app/javascript/flavours/glitch/styles/homogay/diff.scss @@ -29,6 +29,13 @@ .compose-form__poll-wrapper select { border-color: $ui-base-lighter-color; } +.autosuggest-textarea__suggestions { + background: lighten($ui-base-color, 4%); +} +.autosuggest-textarea__suggestions__item.selected, +.autosuggest-textarea__suggestions__item:hover { + background: $ui-highlight-color; +} .emoji-mart-bar:first-child { background: lighten($ui-base-color, 7%); @@ -70,6 +77,22 @@ .report-modal__comment .setting-text { background: $ui-base-color; } +.boost-modal__action-bar, +.doodle-modal__action-bar, +.confirmation-modal__action-bar, +.mute-modal__action-bar, +.block-modal__action-bar { + background: lighten($ui-base-color, 5%); +} +.confirmation-modal__secondary-button, +.confirmation-modal__cancel-button, +.mute-modal__cancel-button, +.block-modal__cancel-button { + color: $primary-text-color; + &:hover { + color: $ui-highlight-color; + } +} // app settings modal .glitch.local-settings { From e4f17273a3729028ef5b4e2b6ed31eda1810c925 Mon Sep 17 00:00:00 2001 From: fef Date: Sun, 20 Nov 2022 22:45:09 +0000 Subject: [PATCH 114/166] improve animations --- .../glitch/styles/homogay/animations.scss | 106 ++++++++++++++++++ .../flavours/glitch/styles/homogay/diff.scss | 10 +- 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 app/javascript/flavours/glitch/styles/homogay/animations.scss diff --git a/app/javascript/flavours/glitch/styles/homogay/animations.scss b/app/javascript/flavours/glitch/styles/homogay/animations.scss new file mode 100644 index 0000000000..7d019d2e36 --- /dev/null +++ b/app/javascript/flavours/glitch/styles/homogay/animations.scss @@ -0,0 +1,106 @@ +// turn the timed line into a highly addictive dopamine slot machine + +// fav button +.no-reduce-motion .icon-button.star-icon { + &.deactivate { + & > .fa-star { + animation: none; + } + } + &.activate { + & > .fa-star { + animation: spring-rotate-in .8s cubic-bezier(.2, 0, .4, 1); + } + } +} +@keyframes spring-rotate-in { + 0% { + transform: rotate(0deg); + } + 60% { + transform: rotate(-380deg); + } + 85% { + transform: rotate(-355deg); + } + 100% { + transform: rotate(-360deg); + } +} +@keyframes spring-rotate-out { + 0% { + transform: rotate(-360deg); + } + 60% { + transform: rotate(20deg); + } + 85% { + transform: rotate(-5deg); + } + 100% { + transform: rotate(0deg); + } +} + +// bookmark button +.no-reduce-motion .icon-button { + & > .fa-bookmark { + transform: translateY(0); + animation: none; + } + &.active > .fa-bookmark { + animation: bookmark-save .6s cubic-bezier(.2, 0, .4, 1); + } +} +@keyframes bookmark-save { + 0% { + transform: translateY(0); + } + 40% { + transform: translateY(-3px); + } + 60% { + transform: translateY(4px); + } + 100% { + transform: translateY(0); + } +} + +// collapse button at the top right of all posts +.no-reduce-motion .status__collapse-button { + &.activate > .fa-angle-double-up { + animation: spring-flip-in .5s cubic-bezier(.2, 0, .4, 1); + } + &.deactivate > .fa-angle-double-up { + animation: spring-flip-out .5s cubic-bezier(.2, 0, .4, 1); + } +} +@keyframes spring-flip-in { + 0% { + transform: rotate(0deg); + } + 60% { + transform: rotate(-190deg); + } + 85% { + transform: rotate(-175deg); + } + 100% { + transform: rotate(-180deg); + } +} +@keyframes spring-flip-out { + 0% { + transform: rotate(-180deg); + } + 60% { + transform: rotate(10deg); + } + 85% { + transform: rotate(-5deg) + } + 100% { + transform: rotate(0deg); + } +} diff --git a/app/javascript/flavours/glitch/styles/homogay/diff.scss b/app/javascript/flavours/glitch/styles/homogay/diff.scss index 59731d8bc9..bfa451eb5a 100644 --- a/app/javascript/flavours/glitch/styles/homogay/diff.scss +++ b/app/javascript/flavours/glitch/styles/homogay/diff.scss @@ -146,8 +146,12 @@ border-bottom-right-radius: $border-radius; } -// no separators between posts +// timed line .status { - border-bottom: 0; - margin-bottom: 10px; + border-bottom: 0; // no separators between posts } +.media-gallery__item.letterbox { + background: none; // remove the black background from letterbox images +} + +@import 'animations'; From 1792b688ac6b0eeffa05076327cdf18bc97f5b48 Mon Sep 17 00:00:00 2001 From: fef Date: Sun, 20 Nov 2022 22:46:00 +0000 Subject: [PATCH 115/166] bump to nyastodon 1.0.3 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index fc7ac43272..4bc1b5d643 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ module Mastodon end def post_suffix_version - '-1.0.2' + '-1.0.3' end def to_a From 0da3340f0d1c7257e8398bd339fddb2f78a5220f Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 24 Nov 2022 05:59:09 +0000 Subject: [PATCH 116/166] add emoji reaction database model --- app/models/emoji_reaction.rb | 16 ++++++++++++ app/models/status.rb | 1 + .../20221123004534_create_emoji_reactions.rb | 11 ++++++++ db/schema.rb | 26 ++++++++++++++++++- spec/fabricators/emoji_reaction_fabricator.rb | 5 ++++ spec/models/emoji_reaction_spec.rb | 5 ++++ 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 app/models/emoji_reaction.rb create mode 100644 db/migrate/20221123004534_create_emoji_reactions.rb create mode 100644 spec/fabricators/emoji_reaction_fabricator.rb create mode 100644 spec/models/emoji_reaction_spec.rb diff --git a/app/models/emoji_reaction.rb b/app/models/emoji_reaction.rb new file mode 100644 index 0000000000..4ffabc76f2 --- /dev/null +++ b/app/models/emoji_reaction.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: emoji_reactions +# +# id :bigint(8) not null, primary key +# emoji :string +# status_id :bigint(8) not null +# account_id :bigint(8) not null +# created_at :datetime not null +# updated_at :datetime not null +# +class EmojiReaction < ApplicationRecord + belongs_to :status + belongs_to :account +end diff --git a/app/models/status.rb b/app/models/status.rb index 044816be76..5c81fb8e2d 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -70,6 +70,7 @@ class Status < ApplicationRecord has_many :mentions, dependent: :destroy, inverse_of: :status has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify + has_many :emoji_reactions has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards diff --git a/db/migrate/20221123004534_create_emoji_reactions.rb b/db/migrate/20221123004534_create_emoji_reactions.rb new file mode 100644 index 0000000000..d013ecca28 --- /dev/null +++ b/db/migrate/20221123004534_create_emoji_reactions.rb @@ -0,0 +1,11 @@ +class CreateEmojiReactions < ActiveRecord::Migration[6.1] + def change + create_table :emoji_reactions do |t| + t.string :emoji + t.references :status, null: false, foreign_key: true + t.references :account, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 01a95c0f29..c4c483e7e9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_11_04_133904) do +ActiveRecord::Schema.define(version: 2022_11_23_004534) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -411,6 +411,16 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end + create_table "emoji_reactions", force: :cascade do |t| + t.string "emoji" + t.bigint "status_id", null: false + t.bigint "account_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["account_id"], name: "index_emoji_reactions_on_account_id" + t.index ["status_id"], name: "index_emoji_reactions_on_status_id" + end + create_table "encrypted_messages", id: :bigint, default: -> { "timestamp_id('encrypted_messages'::text)" }, force: :cascade do |t| t.bigint "device_id" t.bigint "from_account_id" @@ -781,6 +791,16 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id" end + create_table "reactions", force: :cascade do |t| + t.string "emoji" + t.bigint "status_id", null: false + t.bigint "account_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["account_id"], name: "index_reactions_on_account_id" + t.index ["status_id"], name: "index_reactions_on_status_id" + end + create_table "relays", force: :cascade do |t| t.string "inbox_url", default: "", null: false t.string "follow_activity_id" @@ -1158,6 +1178,8 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do add_foreign_key "devices", "accounts", on_delete: :cascade add_foreign_key "devices", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade + add_foreign_key "emoji_reactions", "accounts" + add_foreign_key "emoji_reactions", "statuses" add_foreign_key "encrypted_messages", "accounts", column: "from_account_id", on_delete: :cascade add_foreign_key "encrypted_messages", "devices", on_delete: :cascade add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade @@ -1198,6 +1220,8 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do add_foreign_key "polls", "accounts", on_delete: :cascade add_foreign_key "polls", "statuses", on_delete: :cascade add_foreign_key "preview_card_trends", "preview_cards", on_delete: :cascade + add_foreign_key "reactions", "accounts" + add_foreign_key "reactions", "statuses" add_foreign_key "report_notes", "accounts", on_delete: :cascade add_foreign_key "report_notes", "reports", on_delete: :cascade add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", name: "fk_bca45b75fd", on_delete: :nullify diff --git a/spec/fabricators/emoji_reaction_fabricator.rb b/spec/fabricators/emoji_reaction_fabricator.rb new file mode 100644 index 0000000000..b7dc4d61a4 --- /dev/null +++ b/spec/fabricators/emoji_reaction_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:emoji_reaction) do + emoji "MyString" + status nil + account nil +end \ No newline at end of file diff --git a/spec/models/emoji_reaction_spec.rb b/spec/models/emoji_reaction_spec.rb new file mode 100644 index 0000000000..e4ff8a6c17 --- /dev/null +++ b/spec/models/emoji_reaction_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe EmojiReaction, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 416d42e9491885e8f5f9299f303e8f1bbcd737d2 Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 24 Nov 2022 10:33:50 +0000 Subject: [PATCH 117/166] Revert "add emoji reaction database model" I didn't intend to commit this to main and it's kind of a work in progress anyway. This reverts commit 0da3340f0d1c7257e8398bd339fddb2f78a5220f. --- app/models/emoji_reaction.rb | 16 ------------ app/models/status.rb | 1 - .../20221123004534_create_emoji_reactions.rb | 11 -------- db/schema.rb | 26 +------------------ spec/fabricators/emoji_reaction_fabricator.rb | 5 ---- spec/models/emoji_reaction_spec.rb | 5 ---- 6 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 app/models/emoji_reaction.rb delete mode 100644 db/migrate/20221123004534_create_emoji_reactions.rb delete mode 100644 spec/fabricators/emoji_reaction_fabricator.rb delete mode 100644 spec/models/emoji_reaction_spec.rb diff --git a/app/models/emoji_reaction.rb b/app/models/emoji_reaction.rb deleted file mode 100644 index 4ffabc76f2..0000000000 --- a/app/models/emoji_reaction.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: emoji_reactions -# -# id :bigint(8) not null, primary key -# emoji :string -# status_id :bigint(8) not null -# account_id :bigint(8) not null -# created_at :datetime not null -# updated_at :datetime not null -# -class EmojiReaction < ApplicationRecord - belongs_to :status - belongs_to :account -end diff --git a/app/models/status.rb b/app/models/status.rb index 5c81fb8e2d..044816be76 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -70,7 +70,6 @@ class Status < ApplicationRecord has_many :mentions, dependent: :destroy, inverse_of: :status has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify - has_many :emoji_reactions has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards diff --git a/db/migrate/20221123004534_create_emoji_reactions.rb b/db/migrate/20221123004534_create_emoji_reactions.rb deleted file mode 100644 index d013ecca28..0000000000 --- a/db/migrate/20221123004534_create_emoji_reactions.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateEmojiReactions < ActiveRecord::Migration[6.1] - def change - create_table :emoji_reactions do |t| - t.string :emoji - t.references :status, null: false, foreign_key: true - t.references :account, null: false, foreign_key: true - - t.timestamps - end - end -end diff --git a/db/schema.rb b/db/schema.rb index c4c483e7e9..01a95c0f29 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_11_23_004534) do +ActiveRecord::Schema.define(version: 2022_11_04_133904) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -411,16 +411,6 @@ ActiveRecord::Schema.define(version: 2022_11_23_004534) do t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end - create_table "emoji_reactions", force: :cascade do |t| - t.string "emoji" - t.bigint "status_id", null: false - t.bigint "account_id", null: false - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index ["account_id"], name: "index_emoji_reactions_on_account_id" - t.index ["status_id"], name: "index_emoji_reactions_on_status_id" - end - create_table "encrypted_messages", id: :bigint, default: -> { "timestamp_id('encrypted_messages'::text)" }, force: :cascade do |t| t.bigint "device_id" t.bigint "from_account_id" @@ -791,16 +781,6 @@ ActiveRecord::Schema.define(version: 2022_11_23_004534) do t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id" end - create_table "reactions", force: :cascade do |t| - t.string "emoji" - t.bigint "status_id", null: false - t.bigint "account_id", null: false - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.index ["account_id"], name: "index_reactions_on_account_id" - t.index ["status_id"], name: "index_reactions_on_status_id" - end - create_table "relays", force: :cascade do |t| t.string "inbox_url", default: "", null: false t.string "follow_activity_id" @@ -1178,8 +1158,6 @@ ActiveRecord::Schema.define(version: 2022_11_23_004534) do add_foreign_key "devices", "accounts", on_delete: :cascade add_foreign_key "devices", "oauth_access_tokens", column: "access_token_id", on_delete: :cascade add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade - add_foreign_key "emoji_reactions", "accounts" - add_foreign_key "emoji_reactions", "statuses" add_foreign_key "encrypted_messages", "accounts", column: "from_account_id", on_delete: :cascade add_foreign_key "encrypted_messages", "devices", on_delete: :cascade add_foreign_key "favourites", "accounts", name: "fk_5eb6c2b873", on_delete: :cascade @@ -1220,8 +1198,6 @@ ActiveRecord::Schema.define(version: 2022_11_23_004534) do add_foreign_key "polls", "accounts", on_delete: :cascade add_foreign_key "polls", "statuses", on_delete: :cascade add_foreign_key "preview_card_trends", "preview_cards", on_delete: :cascade - add_foreign_key "reactions", "accounts" - add_foreign_key "reactions", "statuses" add_foreign_key "report_notes", "accounts", on_delete: :cascade add_foreign_key "report_notes", "reports", on_delete: :cascade add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", name: "fk_bca45b75fd", on_delete: :nullify diff --git a/spec/fabricators/emoji_reaction_fabricator.rb b/spec/fabricators/emoji_reaction_fabricator.rb deleted file mode 100644 index b7dc4d61a4..0000000000 --- a/spec/fabricators/emoji_reaction_fabricator.rb +++ /dev/null @@ -1,5 +0,0 @@ -Fabricator(:emoji_reaction) do - emoji "MyString" - status nil - account nil -end \ No newline at end of file diff --git a/spec/models/emoji_reaction_spec.rb b/spec/models/emoji_reaction_spec.rb deleted file mode 100644 index e4ff8a6c17..0000000000 --- a/spec/models/emoji_reaction_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe EmojiReaction, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end From 6a251a7dd845a0363f690ae3bc7118b01d009985 Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 24 Nov 2022 11:50:32 +0000 Subject: [PATCH 118/166] add backend support for status emoji reactions turns out we can just reuse the code for announcement reactions. --- .../api/v1/statuses/reactions_controller.rb | 29 ++++++++++++++++++ app/models/status.rb | 16 ++++++++++ app/models/status_reaction.rb | 29 ++++++++++++++++++ app/serializers/rest/status_serializer.rb | 5 ++++ app/validators/status_reaction_validator.rb | 28 +++++++++++++++++ config/routes.rb | 1 + .../20221124114030_create_status_reactions.rb | 14 +++++++++ db/schema.rb | 30 ++++++++++++++++++- .../fabricators/status_reaction_fabricator.rb | 6 ++++ spec/models/status_reaction_spec.rb | 5 ++++ 10 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/v1/statuses/reactions_controller.rb create mode 100644 app/models/status_reaction.rb create mode 100644 app/validators/status_reaction_validator.rb create mode 100644 db/migrate/20221124114030_create_status_reactions.rb create mode 100644 spec/fabricators/status_reaction_fabricator.rb create mode 100644 spec/models/status_reaction_spec.rb diff --git a/app/controllers/api/v1/statuses/reactions_controller.rb b/app/controllers/api/v1/statuses/reactions_controller.rb new file mode 100644 index 0000000000..9a1bf57079 --- /dev/null +++ b/app/controllers/api/v1/statuses/reactions_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Api::V1::Statuses::ReactionsController < Api::BaseController + before_action -> { doorkeeper_authorize! :write, :'write:favourites' } + before_action :require_user! + + before_action :set_status + before_action :set_reaction, except: :update + + def update + @status.status_reactions.create!(account: current_account, name: params[:id]) + render_empty + end + + def destroy + @reaction.destroy! + render_empty + end + + private + + def set_reaction + @reaction = @status.status_reactions.where(account: current_account).find_by!(name: params[:id]) + end + + def set_status + @status = Status.find(params[:status_id]) + end +end diff --git a/app/models/status.rb b/app/models/status.rb index 044816be76..f8eb988c2c 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -70,6 +70,7 @@ class Status < ApplicationRecord has_many :mentions, dependent: :destroy, inverse_of: :status has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify + has_many :status_reactions, dependent: :destroy has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards @@ -262,6 +263,21 @@ class Status < ApplicationRecord @emojis = CustomEmoji.from_text(fields.join(' '), account.domain) end + def reactions(account = nil) + records = begin + scope = status_reactions.group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at) ASC')) + + if account.nil? + scope.select('name, custom_emoji_id, count(*) as count, false as me') + else + scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from status_reactions r where r.account_id = #{account.id} and r.status_id = status_reactions.status_id and r.name = status_reactions.name) as me") + end + end + + ActiveRecord::Associations::Preloader.new.preload(records, :custom_emoji) + records + end + def ordered_media_attachments if ordered_media_attachment_ids.nil? media_attachments diff --git a/app/models/status_reaction.rb b/app/models/status_reaction.rb new file mode 100644 index 0000000000..32cb9edd4a --- /dev/null +++ b/app/models/status_reaction.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: status_reactions +# +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# status_id :bigint(8) not null +# name :string default(""), not null +# custom_emoji_id :bigint(8) +# created_at :datetime not null +# updated_at :datetime not null +# +class StatusReaction < ApplicationRecord + belongs_to :account + belongs_to :status, inverse_of: :status_reactions + belongs_to :custom_emoji, optional: true + + validates :name, presence: true + validates_with StatusReactionValidator + + before_validation :set_custom_emoji + + private + + def set_custom_emoji + self.custom_emoji = CustomEmoji.local.find_by(disabled: false, shortcode: name) if name.present? + end +end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 659c45b835..43c1e86aff 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -28,6 +28,7 @@ class REST::StatusSerializer < ActiveModel::Serializer has_many :ordered_mentions, key: :mentions has_many :tags has_many :emojis, serializer: REST::CustomEmojiSerializer + has_many :reactions, serializer: REST::ReactionSerializer has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer @@ -146,6 +147,10 @@ class REST::StatusSerializer < ActiveModel::Serializer object.active_mentions.to_a.sort_by(&:id) end + def reactions + object.reactions(current_user&.account) + end + class ApplicationSerializer < ActiveModel::Serializer attributes :name, :website diff --git a/app/validators/status_reaction_validator.rb b/app/validators/status_reaction_validator.rb new file mode 100644 index 0000000000..7c1c6983bb --- /dev/null +++ b/app/validators/status_reaction_validator.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class StatusReactionValidator < ActiveModel::Validator + SUPPORTED_EMOJIS = Oj.load_file(Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json').to_s).keys.freeze + + LIMIT = 8 + + def validate(reaction) + return if reaction.name.blank? + + reaction.errors.add(:name, I18n.t('reactions.errors.unrecognized_emoji')) if reaction.custom_emoji_id.blank? && !unicode_emoji?(reaction.name) + reaction.errors.add(:base, I18n.t('reactions.errors.limit_reached')) if new_reaction?(reaction) && limit_reached?(reaction) + end + + private + + def unicode_emoji?(name) + SUPPORTED_EMOJIS.include?(name) + end + + def new_reaction?(reaction) + !reaction.status.status_reactions.where(name: reaction.name).exists? + end + + def limit_reached?(reaction) + reaction.status.status_reactions.where.not(name: reaction.name).count('distinct name') >= LIMIT + end +end diff --git a/config/routes.rb b/config/routes.rb index 8639f0ef53..da4ddfa200 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -453,6 +453,7 @@ Rails.application.routes.draw do resource :history, only: :show resource :source, only: :show + resources :reactions, only: [:update, :destroy] post :translate, to: 'translations#create' end diff --git a/db/migrate/20221124114030_create_status_reactions.rb b/db/migrate/20221124114030_create_status_reactions.rb new file mode 100644 index 0000000000..bbf1f3376a --- /dev/null +++ b/db/migrate/20221124114030_create_status_reactions.rb @@ -0,0 +1,14 @@ +class CreateStatusReactions < ActiveRecord::Migration[6.1] + def change + create_table :status_reactions do |t| + t.references :account, null: false, foreign_key: true + t.references :status, null: false, foreign_key: true + t.string :name, null: false, default: '' + t.references :custom_emoji, null: true, foreign_key: true + + t.timestamps + end + + add_index :status_reactions, [:account_id, :status_id, :name], unique: true, name: :index_status_reactions_on_account_id_and_status_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 01a95c0f29..b80ce713ed 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_11_04_133904) do +ActiveRecord::Schema.define(version: 2022_11_24_114030) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -781,6 +781,16 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id" end + create_table "reactions", force: :cascade do |t| + t.string "emoji" + t.bigint "status_id", null: false + t.bigint "account_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["account_id"], name: "index_reactions_on_account_id" + t.index ["status_id"], name: "index_reactions_on_status_id" + end + create_table "relays", force: :cascade do |t| t.string "inbox_url", default: "", null: false t.string "follow_activity_id" @@ -897,6 +907,19 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do t.index ["status_id"], name: "index_status_pins_on_status_id" end + create_table "status_reactions", force: :cascade do |t| + t.bigint "account_id", null: false + t.bigint "status_id", null: false + t.string "name", default: "", null: false + t.bigint "custom_emoji_id" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["account_id", "status_id", "name"], name: "index_status_reactions_on_account_id_and_status_id", unique: true + t.index ["account_id"], name: "index_status_reactions_on_account_id" + t.index ["custom_emoji_id"], name: "index_status_reactions_on_custom_emoji_id" + t.index ["status_id"], name: "index_status_reactions_on_status_id" + end + create_table "status_stats", force: :cascade do |t| t.bigint "status_id", null: false t.bigint "replies_count", default: 0, null: false @@ -1198,6 +1221,8 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do add_foreign_key "polls", "accounts", on_delete: :cascade add_foreign_key "polls", "statuses", on_delete: :cascade add_foreign_key "preview_card_trends", "preview_cards", on_delete: :cascade + add_foreign_key "reactions", "accounts" + add_foreign_key "reactions", "statuses" add_foreign_key "report_notes", "accounts", on_delete: :cascade add_foreign_key "report_notes", "reports", on_delete: :cascade add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", name: "fk_bca45b75fd", on_delete: :nullify @@ -1211,6 +1236,9 @@ ActiveRecord::Schema.define(version: 2022_11_04_133904) do add_foreign_key "status_edits", "statuses", on_delete: :cascade add_foreign_key "status_pins", "accounts", name: "fk_d4cb435b62", on_delete: :cascade add_foreign_key "status_pins", "statuses", on_delete: :cascade + add_foreign_key "status_reactions", "accounts" + add_foreign_key "status_reactions", "custom_emojis" + add_foreign_key "status_reactions", "statuses" add_foreign_key "status_stats", "statuses", on_delete: :cascade add_foreign_key "status_trends", "accounts", on_delete: :cascade add_foreign_key "status_trends", "statuses", on_delete: :cascade diff --git a/spec/fabricators/status_reaction_fabricator.rb b/spec/fabricators/status_reaction_fabricator.rb new file mode 100644 index 0000000000..3d4b93efe0 --- /dev/null +++ b/spec/fabricators/status_reaction_fabricator.rb @@ -0,0 +1,6 @@ +Fabricator(:status_reaction) do + account nil + status nil + name "MyString" + custom_emoji nil +end \ No newline at end of file diff --git a/spec/models/status_reaction_spec.rb b/spec/models/status_reaction_spec.rb new file mode 100644 index 0000000000..18860318cc --- /dev/null +++ b/spec/models/status_reaction_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe StatusReaction, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 9172df2fdfd41bc83bee3c5aad680f3618266590 Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 24 Nov 2022 17:30:52 +0000 Subject: [PATCH 119/166] add frontend for emoji reactions this is still pretty bare bones but hey, it works. --- .../flavours/glitch/actions/interactions.js | 85 +++++++++ .../flavours/glitch/components/status.js | 12 ++ .../glitch/components/status_reactions_bar.js | 177 ++++++++++++++++++ .../glitch/containers/status_container.js | 16 +- .../flavours/glitch/reducers/statuses.js | 44 +++++ 5 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 app/javascript/flavours/glitch/components/status_reactions_bar.js diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index 225ee7eb2a..ab275621c2 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -41,6 +41,16 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST'; export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS'; export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL'; +export const STATUS_REACTION_UPDATE = 'STATUS_REACTION_UPDATE'; + +export const STATUS_REACTION_ADD_REQUEST = 'STATUS_REACTION_ADD_REQUEST'; +export const STATUS_REACTION_ADD_SUCCESS = 'STATUS_REACTION_ADD_SUCCESS'; +export const STATUS_REACTION_ADD_FAIL = 'STATUS_REACTION_ADD_FAIL'; + +export const STATUS_REACTION_REMOVE_REQUEST = 'STATUS_REACTION_REMOVE_REQUEST'; +export const STATUS_REACTION_REMOVE_SUCCESS = 'STATUS_REACTION_REMOVE_SUCCESS'; +export const STATUS_REACTION_REMOVE_FAIL = 'STATUS_REACTION_REMOVE_FAIL'; + export function reblog(status, visibility) { return function (dispatch, getState) { dispatch(reblogRequest(status)); @@ -392,3 +402,78 @@ export function unpinFail(status, error) { error, }; }; + +export const statusAddReaction = (statusId, name) => (dispatch, getState) => { + const status = getState().get('statuses').get(statusId); + let alreadyAdded = false; + if (status) { + const reaction = status.get('reactions').find(x => x.get('name') === name); + if (reaction && reaction.get('me')) { + alreadyAdded = true; + } + } + if (!alreadyAdded) { + dispatch(statusAddReactionRequest(statusId, name, alreadyAdded)); + } + + api(getState).put(`/api/v1/statuses/${statusId}/reactions/${name}`).then(() => { + dispatch(statusAddReactionSuccess(statusId, name, alreadyAdded)); + }).catch(err => { + if (!alreadyAdded) { + dispatch(statusAddReactionFail(statusId, name, err)); + } + }); +}; + +export const statusAddReactionRequest = (statusId, name) => ({ + type: STATUS_REACTION_ADD_REQUEST, + id: statusId, + name, + skipLoading: true, +}); + +export const statusAddReactionSuccess = (statusId, name) => ({ + type: STATUS_REACTION_ADD_SUCCESS, + id: statusId, + name, + skipLoading: true, +}); + +export const statusAddReactionFail = (statusId, name, error) => ({ + type: STATUS_REACTION_ADD_FAIL, + id: statusId, + name, + error, + skipLoading: true, +}); + +export const statusRemoveReaction = (statusId, name) => (dispatch, getState) => { + dispatch(statusRemoveReactionRequest(statusId, name)); + + api(getState).delete(`/api/v1/statuses/${statusId}/reactions/${name}`).then(() => { + dispatch(statusRemoveReactionSuccess(statusId, name)); + }).catch(err => { + dispatch(statusRemoveReactionFail(statusId, name, err)); + }); +}; + +export const statusRemoveReactionRequest = (statusId, name) => ({ + type: STATUS_REACTION_REMOVE_REQUEST, + id: statusId, + name, + skipLoading: true, +}); + +export const statusRemoveReactionSuccess = (statusId, name) => ({ + type: STATUS_REACTION_REMOVE_SUCCESS, + id: statusId, + name, + skipLoading: true, +}); + +export const statusRemoveReactionFail = (statusId, name) => ({ + type: STATUS_REACTION_REMOVE_FAIL, + id: statusId, + name, + skipLoading: true, +}); diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 800832dc8e..5207ece21f 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -6,6 +6,7 @@ import StatusHeader from './status_header'; import StatusIcons from './status_icons'; import StatusContent from './status_content'; import StatusActionBar from './status_action_bar'; +import StatusReactionsBar from './status_reactions_bar'; import AttachmentList from './attachment_list'; import Card from '../features/status/components/card'; import { injectIntl, FormattedMessage } from 'react-intl'; @@ -75,6 +76,8 @@ class Status extends ImmutablePureComponent { onDelete: PropTypes.func, onDirect: PropTypes.func, onMention: PropTypes.func, + onReactionAdd: PropTypes.func, + onReactionRemove: PropTypes.func, onPin: PropTypes.func, onOpenMedia: PropTypes.func, onOpenVideo: PropTypes.func, @@ -101,6 +104,7 @@ class Status extends ImmutablePureComponent { scrollKey: PropTypes.string, deployPictureInPicture: PropTypes.func, settings: ImmutablePropTypes.map.isRequired, + emojiMap: ImmutablePropTypes.map.isRequired, pictureInPicture: PropTypes.shape({ inUse: PropTypes.bool, available: PropTypes.bool, @@ -794,6 +798,14 @@ class Status extends ImmutablePureComponent { rewriteMentions={settings.get('rewrite_mentions')} /> + + {!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar'])) ? ( { + const { addReaction, statusId } = this.props; + addReaction(statusId, data.native.replace(/:/g, '')); + } + + willEnter() { + return { scale: reduceMotion ? 1 : 0 }; + } + + willLeave() { + return { scale: reduceMotion ? 0 : spring(0, { stiffness: 170, damping: 26 }) }; + } + + render() { + const { reactions } = this.props; + const visibleReactions = reactions.filter(x => x.get('count') > 0); + + const styles = visibleReactions.map(reaction => ({ + key: reaction.get('name'), + data: reaction, + style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) }, + })).toArray(); + + return ( + + {items => ( +

+ {items.map(({ key, data, style }) => ( + + ))} + + {visibleReactions.size < 8 && } />} +
+ )} + + ); + } + +} + +class Reaction extends ImmutablePureComponent { + + static propTypes = { + statusId: PropTypes.string, + reaction: ImmutablePropTypes.map.isRequired, + addReaction: PropTypes.func.isRequired, + removeReaction: PropTypes.func.isRequired, + emojiMap: ImmutablePropTypes.map.isRequired, + style: PropTypes.object, + }; + + state = { + hovered: false, + }; + + handleClick = () => { + const { reaction, statusId, addReaction, removeReaction } = this.props; + + if (reaction.get('me')) { + removeReaction(statusId, reaction.get('name')); + } else { + addReaction(statusId, reaction.get('name')); + } + } + + handleMouseEnter = () => this.setState({ hovered: true }) + + handleMouseLeave = () => this.setState({ hovered: false }) + + render() { + const { reaction } = this.props; + + let shortCode = reaction.get('name'); + + if (unicodeMapping[shortCode]) { + shortCode = unicodeMapping[shortCode].shortCode; + } + + return ( + + ); + } + +} + +class Emoji extends React.PureComponent { + + static propTypes = { + emoji: PropTypes.string.isRequired, + emojiMap: ImmutablePropTypes.map.isRequired, + hovered: PropTypes.bool.isRequired, + }; + + render() { + const { emoji, emojiMap, hovered } = this.props; + + if (unicodeMapping[emoji]) { + const { filename, shortCode } = unicodeMapping[this.props.emoji]; + const title = shortCode ? `:${shortCode}:` : ''; + + return ( + {emoji} + ); + } else if (emojiMap.get(emoji)) { + const filename = (autoPlayGif || hovered) + ? emojiMap.getIn([emoji, 'url']) + : emojiMap.getIn([emoji, 'static_url']); + const shortCode = `:${emoji}:`; + + return ( + {shortCode} + ); + } else { + return null; + } + } + +} diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index c12b2e6143..aed1df96e0 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -1,6 +1,5 @@ import { connect } from 'react-redux'; import Status from 'flavours/glitch/components/status'; -import { List as ImmutableList } from 'immutable'; import { makeGetStatus } from 'flavours/glitch/selectors'; import { replyCompose, @@ -16,6 +15,8 @@ import { unbookmark, pin, unpin, + statusAddReaction, + statusRemoveReaction, } from 'flavours/glitch/actions/interactions'; import { muteStatus, @@ -42,6 +43,10 @@ import { showAlertForError } from '../actions/alerts'; import AccountContainer from 'flavours/glitch/containers/account_container'; import Spoilers from '../components/spoilers'; import Icon from 'flavours/glitch/components/icon'; +import { createSelector } from 'reselect'; +import { Map as ImmutableMap } from 'immutable'; + +const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap())); const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -81,6 +86,7 @@ const makeMapStateToProps = () => { account: account || props.account, settings: state.get('local_settings'), prepend: prepend || props.prepend, + emojiMap: customEmojiMap(state), pictureInPicture: { inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id, @@ -164,6 +170,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ } }, + onReactionAdd (statusId, name) { + dispatch(statusAddReaction(statusId, name)); + }, + + onReactionRemove (statusId, name) { + dispatch(statusRemoveReaction(statusId, name)); + }, + onEmbed (status) { dispatch(openModal('EMBED', { url: status.get('url'), diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js index b47155c5f6..c469f4cc16 100644 --- a/app/javascript/flavours/glitch/reducers/statuses.js +++ b/app/javascript/flavours/glitch/reducers/statuses.js @@ -6,6 +6,11 @@ import { UNFAVOURITE_SUCCESS, BOOKMARK_REQUEST, BOOKMARK_FAIL, + STATUS_REACTION_UPDATE, + STATUS_REACTION_ADD_FAIL, + STATUS_REACTION_REMOVE_FAIL, + STATUS_REACTION_ADD_REQUEST, + STATUS_REACTION_REMOVE_REQUEST, } from 'flavours/glitch/actions/interactions'; import { STATUS_MUTE_SUCCESS, @@ -35,6 +40,37 @@ const deleteStatus = (state, id, references) => { return state.delete(id); }; +const updateReaction = (state, id, name, updater) => state.update( + id, + status => status.update( + 'reactions', + reactions => { + const index = reactions.findIndex(reaction => reaction.get('name') === name); + if (index > -1) { + return reactions.update(index, reaction => updater(reaction)); + } else { + return reactions.push(updater(fromJS({ name, count: 0 }))); + } + }, + ), +); + +const updateReactionCount = (state, reaction) => updateReaction(state, reaction.status_id, reaction.name, x => x.set('count', reaction.count)); + +const addReaction = (state, id, name) => updateReaction( + state, + id, + name, + x => x.set('me', true).update('count', n => n + 1), +); + +const removeReaction = (state, id, name) => updateReaction( + state, + id, + name, + x => x.set('me', false).update('count', n => n - 1), +); + const initialState = ImmutableMap(); export default function statuses(state = initialState, action) { @@ -61,6 +97,14 @@ export default function statuses(state = initialState, action) { return state.setIn([action.status.get('id'), 'reblogged'], true); case REBLOG_FAIL: return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false); + case STATUS_REACTION_UPDATE: + return updateReactionCount(state, action.reaction); + case STATUS_REACTION_ADD_REQUEST: + case STATUS_REACTION_REMOVE_FAIL: + return addReaction(state, action.id, action.name); + case STATUS_REACTION_REMOVE_REQUEST: + case STATUS_REACTION_ADD_FAIL: + return removeReaction(state, action.id, action.name); case STATUS_MUTE_SUCCESS: return state.setIn([action.id, 'muted'], true); case STATUS_UNMUTE_SUCCESS: From 7b4f3d26474508cad5cfdbb0d14caa389f3d218f Mon Sep 17 00:00:00 2001 From: fef Date: Thu, 24 Nov 2022 17:34:45 +0000 Subject: [PATCH 120/166] bump to nyastodon 1.1.0beta1 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 3a20daa2b1..3b91c7c840 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ module Mastodon end def post_suffix_version - '-1.0.3' + '-1.1.0beta1' end def to_a From 78a86bda163eb98304506666c40a313ec40d48a3 Mon Sep 17 00:00:00 2001 From: fef Date: Fri, 25 Nov 2022 23:02:40 +0000 Subject: [PATCH 121/166] show reactions in detailed status view --- .../glitch/containers/status_container.js | 7 ++--- .../status/components/detailed_status.js | 12 +++++++ .../flavours/glitch/features/status/index.js | 31 +++++++++++++++++++ .../flavours/glitch/utils/emoji_map.js | 11 +++++++ 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 app/javascript/flavours/glitch/utils/emoji_map.js diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index aed1df96e0..54d03b9543 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -43,10 +43,7 @@ import { showAlertForError } from '../actions/alerts'; import AccountContainer from 'flavours/glitch/containers/account_container'; import Spoilers from '../components/spoilers'; import Icon from 'flavours/glitch/components/icon'; -import { createSelector } from 'reselect'; -import { Map as ImmutableMap } from 'immutable'; - -const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap())); +import buildCustomEmojiMap from '../utils/emoji_map'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -86,7 +83,7 @@ const makeMapStateToProps = () => { account: account || props.account, settings: state.get('local_settings'), prepend: prepend || props.prepend, - emojiMap: customEmojiMap(state), + emojiMap: buildCustomEmojiMap(state), pictureInPicture: { inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id, diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index 46770930f5..03dc922820 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -20,6 +20,7 @@ import Icon from 'flavours/glitch/components/icon'; import AnimatedNumber from 'flavours/glitch/components/animated_number'; import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder'; import EditedTimestamp from 'flavours/glitch/components/edited_timestamp'; +import StatusReactionsBar from '../../../components/status_reactions_bar'; export default @injectIntl class DetailedStatus extends ImmutablePureComponent { @@ -42,6 +43,9 @@ class DetailedStatus extends ImmutablePureComponent { showMedia: PropTypes.bool, usingPiP: PropTypes.bool, onToggleMediaVisibility: PropTypes.func, + onReactionAdd: PropTypes.func.isRequired, + onReactionRemove: PropTypes.func.isRequired, + emojiMap: ImmutablePropTypes.map.isRequired, intl: PropTypes.object.isRequired, }; @@ -312,6 +316,14 @@ class DetailedStatus extends ImmutablePureComponent { disabled /> + +
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index aaa9c7928f..2be0dd173d 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -20,6 +20,8 @@ import { unreblog, pin, unpin, + statusAddReaction, + statusRemoveReaction, } from 'flavours/glitch/actions/interactions'; import { replyCompose, @@ -54,6 +56,7 @@ import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import { textForScreenReader, defaultMediaVisibility } from 'flavours/glitch/components/status'; import Icon from 'flavours/glitch/components/icon'; import { Helmet } from 'react-helmet'; +import buildCustomEmojiMap from '../../utils/emoji_map'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -144,6 +147,7 @@ const makeMapStateToProps = () => { askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0, domain: state.getIn(['meta', 'domain']), usingPiP: state.get('picture_in_picture').statusId === props.params.statusId, + emojiMap: buildCustomEmojiMap(state), }; }; @@ -289,6 +293,30 @@ class Status extends ImmutablePureComponent { } } + handleReactionAdd = (statusId, name) => { + const { dispatch } = this.props; + const { signedIn } = this.context.identity; + + if (signedIn) { + dispatch(statusAddReaction(statusId, name)); + } else { + dispatch(openModal('INTERACTION', { + type: 'reaction_add', + accountId: status.getIn(['account', 'id']), + url: status.get('url'), + })); + } + } + + handleReactionRemove = (statusId, name) => { + const { dispatch } = this.props; + const { signedIn } = this.context.identity; + + if (signedIn) { + dispatch(statusRemoveReaction(statusId, name)); + } + } + handlePin = (status) => { if (status.get('pinned')) { this.props.dispatch(unpin(status)); @@ -664,12 +692,15 @@ class Status extends ImmutablePureComponent { settings={settings} onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} + onReactionAdd={this.handleReactionAdd} + onReactionRemove={this.handleReactionRemove} expanded={isExpanded} onToggleHidden={this.handleToggleHidden} domain={domain} showMedia={this.state.showMedia} onToggleMediaVisibility={this.handleToggleMediaVisibility} usingPiP={usingPiP} + emojiMap={this.props.emojiMap} /> state.get('custom_emojis')], + items => items.reduce( + (map, emoji) => map.set(emoji.get('shortcode'), emoji), + ImmutableMap(), + ), +); +export default buildCustomEmojiMap; From 1bfdff679596e8ed9d279ca454d8c751fefe170b Mon Sep 17 00:00:00 2001 From: fef Date: Fri, 25 Nov 2022 23:12:19 +0000 Subject: [PATCH 122/166] bump to nyastodon 1.1.0-beta2 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 3b91c7c840..1f2111bcdf 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ module Mastodon end def post_suffix_version - '-1.1.0beta1' + '-1.1.0beta2' end def to_a From cf4ef1d724a7d110e4648ea76362e2a195c9bac8 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 26 Nov 2022 14:24:05 +0100 Subject: [PATCH 123/166] Try Ruby 3.1.3 (requires OpenSSL 3) --- .gitignore | 3 +++ .ruby-version | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7d76b82751..882fd8de41 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,6 @@ yarn-debug.log # Ignore Docker option files docker-compose.override.yml + +# Ignore for push +.ruby-version diff --git a/.ruby-version b/.ruby-version index b0f2dcb32f..ff365e06b9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.0.4 +3.1.3 From 21f4a310a7849ec312f47ac69f3945a85978f7ba Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 26 Nov 2022 14:37:19 +0100 Subject: [PATCH 124/166] Move to Ruby 3.0.5 --- .gitignore | 3 --- .ruby-version | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 882fd8de41..7d76b82751 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,3 @@ yarn-debug.log # Ignore Docker option files docker-compose.override.yml - -# Ignore for push -.ruby-version diff --git a/.ruby-version b/.ruby-version index ff365e06b9..eca690e737 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.3 +3.0.5 From 0897c13799d5aa1499340c4b492b9b851cb322e8 Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sat, 26 Nov 2022 14:42:36 +0100 Subject: [PATCH 125/166] v4.0.2+1.0.5 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 0435bb6b4d..4007b9030f 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -25,7 +25,7 @@ module Mastodon end def suffix_version - '+1.0.4' + '+1.0.5' end def to_a From 2d4d99f13559490831f3123e39fcbc7ece2e345a Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 28 Nov 2022 16:42:04 +0100 Subject: [PATCH 126/166] Fix expanded statuses not always being scrolled into view (#21797) --- app/javascript/mastodon/features/status/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index cb67944c94..8a63cced20 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -222,6 +222,10 @@ class Status extends ImmutablePureComponent { this.props.dispatch(fetchStatus(nextProps.params.statusId)); } + if (nextProps.params.statusId && nextProps.ancestorsIds.size > this.props.ancestorsIds.size) { + this._scrolledIntoView = false; + } + if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) { this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') }); } From 32d689a00ebdf849cb3c1ea04c09df428895d083 Mon Sep 17 00:00:00 2001 From: fef Date: Mon, 28 Nov 2022 22:23:13 +0000 Subject: [PATCH 127/166] federate emoji reactions this is kind of experimental, but it should work in theory. at least i tested it with a remove akkoma instance and it didn't crash. --- .../api/v1/statuses/reactions_controller.rb | 4 +-- app/lib/activitypub/activity.rb | 2 ++ app/lib/activitypub/activity/emoji_react.rb | 14 ++++++++ app/models/concerns/account_interactions.rb | 4 +++ app/models/status.rb | 2 +- .../activitypub/emoji_reaction_serializer.rb | 36 +++++++++++++++++++ .../undo_emoji_reaction_serializer.rb | 19 ++++++++++ .../undo_emoji_reaction_serializer.rb | 0 app/services/status_reaction_service.rb | 27 ++++++++++++++ app/services/status_unreaction_service.rb | 21 +++++++++++ 10 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 app/lib/activitypub/activity/emoji_react.rb create mode 100644 app/serializers/activitypub/emoji_reaction_serializer.rb create mode 100644 app/serializers/activitypub/undo_emoji_reaction_serializer.rb create mode 100644 app/serializers/undo_emoji_reaction_serializer.rb create mode 100644 app/services/status_reaction_service.rb create mode 100644 app/services/status_unreaction_service.rb diff --git a/app/controllers/api/v1/statuses/reactions_controller.rb b/app/controllers/api/v1/statuses/reactions_controller.rb index 9a1bf57079..f7dc2f99ce 100644 --- a/app/controllers/api/v1/statuses/reactions_controller.rb +++ b/app/controllers/api/v1/statuses/reactions_controller.rb @@ -8,12 +8,12 @@ class Api::V1::Statuses::ReactionsController < Api::BaseController before_action :set_reaction, except: :update def update - @status.status_reactions.create!(account: current_account, name: params[:id]) + StatusReactionService.new.call(current_account, @status, params[:id]) render_empty end def destroy - @reaction.destroy! + StatusUnreactionService.new.call(current_account, @status) render_empty end diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb index f4c67cccd7..a6b91f62da 100644 --- a/app/lib/activitypub/activity.rb +++ b/app/lib/activitypub/activity.rb @@ -39,6 +39,8 @@ class ActivityPub::Activity ActivityPub::Activity::Follow when 'Like' ActivityPub::Activity::Like + when 'EmojiReact' + ActivityPub::Activity::EmojiReact when 'Block' ActivityPub::Activity::Block when 'Update' diff --git a/app/lib/activitypub/activity/emoji_react.rb b/app/lib/activitypub/activity/emoji_react.rb new file mode 100644 index 0000000000..82c098f56e --- /dev/null +++ b/app/lib/activitypub/activity/emoji_react.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class ActivityPub::Activity::EmojiReact < ActivityPub::Activity + def perform + original_status = status_from_uri(object_uri) + name = @json['content'] + return if original_status.nil? || + !original_status.account.local? || + delete_arrived_first?(@json['id']) || + @account.reacted?(original_status, name) + + original_status.status_reactions.create!(account: @account, name: name) + end +end diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb index 15c49f2fec..b8dd7e4d01 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account_interactions.rb @@ -235,6 +235,10 @@ module AccountInteractions status.proper.favourites.where(account: self).exists? end + def reacted?(status, name, custom_emoji = nil) + status.proper.status_reactions.where(account: self, name: name, custom_emoji: custom_emoji).exists? + end + def bookmarked?(status) status.proper.bookmarks.where(account: self).exists? end diff --git a/app/models/status.rb b/app/models/status.rb index f8eb988c2c..95dddbcc08 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -70,7 +70,7 @@ class Status < ApplicationRecord has_many :mentions, dependent: :destroy, inverse_of: :status has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify - has_many :status_reactions, dependent: :destroy + has_many :status_reactions, inverse_of: :status, dependent: :destroy has_and_belongs_to_many :tags has_and_belongs_to_many :preview_cards diff --git a/app/serializers/activitypub/emoji_reaction_serializer.rb b/app/serializers/activitypub/emoji_reaction_serializer.rb new file mode 100644 index 0000000000..b4111150a4 --- /dev/null +++ b/app/serializers/activitypub/emoji_reaction_serializer.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ActivityPub::EmojiReactionSerializer < ActivityPub::Serializer + attributes :id, :type, :actor, :content + attribute :virtual_object, key: :object + + has_one :custom_emoji, key: :tag, serializer: ActivityPub::EmojiSerializer, unless: -> { object.custom_emoji.nil? } + + def id + [ActivityPub::TagManager.instance.uri_for(object.account), '#emoji_reactions/', object.id].join + end + + def type + 'EmojiReact' + end + + def actor + ActivityPub::TagManager.instance.uri_for(object.account) + end + + def virtual_object + ActivityPub::TagManager.instance.uri_for(object.status) + end + + def content + if object.custom_emoji.nil? + object.name + else + ":#{object.name}:" + end + end + + def reaction + content + end +end diff --git a/app/serializers/activitypub/undo_emoji_reaction_serializer.rb b/app/serializers/activitypub/undo_emoji_reaction_serializer.rb new file mode 100644 index 0000000000..49f0c1c8fd --- /dev/null +++ b/app/serializers/activitypub/undo_emoji_reaction_serializer.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class ActivityPub::UndoEmojiReactionSerializer < ActivityPub::Serializer + attributes :id, :type, :actor + + has_one :object, serializer: ActivityPub::EmojiReactionSerializer + + def id + [ActivityPub::TagManager.instance.uri_for(object.account), '#emoji_reactions/', object.id, '/undo'].join + end + + def type + 'Undo' + end + + def actor + ActivityPub::TagManager.instance.uri_for(object.account) + end +end diff --git a/app/serializers/undo_emoji_reaction_serializer.rb b/app/serializers/undo_emoji_reaction_serializer.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/services/status_reaction_service.rb b/app/services/status_reaction_service.rb new file mode 100644 index 0000000000..17acfe7488 --- /dev/null +++ b/app/services/status_reaction_service.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class StatusReactionService < BaseService + include Authorization + include Payloadable + + def call(account, status, emoji) + reaction = StatusReaction.find_by(account: account, status: status) + return reaction unless reaction.nil? + + name, domain = emoji.split("@") + + custom_emoji = CustomEmoji.find_by(shortcode: name, domain: domain) + reaction = StatusReaction.create!(account: account, status: status, name: name, custom_emoji: custom_emoji) + + json = Oj.dump(serialize_payload(reaction, ActivityPub::EmojiReactionSerializer)) + if status.account.local? + ActivityPub::RawDistributionWorker.perform_async(json, status.account.id) + else + ActivityPub::DeliveryWorker.perform_async(json, reaction.account_id, status.account.inbox_url) + end + + ActivityTracker.increment('activity:interactions') + + reaction + end +end diff --git a/app/services/status_unreaction_service.rb b/app/services/status_unreaction_service.rb new file mode 100644 index 0000000000..13c3c428db --- /dev/null +++ b/app/services/status_unreaction_service.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class StatusUnreactionService < BaseService + include Payloadable + + def call(account, status) + reaction = StatusReaction.find_by(account: account, status: status) + return if reaction.nil? + + reaction.destroy! + + json = Oj.dump(serialize_payload(reaction, ActivityPub::UndoEmojiReactionSerializer)) + if status.account.local? + ActivityPub::RawDistributionWorker.perform_async(json, status.account.id) + else + ActivityPub::DeliveryWorker.perform_async(json, reaction.account_id, status.account.inbox_url) + end + + reaction + end +end From 9c7db58e302d5337403f73a179d06bec1604fa0e Mon Sep 17 00:00:00 2001 From: fef Date: Mon, 28 Nov 2022 22:25:12 +0000 Subject: [PATCH 128/166] remove accidentally created file --- app/serializers/undo_emoji_reaction_serializer.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/serializers/undo_emoji_reaction_serializer.rb diff --git a/app/serializers/undo_emoji_reaction_serializer.rb b/app/serializers/undo_emoji_reaction_serializer.rb deleted file mode 100644 index e69de29bb2..0000000000 From 1a84936322ae10f48f9647b26e70a86a618ea430 Mon Sep 17 00:00:00 2001 From: fef Date: Mon, 28 Nov 2022 22:47:32 +0000 Subject: [PATCH 129/166] bump to nyastodon 1.1.0 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 1f2111bcdf..76a4c2121a 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -33,7 +33,7 @@ module Mastodon end def post_suffix_version - '-1.1.0beta2' + '-1.1.0' end def to_a From c7129ef7e757f6883b4c34140270380e37f9b241 Mon Sep 17 00:00:00 2001 From: fef Date: Mon, 28 Nov 2022 23:16:56 +0000 Subject: [PATCH 130/166] make status reaction count limit configurable --- .env.production.sample | 3 +++ app/validators/status_reaction_validator.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.env.production.sample b/.env.production.sample index 779d81717e..fb5832c90d 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -272,6 +272,9 @@ MAX_POLL_OPTIONS=5 # Maximum allowed poll option characters MAX_POLL_OPTION_CHARS=100 +# Maximum number of emoji reactions per toot and user (minimum 1) +MAX_STATUS_REACTIONS=8 + # Maximum image and video/audio upload sizes # Units are in bytes # 1048576 bytes equals 1 megabyte diff --git a/app/validators/status_reaction_validator.rb b/app/validators/status_reaction_validator.rb index 7c1c6983bb..113e9342ba 100644 --- a/app/validators/status_reaction_validator.rb +++ b/app/validators/status_reaction_validator.rb @@ -3,7 +3,7 @@ class StatusReactionValidator < ActiveModel::Validator SUPPORTED_EMOJIS = Oj.load_file(Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json').to_s).keys.freeze - LIMIT = 8 + LIMIT = [1, (ENV['MAX_STATUS_REACTIONS'] || 1).to_i].max def validate(reaction) return if reaction.name.blank? From cfa4824492e04ff0c60cb99f52b9b7be26fcc181 Mon Sep 17 00:00:00 2001 From: fef Date: Tue, 29 Nov 2022 00:39:40 +0000 Subject: [PATCH 131/166] make frontend fetch reaction limit the maximum number of reactions was previously hardcoded to 8. this commit also fixes an incorrect query in StatusReactionValidator where it didn't count per-user reactions but the total amount of different ones. --- .env.production.sample | 2 +- .../flavours/glitch/components/status_reactions_bar.js | 5 +++-- app/javascript/flavours/glitch/initial_state.js | 3 +++ app/serializers/initial_state_serializer.rb | 6 +++++- app/services/status_reaction_service.rb | 7 +++---- app/validators/status_reaction_validator.rb | 6 +++--- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index fb5832c90d..ac6e7f9943 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -273,7 +273,7 @@ MAX_POLL_OPTIONS=5 MAX_POLL_OPTION_CHARS=100 # Maximum number of emoji reactions per toot and user (minimum 1) -MAX_STATUS_REACTIONS=8 +MAX_REACTIONS=8 # Maximum image and video/audio upload sizes # Units are in bytes diff --git a/app/javascript/flavours/glitch/components/status_reactions_bar.js b/app/javascript/flavours/glitch/components/status_reactions_bar.js index db1905be4f..ac57341bcc 100644 --- a/app/javascript/flavours/glitch/components/status_reactions_bar.js +++ b/app/javascript/flavours/glitch/components/status_reactions_bar.js @@ -11,13 +11,14 @@ import React from 'react'; import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light'; import AnimatedNumber from './animated_number'; import { assetHost } from '../utils/config'; -import { autoPlayGif } from '../initial_state'; +import { autoPlayGif, maxReactions } from '../initial_state'; export default class StatusReactionsBar extends ImmutablePureComponent { static propTypes = { statusId: PropTypes.string.isRequired, reactions: ImmutablePropTypes.list.isRequired, + reactionLimit: PropTypes.number.isRequired, addReaction: PropTypes.func.isRequired, removeReaction: PropTypes.func.isRequired, emojiMap: ImmutablePropTypes.map.isRequired, @@ -62,7 +63,7 @@ export default class StatusReactionsBar extends ImmutablePureComponent { /> ))} - {visibleReactions.size < 8 && } />} + {visibleReactions.size < maxReactions && } />}
)} diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 5be177cedf..f093645561 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -146,4 +146,7 @@ export const pollLimits = (initialState && initialState.poll_limits); export const defaultContentType = getMeta('default_content_type'); export const useSystemEmojiFont = getMeta('system_emoji_font'); +// nyastodon-specific settings +export const maxReactions = (initialState && initialState.max_reactions) || 8; + export default initialState; diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 1fd44464f6..e5d638441d 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -6,7 +6,7 @@ class InitialStateSerializer < ActiveModel::Serializer attributes :meta, :compose, :accounts, :media_attachments, :settings, :max_toot_chars, :poll_limits, - :languages + :languages, :max_reactions has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer has_one :role, serializer: REST::RoleSerializer @@ -15,6 +15,10 @@ class InitialStateSerializer < ActiveModel::Serializer StatusLengthValidator::MAX_CHARS end + def max_reactions + StatusReactionValidator::LIMIT + end + def poll_limits { min_options: PollValidator::MIN_OPTIONS, diff --git a/app/services/status_reaction_service.rb b/app/services/status_reaction_service.rb index 17acfe7488..e823f6bd88 100644 --- a/app/services/status_reaction_service.rb +++ b/app/services/status_reaction_service.rb @@ -5,12 +5,11 @@ class StatusReactionService < BaseService include Payloadable def call(account, status, emoji) - reaction = StatusReaction.find_by(account: account, status: status) + name, domain = emoji.split('@') + custom_emoji = CustomEmoji.find_by(shortcode: name, domain: domain) + reaction = StatusReaction.find_by(account: account, status: status, name: name, custom_emoji: custom_emoji) return reaction unless reaction.nil? - name, domain = emoji.split("@") - - custom_emoji = CustomEmoji.find_by(shortcode: name, domain: domain) reaction = StatusReaction.create!(account: account, status: status, name: name, custom_emoji: custom_emoji) json = Oj.dump(serialize_payload(reaction, ActivityPub::EmojiReactionSerializer)) diff --git a/app/validators/status_reaction_validator.rb b/app/validators/status_reaction_validator.rb index 113e9342ba..fa6fb2e765 100644 --- a/app/validators/status_reaction_validator.rb +++ b/app/validators/status_reaction_validator.rb @@ -3,13 +3,13 @@ class StatusReactionValidator < ActiveModel::Validator SUPPORTED_EMOJIS = Oj.load_file(Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json').to_s).keys.freeze - LIMIT = [1, (ENV['MAX_STATUS_REACTIONS'] || 1).to_i].max + LIMIT = [1, (ENV['MAX_REACTIONS'] || 8).to_i].max def validate(reaction) return if reaction.name.blank? reaction.errors.add(:name, I18n.t('reactions.errors.unrecognized_emoji')) if reaction.custom_emoji_id.blank? && !unicode_emoji?(reaction.name) - reaction.errors.add(:base, I18n.t('reactions.errors.limit_reached')) if new_reaction?(reaction) && limit_reached?(reaction) + reaction.errors.add(:base, I18n.t('reactions.errors.limit_reached')) if limit_reached?(reaction) end private @@ -23,6 +23,6 @@ class StatusReactionValidator < ActiveModel::Validator end def limit_reached?(reaction) - reaction.status.status_reactions.where.not(name: reaction.name).count('distinct name') >= LIMIT + reaction.status.status_reactions.where(status: reaction.status, account: reaction.account).count >= LIMIT end end From d6e3a9f17f2ad4ce2198c30c23eb2f8671aba23d Mon Sep 17 00:00:00 2001 From: fef Date: Tue, 29 Nov 2022 03:31:22 +0000 Subject: [PATCH 132/166] send reaction notifications --- .../flavours/glitch/actions/notifications.js | 1 + .../flavours/glitch/components/status.js | 1 + .../glitch/components/status_prepend.js | 11 ++++++ .../glitch/components/status_reactions_bar.js | 1 - .../components/column_settings.js | 11 ++++++ .../notifications/components/filter_bar.js | 8 ++++ .../notifications/components/notification.js | 22 +++++++++++ app/javascript/flavours/glitch/locales/de.js | 5 +++ app/javascript/flavours/glitch/locales/en.js | 4 ++ app/javascript/flavours/glitch/locales/fr.js | 5 ++- .../flavours/glitch/reducers/settings.js | 3 ++ app/models/notification.rb | 38 ++++++++++++------- .../rest/notification_serializer.rb | 2 +- app/services/status_reaction_service.rb | 1 + config/locales/de.yml | 4 ++ config/locales/en.yml | 4 ++ config/locales/en_GB.yml | 4 ++ config/locales/fr.yml | 4 ++ 18 files changed, 112 insertions(+), 17 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index 158a5b7e43..fa556269c8 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -139,6 +139,7 @@ const excludeTypesFromFilter = filter => { 'follow', 'follow_request', 'favourite', + 'reaction', 'reblog', 'mention', 'poll', diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 5207ece21f..a757f4842c 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -721,6 +721,7 @@ class Status extends ImmutablePureComponent { if (this.props.prepend && account) { const notifKind = { favourite: 'favourited', + reaction: 'reacted', reblog: 'boosted', reblogged_by: 'boosted', status: 'posted', diff --git a/app/javascript/flavours/glitch/components/status_prepend.js b/app/javascript/flavours/glitch/components/status_prepend.js index f825330621..6c96f2ee2f 100644 --- a/app/javascript/flavours/glitch/components/status_prepend.js +++ b/app/javascript/flavours/glitch/components/status_prepend.js @@ -56,6 +56,14 @@ export default class StatusPrepend extends React.PureComponent { values={{ name : link }} /> ); + case 'reaction': + return ( + + ); case 'reblog': return ( +
+ + +
+ + {showPushSettings && } + + +
+
+
diff --git a/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js b/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js index c1de0f90ea..6027a55d85 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js +++ b/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js @@ -6,6 +6,7 @@ import Icon from 'flavours/glitch/components/icon'; const tooltips = defineMessages({ mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' }, favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' }, + reactions: { id: 'notifications.filter.reactions', defaultMessage: 'Reactions' }, boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' }, polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' }, follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' }, @@ -74,6 +75,13 @@ class FilterBar extends React.PureComponent { > + + ); + if (status.get('spoiler_text').length > 0) { let mentionsPlaceholder = ''; @@ -355,6 +371,7 @@ export default class StatusContent extends React.PureComponent { {extraMedia} + {!hidden && renderTranslate && translateButton}
); } else if (parseClick) { @@ -377,6 +394,7 @@ export default class StatusContent extends React.PureComponent { /> {media} {extraMedia} + {!hidden && renderTranslate && translateButton} ); } else { @@ -397,6 +415,7 @@ export default class StatusContent extends React.PureComponent { /> {media} {extraMedia} + {!hidden && renderTranslate && translateButton} ); } diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index c12b2e6143..947573fc7d 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -23,7 +23,9 @@ import { deleteStatus, hideStatus, revealStatus, - editStatus + editStatus, + translateStatus, + undoStatusTranslation, } from 'flavours/glitch/actions/statuses'; import { initAddFilter, @@ -187,6 +189,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ dispatch(editStatus(status.get('id'), history)); }, + onTranslate (status) { + if (status.get('translation')) { + dispatch(undoStatusTranslation(status.get('id'))); + } else { + dispatch(translateStatus(status.get('id'))); + } + }, + onDirect (account, router) { dispatch(directCompose(account, router)); }, diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index 46770930f5..7d2c2aace3 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -34,6 +34,7 @@ class DetailedStatus extends ImmutablePureComponent { onOpenMedia: PropTypes.func.isRequired, onOpenVideo: PropTypes.func.isRequired, onToggleHidden: PropTypes.func, + onTranslate: PropTypes.func.isRequired, expanded: PropTypes.bool, measureHeight: PropTypes.bool, onHeightChange: PropTypes.func, @@ -112,6 +113,11 @@ class DetailedStatus extends ImmutablePureComponent { window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes'); } + handleTranslate = () => { + const { onTranslate, status } = this.props; + onTranslate(status); + } + render () { const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props; @@ -305,6 +311,7 @@ class DetailedStatus extends ImmutablePureComponent { expanded={expanded} collapsed={false} onExpandedToggle={onToggleHidden} + onTranslate={this.handleTranslate} parseClick={this.parseClick} onUpdate={this.handleChildUpdate} tagLinks={settings.get('tag_misleading_links')} diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index aaa9c7928f..e190652b06 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -33,7 +33,9 @@ import { deleteStatus, editStatus, hideStatus, - revealStatus + revealStatus, + translateStatus, + undoStatusTranslation, } from 'flavours/glitch/actions/statuses'; import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initBlockModal } from 'flavours/glitch/actions/blocks'; @@ -437,6 +439,16 @@ class Status extends ImmutablePureComponent { this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded }); } + handleTranslate = status => { + const { dispatch } = this.props; + + if (status.get('translation')) { + dispatch(undoStatusTranslation(status.get('id'))); + } else { + dispatch(translateStatus(status.get('id'))); + } + } + handleBlockClick = (status) => { const { dispatch } = this.props; const account = status.get('account'); @@ -666,6 +678,7 @@ class Status extends ImmutablePureComponent { onOpenMedia={this.handleOpenMedia} expanded={isExpanded} onToggleHidden={this.handleToggleHidden} + onTranslate={this.handleTranslate} domain={domain} showMedia={this.state.showMedia} onToggleMediaVisibility={this.handleToggleMediaVisibility} diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js index b47155c5f6..f0c4c804b7 100644 --- a/app/javascript/flavours/glitch/reducers/statuses.js +++ b/app/javascript/flavours/glitch/reducers/statuses.js @@ -13,6 +13,8 @@ import { STATUS_REVEAL, STATUS_HIDE, STATUS_COLLAPSE, + STATUS_TRANSLATE_SUCCESS, + STATUS_TRANSLATE_UNDO, STATUS_FETCH_REQUEST, STATUS_FETCH_FAIL, } from 'flavours/glitch/actions/statuses'; @@ -85,6 +87,10 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'collapsed'], action.isCollapsed); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.references); + case STATUS_TRANSLATE_SUCCESS: + return state.setIn([action.id, 'translation'], fromJS(action.translation)); + case STATUS_TRANSLATE_UNDO: + return state.deleteIn([action.id, 'translation']); default: return state; } From b5ee275ed978d307b87ced8a0b98f7efcaf5a070 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 28 Sep 2022 01:02:01 +0200 Subject: [PATCH 148/166] [Glitch] Fix translations not being formatted, other issues in web UI Port 55a2e9b5beb1fc923c42257edee3df738e208b38 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/components/status_content.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 63d017a386..a4ad5d1c8c 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -5,7 +5,7 @@ import { FormattedMessage, injectIntl } from 'react-intl'; import Permalink from './permalink'; import classnames from 'classnames'; import Icon from 'flavours/glitch/components/icon'; -import { autoPlayGif } from 'flavours/glitch/initial_state'; +import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; const textMatchesTarget = (text, origin, host) => { @@ -274,8 +274,9 @@ class StatusContent extends React.PureComponent { } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; - const renderTranslate = this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && intl.locale !== status.get('language'); - const languageNames = new Intl.DisplayNames([intl.locale], { type: 'language' }); + const renderTranslate = this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && intl.locale !== status.get('language'); + const language = preloadedLanguages.find(lang => lang[0] === status.get('language')); + const languageName = language ? language[2] : status.get('language'); const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') }; @@ -287,7 +288,7 @@ class StatusContent extends React.PureComponent { const translateButton = ( ); From 5e143db13aeffc5f0353ed7226b8f0df13763868 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 29 Sep 2022 06:21:51 +0200 Subject: [PATCH 149/166] [Glitch] Don't show translate button to logged-out users Port part of e623c302d5d4dfc05689eb8fb8e051e30fc38ec8 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status_content.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index a4ad5d1c8c..cadcf5c11c 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -65,6 +65,10 @@ const isLinkMisleading = (link) => { export default @injectIntl class StatusContent extends React.PureComponent { + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { status: ImmutablePropTypes.map.isRequired, expanded: PropTypes.bool, @@ -274,7 +278,7 @@ class StatusContent extends React.PureComponent { } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; - const renderTranslate = this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && intl.locale !== status.get('language'); + const renderTranslate = this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && intl.locale !== status.get('language'); const language = preloadedLanguages.find(lang => lang[0] === status.get('language')); const languageName = language ? language[2] : status.get('language'); From e8d0a1b320713eda76f303b6e840d004f1890d27 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 18 Oct 2022 21:21:20 +0200 Subject: [PATCH 150/166] [Glitch] Fix showing translate button when status has no language in web UI Port 4adb267f9177f6036f8f27cd37544c54b97f3dd2 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status_content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index cadcf5c11c..7c13c7ee48 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -278,7 +278,7 @@ class StatusContent extends React.PureComponent { } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; - const renderTranslate = this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && intl.locale !== status.get('language'); + const renderTranslate = this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language'); const language = preloadedLanguages.find(lang => lang[0] === status.get('language')); const languageName = language ? language[2] : status.get('language'); From 47bd934061c8f6686bb478d5ea1eaa131bd1d1f9 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 24 Oct 2022 18:30:58 +0200 Subject: [PATCH 151/166] =?UTF-8?q?[Glitch]=20Change=20=E2=80=9CTranslate?= =?UTF-8?q?=E2=80=9D=20button=20to=20only=20show=20up=20when=20a=20transla?= =?UTF-8?q?tion=20backend=20is=20configured?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port 8046cf34d68209b39845e07a9d2db40926cc5512 to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status_content.js | 4 ++-- app/javascript/flavours/glitch/initial_state.js | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 7c13c7ee48..a510cd24d8 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -5,7 +5,7 @@ import { FormattedMessage, injectIntl } from 'react-intl'; import Permalink from './permalink'; import classnames from 'classnames'; import Icon from 'flavours/glitch/components/icon'; -import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; +import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; const textMatchesTarget = (text, origin, host) => { @@ -278,7 +278,7 @@ class StatusContent extends React.PureComponent { } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; - const renderTranslate = this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language'); + const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language'); const language = preloadedLanguages.find(lang => lang[0] === status.get('language')); const languageName = language ? language[2] : status.get('language'); diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 5be177cedf..bbf25c8a85 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -79,6 +79,7 @@ * @property {boolean} use_blurhash * @property {boolean=} use_pending_items * @property {string} version + * @property {boolean} translation_enabled * @property {object} local_settings */ @@ -137,6 +138,7 @@ export const unfollowModal = getMeta('unfollow_modal'); export const useBlurhash = getMeta('use_blurhash'); export const usePendingItems = getMeta('use_pending_items'); export const version = getMeta('version'); +export const translationEnabled = getMeta('translation_enabled'); export const languages = initialState?.languages; // Glitch-soc-specific settings From 44c0ba445a61dfbe65d9d1056933c6a8d190c907 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 24 Oct 2022 18:37:57 +0200 Subject: [PATCH 152/166] [Glitch] Add mention of the translation provider when translating a post Port 30453fab80d55fc10766f0e067c31d96753ccfda to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/components/status_content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index a510cd24d8..9244b6f27f 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -292,7 +292,7 @@ class StatusContent extends React.PureComponent { const translateButton = ( ); From f7684a31fe6ea1f069e3885783ab25b1322e97f6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 25 Oct 2022 18:47:21 +0200 Subject: [PATCH 153/166] [Glitch] Change design of translations in web UI Port fcca781aae609067bc9e43ad4a466ef6d2074bbb to glitch-soc Signed-off-by: Claire --- .../glitch/components/status_content.js | 51 +++++++++++++++---- .../glitch/styles/components/index.scss | 2 +- .../glitch/styles/components/status.scss | 10 ++-- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 9244b6f27f..9ed9fbf9bd 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -62,6 +62,43 @@ const isLinkMisleading = (link) => { return !(textMatchesTarget(text, origin, host) || textMatchesTarget(text.toLowerCase(), origin, host)); }; +class TranslateButton extends React.PureComponent { + + static propTypes = { + translation: ImmutablePropTypes.map, + onClick: PropTypes.func, + }; + + render () { + const { translation, onClick } = this.props; + + if (translation) { + const language = preloadedLanguages.find(lang => lang[0] === translation.get('detected_source_language')); + const languageName = language ? language[2] : translation.get('detected_source_language'); + const provider = translation.get('provider'); + + return ( +
+
+ +
+ + +
+ ); + } + + return ( + + ); + } + +} + export default @injectIntl class StatusContent extends React.PureComponent { @@ -279,8 +316,6 @@ class StatusContent extends React.PureComponent { const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language'); - const language = preloadedLanguages.find(lang => lang[0] === status.get('language')); - const languageName = language ? language[2] : status.get('language'); const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') }; @@ -290,10 +325,8 @@ class StatusContent extends React.PureComponent { 'status__content--with-spoiler': status.get('spoiler_text').length > 0, }); - const translateButton = ( - + const translateButton = renderTranslate && ( + ); if (status.get('spoiler_text').length > 0) { @@ -376,7 +409,7 @@ class StatusContent extends React.PureComponent { {extraMedia} - {!hidden && renderTranslate && translateButton} + {!hidden && translateButton} ); } else if (parseClick) { @@ -399,7 +432,7 @@ class StatusContent extends React.PureComponent { /> {media} {extraMedia} - {!hidden && renderTranslate && translateButton} + {!hidden && translateButton} ); } else { @@ -420,7 +453,7 @@ class StatusContent extends React.PureComponent { /> {media} {extraMedia} - {!hidden && renderTranslate && translateButton} + {!hidden && translateButton} ); } diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 80b0598a50..84aca2ebc0 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -15,7 +15,7 @@ display: block; font-size: 15px; line-height: 20px; - color: $ui-highlight-color; + color: $highlight-text-color; border: 0; background: transparent; padding: 0; diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index 054110e410..7cc6f5386b 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -206,15 +206,13 @@ } } -.status__content__edited-label { - display: block; - cursor: default; +.translate-button { + margin-top: 16px; font-size: 15px; line-height: 20px; - padding: 0; - padding-top: 8px; + display: flex; + justify-content: space-between; color: $dark-text-color; - font-weight: 500; } .status__content__spoiler-link { From 620b079a7827173c493d9ba4c2bd31001bdbde5b Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Nov 2022 13:52:43 +0100 Subject: [PATCH 154/166] =?UTF-8?q?Fix=20inconsistent=20with=20=E2=80=9Ctr?= =?UTF-8?q?anslate=E2=80=9D=20button=20padding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/flavours/glitch/styles/statuses.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/styles/statuses.scss b/app/javascript/flavours/glitch/styles/statuses.scss index 947a5d3aed..88fa3ffa07 100644 --- a/app/javascript/flavours/glitch/styles/statuses.scss +++ b/app/javascript/flavours/glitch/styles/statuses.scss @@ -268,7 +268,7 @@ a.button.logo-button { border: 0; background: transparent; padding: 0; - padding-top: 8px; + padding-top: 16px; text-decoration: none; &:hover, From 96cd869dad22e563fcc5fca12d4c9c76b54a12ec Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 30 Nov 2022 13:20:20 +0000 Subject: [PATCH 155/166] make number of displayed reactions a setting This adds an extra item to the local settings for specifying the number of reactions shown in toots. The detailed status view always shows all reactions. --- .env.production.sample | 2 +- .../flavours/glitch/components/status.js | 1 + .../glitch/components/status_reactions.js | 23 +++++++++++-------- .../features/local_settings/page/index.js | 12 ++++++++++ .../local_settings/page/item/index.js | 11 +++++---- app/javascript/flavours/glitch/locales/de.js | 3 +++ app/javascript/flavours/glitch/locales/en.js | 2 ++ app/javascript/flavours/glitch/locales/fr.js | 3 +++ .../glitch/reducers/local_settings.js | 1 + 9 files changed, 42 insertions(+), 16 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index ac6e7f9943..8ecca9d604 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -273,7 +273,7 @@ MAX_POLL_OPTIONS=5 MAX_POLL_OPTION_CHARS=100 # Maximum number of emoji reactions per toot and user (minimum 1) -MAX_REACTIONS=8 +MAX_REACTIONS=1 # Maximum image and video/audio upload sizes # Units are in bytes diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 65631792b9..d496d6738b 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -802,6 +802,7 @@ class Status extends ImmutablePureComponent { { - const { addReaction, statusId } = this.props; - addReaction(statusId, data.native.replace(/:/g, '')); - } - willEnter() { return { scale: reduceMotion ? 1 : 0 }; } @@ -35,11 +31,18 @@ export default class StatusReactions extends ImmutablePureComponent { } render() { - const { reactions } = this.props; - const visibleReactions = reactions + const { reactions, numVisible } = this.props; + let visibleReactions = reactions .filter(x => x.get('count') > 0) - .sort((a, b) => b.get('count') - a.get('count')) - .filter((_, i) => i < maxReactions); + .sort((a, b) => b.get('count') - a.get('count')); + + // numVisible might be NaN because it's pulled from local settings + // which doesn't do a whole lot of input validation, but that's okay + // because NaN >= 0 evaluates false. + // Still, this should be improved at some point. + if (numVisible >= 0) { + visibleReactions = visibleReactions.filter((_, i) => i < numVisible); + } const styles = visibleReactions.map(reaction => ({ key: reaction.get('name'), diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js index d01eec811e..e88e50b674 100644 --- a/app/javascript/flavours/glitch/features/local_settings/page/index.js +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -29,6 +29,8 @@ const messages = defineMessages({ rewrite_mentions_username: { id: 'settings.rewrite_mentions_username', defaultMessage: 'Rewrite with username' }, pop_in_left: { id: 'settings.pop_in_left', defaultMessage: 'Left' }, pop_in_right: { id: 'settings.pop_in_right', defaultMessage: 'Right' }, + visible_reactions_count: { id: 'settings.visible_reactions_count', defaultMessage: 'Number of visible reactions' }, + enter_amount_prompt: { id: 'settings.enter_amount_prompt', defaultMessage: 'Enter an amount' }, }); export default @injectIntl @@ -92,6 +94,16 @@ class LocalSettingsPage extends React.PureComponent { > + + +

{ const { target } = e; - const { item, onChange, options, placeholder } = this.props; + const { item, onChange, options, placeholder, number } = this.props; if (options && options.length > 0) onChange(item, target.value); - else if (placeholder) onChange(item, target.value); + else if (placeholder) onChange(item, number ? parseInt(target.value) : target.value); else onChange(item, target.checked); } render () { const { handleChange } = this; - const { settings, item, id, options, children, dependsOn, dependsOnNot, placeholder, disabled } = this.props; + const { settings, item, id, options, children, dependsOn, dependsOnNot, placeholder, number, disabled } = this.props; let enabled = !disabled; if (dependsOn) { @@ -76,7 +77,7 @@ export default class LocalSettingsPageItem extends React.PureComponent { ); - } else if (placeholder) { + } else if (placeholder || number) { return (
); } else { @@ -453,7 +453,7 @@ class StatusContent extends React.PureComponent { /> {media} {extraMedia} - {!hidden && translateButton} + {translateButton} ); } From e64909d8bfe084565595c4a277af837c57b65efa Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 30 Nov 2022 15:03:47 +0100 Subject: [PATCH 157/166] Move translate button above media attachments/preview cards --- .../flavours/glitch/components/status_content.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 2c59c054f3..c59f42220c 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -404,12 +404,11 @@ class StatusContent extends React.PureComponent { onMouseLeave={this.handleMouseLeave} lang={lang} /> + {!hidden && translateButton} {media} {extraMedia} - - {!hidden && translateButton} ); } else if (parseClick) { @@ -430,9 +429,9 @@ class StatusContent extends React.PureComponent { onMouseLeave={this.handleMouseLeave} lang={lang} /> + {translateButton} {media} {extraMedia} - {translateButton} ); } else { @@ -451,9 +450,9 @@ class StatusContent extends React.PureComponent { onMouseLeave={this.handleMouseLeave} lang={lang} /> + {translateButton} {media} {extraMedia} - {translateButton} ); } From 4571ee9da6a70ddb0978eed4cdfd0f543a107d23 Mon Sep 17 00:00:00 2001 From: fef Date: Wed, 30 Nov 2022 14:59:37 +0000 Subject: [PATCH 158/166] make number of visible reactions a vanilla setting Reactions will be backported to the vanilla flavour, which requires all related settings to be accessible from the vanilla settings page rather than the glitch specific settings modal. --- app/controllers/settings/preferences_controller.rb | 1 + app/javascript/flavours/glitch/components/status.js | 4 ++-- .../glitch/features/local_settings/page/index.js | 11 ----------- app/javascript/flavours/glitch/initial_state.js | 5 ++--- app/javascript/flavours/glitch/locales/de.js | 3 --- app/javascript/flavours/glitch/locales/en.js | 2 -- app/javascript/flavours/glitch/locales/fr.js | 3 --- .../flavours/glitch/reducers/local_settings.js | 1 - app/lib/user_settings_decorator.rb | 9 +++++++++ app/models/user.rb | 2 +- app/serializers/initial_state_serializer.rb | 1 + .../settings/preferences/appearance/show.html.haml | 3 +++ config/locales/simple_form.de.yml | 1 + config/locales/simple_form.en.yml | 1 + config/locales/simple_form.fr.yml | 1 + config/settings.yml | 1 + 16 files changed, 23 insertions(+), 26 deletions(-) diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 4c13364369..39715b724b 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -57,6 +57,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_use_pending_items, :setting_trends, :setting_crop_images, + :setting_visible_reactions, :setting_always_send_emails, notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag trending_link trending_status appeal), interactions: %i(must_be_follower must_be_following must_be_following_dm) diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index d496d6738b..95563503b2 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -17,7 +17,7 @@ import NotificationOverlayContainer from 'flavours/glitch/features/notifications import classNames from 'classnames'; import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning'; import PollContainer from 'flavours/glitch/containers/poll_container'; -import { displayMedia } from 'flavours/glitch/initial_state'; +import { displayMedia, visibleReactions } from 'flavours/glitch/initial_state'; import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder'; // We use the component (and not the container) since we do not want @@ -802,7 +802,7 @@ class Status extends ImmutablePureComponent {
- - -

diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.js b/app/javascript/mastodon/features/notifications/components/filter_bar.js index 368eb0b7e6..2b5f48f0b4 100644 --- a/app/javascript/mastodon/features/notifications/components/filter_bar.js +++ b/app/javascript/mastodon/features/notifications/components/filter_bar.js @@ -6,6 +6,7 @@ import Icon from 'mastodon/components/icon'; const tooltips = defineMessages({ mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' }, favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' }, + reactions: { id: 'notifications.filter.reactions', defaultMessage: 'Reactions' }, boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' }, polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' }, follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' }, @@ -74,6 +75,13 @@ class FilterBar extends React.PureComponent { > +