Compare commits

..

460 Commits

Author SHA1 Message Date
Claire
cd71fdcdff Merge pull request #3311 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 9d81561bb2
2025-12-10 11:53:00 +01:00
Claire
5422e43e31 Fix wrapstodon standalone page not loading JS module 2025-12-10 09:49:09 +01:00
diondiondion
5a66331003 [Glitch] Update Wrapstodon design
Port 9d81561bb2 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-09 18:27:14 +01:00
Matt Jankowski
09e3955145 [Glitch] Fix misc comment typos
Port ac71771d98 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-09 18:10:04 +01:00
Echo
e554e5723d [Glitch] Fix emoji on Wrapstodon
Port 9702cbb41c to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-09 18:09:22 +01:00
Claire
315f5e5a31 Merge commit '9d81561bb2440c8fb9a75bd05277120aff346b1e' into glitch-soc/merge-upstream 2025-12-09 18:06:56 +01:00
diondiondion
9d81561bb2 Update Wrapstodon design (#37169) 2025-12-09 16:51:05 +00:00
Matt Jankowski
ac71771d98 Fix misc comment typos (#37183) 2025-12-09 16:09:01 +00:00
Claire
697569e5f9 Add account_id attribute to AnnualReport entity (#37182) 2025-12-09 15:59:40 +00:00
Claire
4cdcdaa7d9 Fix streaming image build after removal of .yarn (#37181) 2025-12-09 15:36:46 +00:00
Echo
9702cbb41c Fix emoji on Wrapstodon (#37177) 2025-12-09 10:36:08 +00:00
David Roetzel
ea768c17db Add counter cache to collections (#37176) 2025-12-09 10:31:35 +00:00
renovate[bot]
5347cabf3e Update dependency oj to v3.16.13 (#37135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 10:07:35 +00:00
renovate[bot]
eef40ba96b Update dependency hiredis-client to v0.26.2 (#37137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 09:47:12 +00:00
Matt Jankowski
9063c3b660 Remove yarn patch for babel-plugin-lodash, removed during Vite upgrade (#37161) 2025-12-09 09:30:57 +00:00
Matt Jankowski
e147947eb8 Add wrapstodon page spec (#37168) 2025-12-09 09:27:29 +00:00
Claire
8c52889c86 Merge pull request #3307 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 607449336d
2025-12-08 17:14:29 +01:00
Claire
05e45beb34 Merge commit '607449336da198ea9fe9c014220a5374a0ca1ae4' into glitch-soc/merge-upstream 2025-12-08 16:33:27 +01:00
Claire
607449336d Merge commit from fork 2025-12-08 15:44:08 +01:00
github-actions[bot]
85bf5be604 New Crowdin Translations (automated) (#37146)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-08 11:42:24 +00:00
David Roetzel
cf23f0414f Add id to collection serializers (#37157) 2025-12-08 11:40:17 +00:00
David Roetzel
55becaa1b5 Preload tag to prevent n+1 (#37154) 2025-12-08 10:30:10 +00:00
David Roetzel
8625721805 Draft API to get all collections by an account (#37139) 2025-12-08 08:56:13 +00:00
Claire
cd34331842 Merge pull request #3306 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 1ae3b4672b
2025-12-06 11:35:37 +01:00
Claire
691f4e3210 [Glitch] Fix “Delete and Redraft” on a non-quote being treated as a quote post in some cases
Port 1ae3b4672b to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 20:08:05 +01:00
diondiondion
7f1862b67e [Glitch] Increase maximum height of media items on desktop
Port ce22c835ac to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 20:07:28 +01:00
Claire
71cda79e3b [Glitch] Remove more unused data from 2025 annual reports
Port 9b851616fe to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 20:06:12 +01:00
diondiondion
c3f254f170 [Glitch] Fix inversion of emoji colours based on dark/light mode
Port 591776d7ad to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 20:05:32 +01:00
Echo
05691293f0 [Glitch] Fixes YouTube embeds
Port 46f3b39fae to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 20:01:52 +01:00
Claire
2f6cdd6d47 Merge commit '1ae3b4672bffbc4aaf26f60c7063d6901239de5d' into glitch-soc/merge-upstream 2025-12-05 20:00:40 +01:00
Claire
4625bbe282 Merge pull request #3305 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstrema changes up to 65b216353e
2025-12-05 19:58:48 +01:00
diondiondion
4dc196b595 [Glitch] Handle dark/light/contrast theme modes in common CSS
Port 65b216353e to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-05 19:29:37 +01:00
Claire
3b4c4c5b09 Merge commit '65b216353e9bc9ae8517d967f8c2cd8122551fdc' into glitch-soc/merge-upstream
- `app/helpers/application_helper.rb`:
  Upstream updated the classes used for theming purposes,
  but glitch-soc has its own theming system.
  Adapted upstream's changes.
- `spec/helpers/application_helper_spec.rb`:
  Upstream updated the classes used for theming purposes,
  but glitch-soc has its own theming system.
  Adapted upstream's changes.
2025-12-05 19:25:40 +01:00
Matt Jankowski
7fe3e80758 Rely on locale for options order in DOB input (#36895) 2025-12-05 15:21:22 +00:00
Claire
1ae3b4672b Fix “Delete and Redraft” on a non-quote being treated as a quote post in some cases (#37140) 2025-12-05 15:17:48 +00:00
Echo
007ae588d8 Fix incorrect date for Wrapstodon start (#37138) 2025-12-05 14:12:26 +00:00
diondiondion
ce22c835ac Increase maximum height of media items on desktop (#37136) 2025-12-05 13:39:48 +00:00
Claire
9b851616fe Remove more unused data from 2025 annual reports (#37134) 2025-12-05 13:36:37 +00:00
diondiondion
591776d7ad Fix inversion of emoji colours based on dark/light mode (#37120) 2025-12-05 10:08:59 +00:00
renovate[bot]
7f1f3236c6 Update opentelemetry-ruby (non-major) (#37101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:57:18 +00:00
renovate[bot]
852727a226 Update dependency aws-sdk-s3 to v1.206.0 (#37100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:57:02 +00:00
github-actions[bot]
429d6bcab4 New Crowdin Translations (automated) (#37131)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-05 09:43:12 +00:00
renovate[bot]
e47a5dd1c2 Update dependency faker to v3.5.3 (#37099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:22:32 +00:00
renovate[bot]
4ec761debd Update dependency sidekiq to v8.0.10 (#37090)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:20:54 +00:00
renovate[bot]
d895ea3433 Update dependency vite to v7.2.6 (#37076)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:20:45 +00:00
renovate[bot]
49105a28a3 Update eslint (non-major) (#36801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:20:34 +00:00
renovate[bot]
1cb650d107 Update dependency jsdom to v27.2.0 (#36845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 09:20:17 +00:00
Claire
0f2ba97c99 Merge pull request #3304 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 0dac31dfd5
2025-12-04 21:42:55 +01:00
Echo
0061f9a699 [Glitch] Create new entrypoint for sharable Wrapstodon
Port 0dac31dfd5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 21:17:30 +01:00
Echo
2d93e63e43 [Glitch] Wrapstodon modal with new share button
Port 31c392b1bc to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 21:17:30 +01:00
diondiondion
5a5ba02f96 [Glitch] Fix color contrast issues caused by new theme tokens
Port 498e88f059 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 21:01:08 +01:00
Claire
c8f365fd1d Merge commit '0dac31dfd588e4cd866f382ed001a9535f06234a' into glitch-soc/merge-upstream 2025-12-04 20:59:54 +01:00
Claire
691fe7cb4c Merge pull request #3303 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to b3b5bf26d1
2025-12-04 20:58:43 +01:00
Echo
376332bfe7 [Glitch] Display Wrapstodon inline widget
Port e5e3a64a9b to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:27:02 +01:00
Echo
8bec8c373b [Glitch] Remove noreferrer from external links
Port 234990cc37 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:25:52 +01:00
diondiondion
5f2d64c4b0 [Glitch] Add Wrapstodon timeline announcement component
Port 9aec6936e5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:21:09 +01:00
Echo
1faaa9706a [Glitch] Add UJS to buttons
Port 100b20f290 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:20:29 +01:00
Echo
ac926baa74 [Glitch] Emoji: Update emoji categories with featured emoji
Port c5c8100d02 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:20:06 +01:00
diondiondion
f21f8df4cc [Glitch] Ensure all pages have a solid background color
Port f5d6f60ca7 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:19:20 +01:00
Claire
62dc7c1ee6 [Glitch] Fix error handling when re-fetching already-known statuses
Port edfbcfb3f5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:18:59 +01:00
diondiondion
c63393c963 [Glitch] Don't reset scroll when using hotkeys to focus columns, add hotkey 0 to scroll to top
Port 9334bd9ede to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-04 20:18:28 +01:00
Claire
34aa825e96 Merge commit 'b3b5bf26d14f809a562244642949826b6a91bade' into glitch-soc/merge-upstream 2025-12-04 20:16:21 +01:00
Echo
46f3b39fae Fixes YouTube embeds (#37126) 2025-12-04 16:27:20 +00:00
diondiondion
65b216353e Handle dark/light/contrast theme modes in common CSS (#37095) 2025-12-04 15:56:35 +00:00
Echo
0dac31dfd5 Create new entrypoint for sharable Wrapstodon (#37121) 2025-12-04 15:48:10 +00:00
David Roetzel
75b9e9a8b0 Enable missing before actions in Collection API (#37122) 2025-12-04 15:35:47 +00:00
Claire
88aed3c11a Fix streamed quoted polls not being hydrated correctly (#37118) 2025-12-04 14:10:48 +00:00
David Roetzel
9921fa1af7 First draft API to delete collections (#37117) 2025-12-04 13:47:23 +00:00
David Roetzel
5a7a4fff11 First draft of Collection update API (#37110) 2025-12-04 10:00:21 +00:00
github-actions[bot]
9cf52fb976 New Crowdin Translations (automated) (#37115)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-04 09:19:08 +00:00
renovate[bot]
baef5b1659 Update dependency @optimize-lodash/rollup-plugin to v6 (#37062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 09:16:13 +00:00
Matt Jankowski
832d8c7397 Use thread support helper in concurrent insert tag spec (#37112) 2025-12-04 09:11:53 +00:00
Matt Jankowski
d063af2594 Add coverage for array vs string in Tag.find_or_create_by (#37113) 2025-12-04 08:46:24 +00:00
Echo
31c392b1bc Wrapstodon modal with new share button (#37109) 2025-12-03 16:25:36 +00:00
diondiondion
498e88f059 Fix color contrast issues caused by new theme tokens (#37105) 2025-12-03 15:43:26 +00:00
Matt Jankowski
7c730e9041 Use normalizes API for Tag display_name value (#35797) 2025-12-03 15:39:01 +00:00
Matt Jankowski
b3b5bf26d1 Remove duplicate set_locale around action in auth/registrations (#36455) 2025-12-03 15:34:10 +00:00
David Roetzel
4e6d1892b9 Fix creation of duplicate conversations (#37108) 2025-12-03 14:33:27 +00:00
Echo
e5e3a64a9b Display Wrapstodon inline widget (#37106) 2025-12-03 13:58:38 +00:00
Echo
234990cc37 Remove noreferrer from external links (#37107) 2025-12-03 13:54:58 +00:00
Claire
08da9d8fc5 Fix serialization of annual reports with share code (#37104) 2025-12-03 13:05:33 +00:00
Claire
c97d25fcbd Fix serialization of 2025 wrapstodon reports (#37103) 2025-12-03 12:53:16 +00:00
renovate[bot]
e222664539 Update DefinitelyTyped types (non-major) (#37073)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 11:17:01 +00:00
renovate[bot]
9d10137c7c Update dependency bundler-audit to v0.9.3 (#37061)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 11:15:47 +00:00
Claire
5d84957117 Add shareable wrapstodon links (#37047) 2025-12-03 11:00:41 +00:00
github-actions[bot]
954f397743 New Crowdin Translations (automated) (#37098)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-03 10:11:20 +00:00
Claire
73294e2561 Fix typo in AsyncRefresh ID for GenerateAnnualReportWorker (#37096) 2025-12-03 08:31:14 +00:00
Claire
d2e1c0e1e2 Merge pull request #3302 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 92278796c3
2025-12-02 22:18:31 +01:00
Claire
d600950b83 Remove unmaintained win95 theme 2025-12-02 21:06:16 +01:00
Claire
1ca3894048 Replace old glitch-soc themes by the new ones 2025-12-02 21:04:31 +01:00
Claire
13cf55c501 Merge commit '92278796c3d0fa43c08bd59f66815b196fb273a3' into glitch-soc/merge-upstream
Conflicts:
- `app/helpers/theme_helper.rb`:
  Upstream removed the feature flag and refactored, while we have a slightly
  different theming system.
  Updated accordingly.
- `stylelint.config.js`:
  Upstream removed overrides for the temporary themes.
  Did the same.
2025-12-02 20:45:10 +01:00
renovate[bot]
f393ff93cb Update dependency @optimize-lodash/rollup-plugin to v5.1.0 (#37039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 13:45:33 +00:00
renovate[bot]
9c3b41f0a4 Update dependency vite-plugin-pwa to v1.2.0 (#37040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 13:45:27 +00:00
renovate[bot]
e45ecc7d13 Update dependency express to v5.2.0 [SECURITY] (#37089)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 13:43:46 +00:00
Claire
f8422e1fa4 Add API for on-demand generation of annual reports (#37055) 2025-12-02 13:37:05 +00:00
diondiondion
9aec6936e5 Add Wrapstodon timeline announcement component (#37093) 2025-12-02 11:36:20 +00:00
Claire
2b25b65972 Add missing translations for web push notifications (#37078) 2025-12-02 10:52:08 +00:00
Echo
100b20f290 Add UJS to buttons (#37091) 2025-12-02 10:35:29 +00:00
Echo
c5c8100d02 Emoji: Update emoji categories with featured emoji (#37084) 2025-12-02 10:30:08 +00:00
github-actions[bot]
2e5744e8c6 New Crowdin Translations (automated) (#37088)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-02 10:20:40 +00:00
David Roetzel
2d203ca72a First draft of API to get a single collection (#37053) 2025-12-01 15:04:52 +00:00
Christian Oelschlegel
1f9ddb7cf6 fix(tag): prevent dupl. tags on concurrent inserts (#35792)
Co-authored-by: Christian Oelschlegel <oelschle@sciphy.de>
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2025-12-01 14:56:52 +00:00
Echo
048746e56b Hide domain for local emojis in admin (#37080) 2025-12-01 14:24:24 +00:00
diondiondion
f5d6f60ca7 Ensure all pages have a solid background color (#37081) 2025-12-01 14:16:27 +00:00
Claire
e5651e7e04 Make settings-related database migrations more robust (#37079) 2025-12-01 14:11:49 +00:00
Claire
edfbcfb3f5 Fix error handling when re-fetching already-known statuses (#37077) 2025-12-01 10:52:06 +00:00
github-actions[bot]
f562ad9f67 New Crowdin Translations (automated) (#37063)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-12-01 09:42:49 +00:00
Claire
a4357def8a Merge pull request #3301 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 37ccffa95a
2025-12-01 00:02:53 +01:00
diondiondion
bb097056dc [Glitch] Fix current item in pagination not highlighted with new theme tokens
Port 37ccffa95a to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:52:13 +01:00
diondiondion
906dd88d7e [Glitch] Fix post navigation in single-column mode when Advanced UI is enabled
Port f12f198f61 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:51:49 +01:00
diondiondion
b3135c1eed [Glitch] Limit height of tall images in posts
Port 5f33ac208f to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:51:21 +01:00
diondiondion
b1d00f288f [Glitch] Contain tall videos in full-width video container
Port 2bd7c855b0 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:50:56 +01:00
Claire
7bc48f2833 [Glitch] Fix filters not applying to search results
Port 44ff2c32d3 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:50:23 +01:00
Echo
9ede21cbe2 [Glitch] Fix media modal misalignment in Safari
Port f07cff42c2 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:50:01 +01:00
Echo
17379b73f7 [Glitch] Replace Rails UJS library
Port 0d2e9522ff to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:48:45 +01:00
Claire
99ff59b7b2 Merge commit '37ccffa95a30772b55e3f18d486d699ee6c5f9e8' into glitch-soc/merge-upstream 2025-11-30 17:47:27 +01:00
diondiondion
51698213b5 [Glitch] Remove unused bundle-related Redux actions
Port 07ecf648dd to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:03:10 +01:00
diondiondion
78feddec79 [Glitch] Fix error page when logging out or boosting on mobile
Port e126cfc76d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:02:04 +01:00
diondiondion
9d47d6790f [Glitch] Fix null access error in card component
Port ee7e756e89 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:01:23 +01:00
diondiondion
3b4b57e950 [Glitch] Refactor Card component to TypeScript
Port f87f30c1ac to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 17:00:53 +01:00
diondiondion
b75a01634e [Glitch] Fix issues in new theme tokens
Port 59e48657cf to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 16:51:16 +01:00
diondiondion
18d46054b5 [Glitch] Replace most unsafe React lifecycle methods
Port 8c772028ac to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 16:50:13 +01:00
diondiondion
281d12d5d6 [Glitch] Prevent vertical videos from overflowing the viewport
Port 861625fdca to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-30 16:41:33 +01:00
Claire
44e6abe48b Merge commit '0004ed4c8021ebd400d32eb32a5e479ebd7bcf91' into glitch-soc/merge-upstream 2025-11-30 16:40:18 +01:00
Claire
2e543ff246 Port new refactored themes to glitch-soc (#3296) 2025-11-30 16:37:40 +01:00
diondiondion
9334bd9ede Don't reset scroll when using hotkeys to focus columns, add hotkey 0 to scroll to top (#37052) 2025-11-28 16:19:23 +00:00
Claire
801672e3cb Add method to quickly test for AnnualReport account eligibility (#37045) 2025-11-28 16:19:23 +00:00
diondiondion
92278796c3 Remove theme_tokens feature flag & make new styles the default (#37056) 2025-11-28 15:58:46 +00:00
diondiondion
37ccffa95a Fix current item in pagination not highlighted with new theme tokens (#37054) 2025-11-28 14:45:38 +00:00
Echo
84ffb107c3 Adjust Chromatic to run conditionally (#37050) 2025-11-28 13:37:04 +00:00
David Roetzel
f896bbac3b Draft API to create Collections (#37049) 2025-11-28 13:30:43 +00:00
github-actions[bot]
6b38352b17 New Crowdin Translations (automated) (#37042)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-28 10:44:50 +00:00
diondiondion
f12f198f61 Fix post navigation in single-column mode when Advanced UI is enabled (#37044) 2025-11-28 10:05:54 +00:00
Claire
e0912c1729 Fix Card component using incorrect punycode module (#37043) 2025-11-28 09:50:37 +00:00
Claire
945ef5a8e1 Remove unused data from 2025 annual reports (#37033) 2025-11-28 08:58:34 +00:00
diondiondion
5f33ac208f Limit height of tall images in posts (#37035) 2025-11-27 17:05:56 +00:00
diondiondion
2bd7c855b0 Contain tall videos in full-width video container (#37032) 2025-11-27 17:05:21 +00:00
Claire
44ff2c32d3 Fix filters not applying to search results (#36346) 2025-11-27 16:23:11 +00:00
Eashwar Ranganathan
826e9d7047 Make tootctl aware of 'require approval' for email domains (#34579) 2025-11-27 15:52:59 +00:00
Echo
f07cff42c2 Fix media modal misalignment in Safari (#37034) 2025-11-27 15:27:18 +00:00
Echo
0d2e9522ff Replace Rails UJS library (#37031) 2025-11-27 13:10:55 +00:00
renovate[bot]
0004ed4c80 chore(deps): update opentelemetry-ruby (non-major) (#37016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 10:51:57 +00:00
diondiondion
07ecf648dd Remove unused bundle-related Redux actions (#37030) 2025-11-27 10:50:48 +00:00
github-actions[bot]
90466d0262 New Crowdin Translations (automated) (#37026)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-27 09:51:15 +00:00
Matt Jankowski
199376a080 Use existing time format string to generate backup archive filename (#36469) 2025-11-27 09:38:27 +00:00
diondiondion
e126cfc76d Fix error page when logging out or boosting on mobile (#37028) 2025-11-27 09:36:58 +00:00
David Roetzel
322a4fee53 First steps towards a collection creation service (#37020)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2025-11-27 09:36:44 +00:00
renovate[bot]
be2caba527 chore(deps): update dependency i18n-tasks to v1.1.2 (#37027)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 09:29:14 +00:00
renovate[bot]
002632c3bb chore(deps): update dependency aws-sdk-core to v3.239.2 (#37015)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 16:18:51 +00:00
renovate[bot]
81510455d1 chore(deps): update yarn to v4.12.0 (#36797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 16:18:48 +00:00
diondiondion
ee7e756e89 Fix null access error in card component (#37022) 2025-11-26 14:55:40 +00:00
diondiondion
f87f30c1ac Refactor Card component to TypeScript (#36982) 2025-11-26 12:56:17 +00:00
renovate[bot]
1757a0f0f3 chore(deps): update dependency public_suffix to v7 (#36920)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 10:48:12 +00:00
Matt Jankowski
cb4f1cc89c Improve SessionActivation.activate spec (#36983) 2025-11-26 10:26:39 +00:00
Claire
00163e89bf Fix tootctl status remove removing quoted posts and remote quotes of local posts (#37009) 2025-11-26 10:26:26 +00:00
diondiondion
59e48657cf Fix issues in new theme tokens (#37019) 2025-11-26 10:25:49 +00:00
github-actions[bot]
384594f462 New Crowdin Translations (automated) (#37018)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-26 10:25:41 +00:00
renovate[bot]
cd9d166312 chore(deps): update dependency rqrcode to v3.1.1 (#37010)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 09:54:11 +00:00
renovate[bot]
6f4f9942b9 chore(deps): update dependency connection_pool to v2.5.5 (#37003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 09:53:38 +00:00
Claire
7e7c21032b Merge pull request #3300 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to ca53195b31
2025-11-25 18:48:10 +01:00
Claire
382dec843b [Glitch] Fix compose autosuggest always lowercasing token
Port a26636ff1f to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-25 18:23:34 +01:00
Claire
868d45df2f Merge commit 'ca53195b31ef7eb9205f879e022b09b28efc9314' into glitch-soc/merge-upstream 2025-11-25 18:22:48 +01:00
David Roetzel
0725afe1a9 Collections: Add missing validations for boolean columns (#37005) 2025-11-25 14:46:50 +00:00
renovate[bot]
09697045a9 chore(deps): update dependency rails-i18n to v8.1.0 (#36992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 14:18:48 +00:00
renovate[bot]
3e77c3bc8c chore(deps): update dependency omniauth-rails_csrf_protection to v2 (#36993)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 14:18:32 +00:00
renovate[bot]
bd02cd4591 chore(deps): update dependency addressable to v2.8.8 (#37002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 14:07:06 +00:00
renovate[bot]
4ca458e0b4 chore(deps): update devdependencies (non-major) (#36802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 13:53:23 +00:00
diondiondion
8c772028ac Replace most unsafe React lifecycle methods (#36970) 2025-11-25 13:49:45 +00:00
diondiondion
861625fdca Prevent vertical videos from overflowing the viewport (#36966) 2025-11-25 13:21:59 +00:00
renovate[bot]
ca53195b31 chore(deps): update dependency sass to v1.94.2 (#36826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-25 13:21:25 +00:00
Claire
a26636ff1f Fix compose autosuggest always lowercasing token (#36995) 2025-11-25 13:17:44 +00:00
Claire
204143becc Increase HTTP read timeout for expensive S3 batch delete operation (#37004) 2025-11-25 10:18:34 +00:00
github-actions[bot]
f0d7ea61ef New Crowdin Translations (automated) (#37000)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-25 09:52:51 +00:00
Claire
4d92051f40 Merge pull request #3299 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 76d8ac3fe6
2025-11-24 19:26:06 +01:00
Claire
b76530a7f1 Merge commit '76d8ac3fe680e92d20059c590ccb1cd8f74078c2' into glitch-soc/merge-upstream 2025-11-24 19:08:03 +01:00
renovate[bot]
76d8ac3fe6 chore(deps): update dependency i18n-tasks to v1.1.1 (#36997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 15:34:10 +00:00
Claire
96d5e57351 Revert "Increase HTTP read timeout for expensive S3 batch delete operation (#36971)" (#36996) 2025-11-24 13:33:37 +00:00
renovate[bot]
57bfe863f3 chore(deps): update dependency aws-sdk-core to v3.239.1 (#36955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 09:10:07 +00:00
David Roetzel
b16452dd99 Add shared context for API authentication (#36981) 2025-11-24 08:48:49 +00:00
renovate[bot]
1bc13609ab chore(deps): update dependency aws-sdk-s3 to v1.205.0 (#36956)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 08:42:56 +00:00
github-actions[bot]
e44a9c0879 New Crowdin Translations (automated) (#36984)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-24 08:08:01 +00:00
Matt Jankowski
f1bf6e6344 Remove unused from_limited? method from NotifyService (#36988) 2025-11-24 07:57:46 +00:00
Claire
5885b6715a Merge pull request #3295 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 585545d0d5
2025-11-21 23:09:23 +01:00
diondiondion
975c7097b8 [Glitch] Refactor PrivacyDropdown to TypeScript
Port 8a235dd187 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-21 19:35:20 +01:00
Claire
652ed7ab50 Merge commit '585545d0d5678a6ea4b958af4a3bc6593134d0f6' into glitch-soc/merge-upstream 2025-11-21 19:33:14 +01:00
Matt Jankowski
585545d0d5 Add coverage for media#player scenarios (#35947) 2025-11-21 14:46:29 +00:00
Matt Jankowski
d967137adf Remove unneeded type check on Status in og_image partial (#36980) 2025-11-21 14:29:03 +00:00
Pēteris Caune
ad7839e551 Fix the translation of "Latvian" (#36876) 2025-11-21 13:56:20 +00:00
diondiondion
8a235dd187 Refactor PrivacyDropdown to TypeScript (#36979) 2025-11-21 13:33:27 +00:00
Shugo Maeda
48fe679728 Separate remote thumbnails into cache/ directory (#36911) 2025-11-21 13:27:04 +00:00
renovate[bot]
687f3a2a01 chore(deps): update dependency vite to v7.2.4 (#36964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-21 11:00:29 +00:00
David Roetzel
7ffa5fa0c4 Add models to represent "Collections" (#36977) 2025-11-21 10:28:23 +00:00
github-actions[bot]
cfa4f402ef New Crowdin Translations (automated) (#36976)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-21 09:34:55 +00:00
renovate[bot]
aa131e538c chore(deps): update dependency core-js to v3.47.0 (#36931)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-21 09:17:57 +00:00
Matt Jankowski
6151febd73 Suggest ES image version 7.17.29 in docker compose (#36972) 2025-11-21 09:16:50 +00:00
Claire
a54334b714 Increase HTTP read timeout for expensive S3 batch delete operation (#36971) 2025-11-21 08:59:07 +00:00
Claire
2f2065751d Merge pull request #3294 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 21ce99f746
2025-11-20 22:44:55 +01:00
diondiondion
aec23fd4a2 [Glitch] Fix error when visiting non-public hashtag timelines
Port f01e80bed3 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-20 18:20:53 +01:00
Darius Kazemi
8e70c54d0e [Glitch] Remove stray Font Awesome styles
Port dc67dbba82 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-20 18:19:33 +01:00
Claire
284223f45f [Glitch] Fix statuses without text disappearing on reload
Port bb9a633b99 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-20 18:19:11 +01:00
Echo
8e68d6c6bf [Glitch] Improve media modal swipe animation
Port ea616ac4a4 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-20 18:18:46 +01:00
Claire
dd7d750f5d Merge commit '21ce99f746e17867df74029d3b2caca45588818d' into glitch-soc/merge-upstream 2025-11-20 18:13:37 +01:00
Claire
21ce99f746 Bump version to v4.5.2 (#36968) 2025-11-20 15:04:22 +00:00
github-actions[bot]
8e8669b5ef New Crowdin Translations (automated) (#36957)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-20 10:54:39 +00:00
Claire
a28f1d0110 Fix missing fallback link in CW-only quote posts (#36963) 2025-11-20 10:08:36 +00:00
diondiondion
f01e80bed3 Fix error when visiting non-public hashtag timelines (#36961) 2025-11-20 10:08:00 +00:00
Darius Kazemi
dc67dbba82 Remove stray Font Awesome styles (#36960) 2025-11-20 10:02:50 +00:00
Claire
bb9a633b99 Fix statuses without text disappearing on reload (#36962) 2025-11-20 09:52:53 +00:00
Claire
21110f0270 Fix d bookmark keyboard shortcut (#3285) 2025-11-19 22:25:53 +01:00
Claire
4612014192 Merge pull request #3288 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 366856f3bc
2025-11-19 22:25:28 +01:00
diondiondion
b1974a2147 [Glitch] Fix g + h keyboard shortcut not working when a post is focused
Port 4d0aab4a31 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 22:07:22 +01:00
Claire
c57ca36006 [Glitch] Fix quoting overwriting current content warning
Port c22b203bca to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 22:07:22 +01:00
Claire
69dfde3153 [Glitch] Fix scroll-to-status in threaded view being unreliable
Port be0e23bb0a to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 22:07:22 +01:00
Claire
c5a6519af9 [Glitch] Change private quote education modal to not show up on self-quotes
Port c820c66d3c to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 21:54:24 +01:00
Claire
9c7f27ba14 [Glitch] Fix double encoding in links
Port b4daad8c89 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 21:51:54 +01:00
Echo
76ba4000d9 [Glitch] Emoji: Fix path resolution for emoji worker
Port caffb0fd63 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 21:51:34 +01:00
diondiondion
89d04f3bb3 [Glitch] Fix superfluous border & spacing in domains list on the Moderation > Federation page
Port 72c582e7e5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 21:50:37 +01:00
Claire
7a56972381 Merge commit '366856f3bcdc2ff008b04e493a5de317ab83d5d0' into glitch-soc/merge-upstream 2025-11-19 21:49:09 +01:00
Claire
a4fd9b704a Merge commit '284b46fee7ad682b52c99d0455a1134e1d4e3fc3' into glitch-soc/merge-upstream
Conflicts:
- `app/helpers/theme_helper.rb`:
  Upstream added an experimental feature flag to try out theme refactors.
  Glitch-soc's theming system is too different, so skips the new feature flag.
- `stylelint.config.js`:
  Ditto.
2025-11-19 21:34:53 +01:00
Claire
fa721568e0 Change quotes to inherit local-only status of quoted post in composer (#3286) 2025-11-19 21:28:51 +01:00
Echo
ea616ac4a4 Improve media modal swipe animation (#36916) 2025-11-19 15:56:24 +00:00
renovate[bot]
01b11c328c chore(deps): update dependency i18n-tasks to v1.1.0 (#36907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 15:37:02 +00:00
Claire
bc7c83ba76 Update glob dependency (#36940) 2025-11-19 15:28:57 +00:00
diondiondion
366856f3bc Fix theme-related Vite errors even when theme_tokens feature flag is disabled (#36936) 2025-11-19 12:37:15 +00:00
diondiondion
4d0aab4a31 Fix g + h keyboard shortcut not working when a post is focused (#36935) 2025-11-19 10:58:07 +00:00
Claire
c22b203bca Fix quoting overwriting current content warning (#36934) 2025-11-19 10:35:10 +00:00
renovate[bot]
52b92bdc9c chore(deps): update dependency bootsnap to '~> 1.19.0' (#36906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 10:19:49 +00:00
Matt Jankowski
4f6a7e44d1 Update rubocop-rspec to version 3.8.0 (#36853) 2025-11-19 10:14:51 +00:00
github-actions[bot]
81ffd1d3c7 New Crowdin Translations (automated) (#36933)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-19 10:09:54 +00:00
Matt Jankowski
9872197d1f Fix Rails/RedirectBackOrTo cop (#36930) 2025-11-19 09:28:43 +00:00
Claire
41279ac801 Fix threaded mode not resetting quote (#3284) 2025-11-18 18:38:53 +01:00
Claire
902b5a169c Clean up CSS differences with upstream (#3283) 2025-11-18 18:38:48 +01:00
Claire
be0e23bb0a Fix scroll-to-status in threaded view being unreliable (#36927) 2025-11-18 16:20:50 +00:00
Claire
c820c66d3c Change private quote education modal to not show up on self-quotes (#36926) 2025-11-18 16:17:26 +00:00
Claire
b4daad8c89 Fix double encoding in links (#36925) 2025-11-18 15:37:14 +00:00
github-actions[bot]
b14f113929 New Crowdin Translations (automated) (#36922)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-18 08:22:25 +00:00
Echo
caffb0fd63 Emoji: Fix path resolution for emoji worker (#36897) 2025-11-17 15:34:18 +00:00
Matt Jankowski
53703202fb Add IpBlock#to_cidr method to build string (#35773) 2025-11-17 14:51:05 +00:00
Matt Jankowski
59fdff5dc5 Add spec for translation attempt with ineligible target language (#36813) 2025-11-17 14:13:52 +00:00
renovate[bot]
04bdfa1957 chore(deps): update dependency @vitejs/plugin-react to v5.1.1 (#36841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 14:12:38 +00:00
Shugo Maeda
04c566e2e9 Fix ArgumentError of tootctl upgrade storage-schema (#36914) 2025-11-17 13:34:20 +00:00
diondiondion
72c582e7e5 Fix superfluous border & spacing in domains list on the Moderation > Federation page (#36915) 2025-11-17 12:07:44 +00:00
diondiondion
284b46fee7 Implement CSS theme tokens behind feature flag (#36861) 2025-11-17 09:44:55 +00:00
github-actions[bot]
489bee8f4e New Crowdin Translations (automated) (#36898)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-17 09:33:15 +00:00
Claire
932f479a34 Fix missing alt-text confirmation modal not opening (#3281) 2025-11-15 12:03:56 +01:00
Claire
8839ecf2a4 Merge pull request #3279 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to c87b052829
2025-11-15 11:18:59 +01:00
Claire
5645a017b3 [Glitch] Fix inability to paste links
Port c87b052829 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:29:07 +01:00
Echo
8817ebda50 [Glitch] Remove rails delegate
Port 7dbb2ac79a to glitch-soc

Co-authored-by: Renaud Chaput <renchap@gmail.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:28:42 +01:00
Jeong Arm
f782c2c8e9 [Glitch] Allow drag&drop of link to quote in compose form
Port 4ab1d5d724 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:27:29 +01:00
Echo
ee257dc307 [Glitch] Fix error with remote tags including percent signs
Port c5eca8ffb2 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:26:42 +01:00
Claire
8240644b6e [Glitch] Fix bogus quote approval policy not always being replaced correctly
Port f25e066112 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:25:37 +01:00
Claire
593d21d2ed [Glitch] Fix hashtag completion not being inserted correctly
Port 6d8c43ab85 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:24:45 +01:00
diondiondion
951816c5d6 [Glitch] Fix Cmd/Ctrl + Enter in the composer triggering confirmation dialog action
Port 6e294828d6 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-14 18:24:22 +01:00
Claire
e0d7230f97 Merge commit 'c87b05282909383353a9561e97b9f18a2db0766d' into glitch-soc/merge-upstream 2025-11-14 18:19:00 +01:00
Claire
c87b052829 Fix inability to paste links (#36896) 2025-11-14 16:44:08 +00:00
github-actions[bot]
ebc99cd597 New Crowdin Translations (automated) (#36893)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-14 15:57:35 +00:00
Claire
6db4297193 Fix cross-origin handling of CSS modules (#36890) 2025-11-14 15:42:26 +00:00
renovate[bot]
bc47cba123 chore(deps): update dependency js-yaml to v4.1.1 [security] (#36891)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 15:27:43 +00:00
Matt Jankowski
f8ffb85566 Use date attribute for date of birth (#36039) 2025-11-14 15:17:53 +00:00
Echo
7dbb2ac79a Remove rails delegate (#36835)
Co-authored-by: Renaud Chaput <renchap@gmail.com>
2025-11-14 14:18:14 +00:00
renovate[bot]
bc81e299f2 chore(deps): update dependency stoplight to v5.6.0 (#36803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 14:17:50 +00:00
renovate[bot]
277a4c80c0 chore(deps): update dependency prometheus_exporter to v2.3.1 (#36842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 14:17:38 +00:00
renovate[bot]
7be8fe6370 chore(deps): update dependency aws-sdk-core to v3.237.0 (#36823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 14:15:56 +00:00
Jeong Arm
4ab1d5d724 Allow drag&drop of link to quote in compose form (#36859) 2025-11-14 14:14:37 +00:00
Echo
c5eca8ffb2 Fix error with remote tags including percent signs (#36886) 2025-11-14 14:11:41 +00:00
Claire
f25e066112 Fix bogus quote approval policy not always being replaced correctly (#36885) 2025-11-14 12:39:04 +00:00
Claire
6d8c43ab85 Fix hashtag completion not being inserted correctly (#36884) 2025-11-14 12:23:40 +00:00
Claire
0d7c23469b Bump version to v4.5.1 (#36872) 2025-11-14 10:43:08 +00:00
github-actions[bot]
f243a00b90 New Crowdin Translations (automated) (#36875)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-14 10:27:20 +00:00
diondiondion
6e294828d6 Fix Cmd/Ctrl + Enter in the composer triggering confirmation dialog action (#36870) 2025-11-14 09:39:42 +00:00
Claire
101bd01e6e Merge pull request #3277 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 98c8c1ebd2
2025-11-13 18:40:45 +01:00
Claire
d53ff25529 [Glitch] Fix error when sending new posts
Port 98c8c1ebd2 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-13 18:14:59 +01:00
Claire
8ab9040afc [Glitch] Fix posts coming from public/hashtag streaming being marked as unquotable
Port 9dbebbb2ee to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-13 18:14:42 +01:00
diondiondion
19cc39abf0 [Glitch] Fix Cmd/Ctrl + Enter not submitting Alt text modal on some browsers
Port 998d4cc0dc to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-13 18:14:27 +01:00
Claire
bebc79d160 Merge commit '98c8c1ebd278f59e97c7d17628e7710cd4c933d4' into glitch-soc/merge-upstream 2025-11-13 18:12:49 +01:00
Claire
98c8c1ebd2 Fix error when sending new posts (#36869) 2025-11-13 15:42:14 +00:00
diondiondion
998d4cc0dc Fix Cmd/Ctrl + Enter not submitting Alt text modal on some browsers (#36866) 2025-11-13 15:35:25 +00:00
Claire
9dbebbb2ee Fix posts coming from public/hashtag streaming being marked as unquotable (#36860) 2025-11-13 12:54:28 +00:00
github-actions[bot]
3f292e0f5b New Crowdin Translations (automated) (#36858)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-13 10:18:16 +00:00
Claire
9dd7c816d2 Merge pull request #3275 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 7e7f63a2ef
2025-11-12 19:35:15 +01:00
Claire
191d6b071c [Glitch] Revert "Ensure the boost button shows a numeric value (#36805)"
Port 8abec0ffcb to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-12 18:15:19 +01:00
Echo
5f01e75290 [Glitch] Fix deprecation warning in Vite
Port 00cbc1b910 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-12 18:14:54 +01:00
diondiondion
1c749e21f8 [Glitch] Fixes blank screen in browsers that don't support Intl.DisplayNames
Port f303f3458d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-12 18:14:11 +01:00
Claire
01f9397e37 [Glitch] Fix filters not being applied to quotes in detailed view
Port 9f3573d446 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-12 18:13:49 +01:00
Echo
62ce66dacb [Glitch] Fix duplicate counters
Port 4b1532e008 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-12 18:13:27 +01:00
Claire
9525134c28 Merge commit '7e7f63a2efc83edd7c59a12e55eaab9509a4c15b' into glitch-soc/merge-upstream 2025-11-12 18:11:41 +01:00
Itoh Shimon
7e7f63a2ef Use the native script for Divehi (#36254) 2025-11-12 16:58:40 +00:00
Claire
ed3710e58f Fix Update importing old previously-unknown activities and treating them as recent ones (#36848) 2025-11-12 16:09:00 +00:00
Claire
8abec0ffcb Revert "Ensure the boost button shows a numeric value (#36805)" (#36850) 2025-11-12 15:32:55 +00:00
Echo
00cbc1b910 Fix deprecation warning in Vite (#36849) 2025-11-12 14:25:25 +00:00
diondiondion
f303f3458d Fixes blank screen in browsers that don't support Intl.DisplayNames (#36847) 2025-11-12 11:11:48 +00:00
Claire
9f3573d446 Fix filters not being applied to quotes in detailed view (#36843) 2025-11-12 11:09:51 +00:00
Echo
4b1532e008 Fix duplicate counters (#36844) 2025-11-12 11:01:34 +00:00
github-actions[bot]
ff0fca018a New Crowdin Translations (automated) (#36838)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-12 10:24:50 +00:00
Claire
12ac3317aa Merge pull request #3273 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to b5a2fe715d
2025-11-12 09:49:33 +01:00
diondiondion
fdfbc63199 [Glitch] Fix icon buttons animating when they haven't changed
Port b53ee04475 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-11 20:25:33 +01:00
Echo
e265c6bd4c [Glitch] Emoji: Load emoji with hash in URL
Port ff5d745e3d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-11 20:25:14 +01:00
Claire
48146e5371 Merge commit 'b5a2fe715d158d5fdf4ad9ddef0787b781b4da6e' into glitch-soc/merge-upstream 2025-11-11 20:22:04 +01:00
Echo
b5a2fe715d Revert "Fix duplicated counters (fix #32614)" (#36834) 2025-11-11 18:05:44 +00:00
renovate[bot]
11b75d616a chore(deps): update dependency aws-sdk-s3 to v1.203.1 (#36822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-11 15:47:08 +00:00
Asahi Lina
394ed551bb Serialize legacy quotes (#3203) 2025-11-11 13:23:51 +01:00
Claire
565f437f93 Merge pull request #3230 from glitch-soc/i18n/crowdin/translations
New Crowdin Translations (automated)
2025-11-11 12:51:18 +01:00
Matt Jankowski
a002048c8c Update storybook/msw/chromatic (#36739) 2025-11-11 11:17:23 +00:00
Matt Jankowski
fa5318b333 Remove webpack hints IDE config (#36769) 2025-11-11 11:16:15 +00:00
Claire
095a9571e2 Update simple_form.no.yml 2025-11-11 12:15:55 +01:00
Claire
7ca2a7d9d6 Update no.yml 2025-11-11 12:15:43 +01:00
diondiondion
b53ee04475 Fix icon buttons animating when they haven't changed (#36824) 2025-11-11 11:10:27 +00:00
Claire
2759bafe09 Merge pull request #3272 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 1c3e7545cb
2025-11-11 12:07:29 +01:00
xatier
84cdb6cc66 Fix duplicated counters (fix #32614) (#36785) 2025-11-11 11:02:41 +00:00
Echo
ff5d745e3d Emoji: Load emoji with hash in URL (#36808) 2025-11-11 10:03:06 +00:00
github-actions[bot]
391c77f277 New Crowdin Translations (automated) (#36820)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-11 08:38:02 +00:00
Renaud Chaput
bc5397a0db Remove the 2 PRs-per-hour Renovate limit (#36814) 2025-11-11 08:26:43 +00:00
Echo
f5cbe73d76 [Glitch] Centers GIFs in media modal
Port 4ddddc2573 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-10 23:04:50 +01:00
diondiondion
5af57000a0 [Glitch] Fix scroll shift caused by fetch-all-replies alerts
Port 400943cb4e to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-10 22:30:53 +01:00
Echo
c428129c48 [Glitch] Ensure the boost button shows a numeric value
Port 9a42d00c12 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-10 22:30:26 +01:00
Renaud Chaput
5b75667c03 [Glitch] Update to latest eslint-plugin-react-hooks
Port 9addad8ce5 to glitch-soc

Co-authored-by: diondiondion <mail@diondiondion.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-10 22:29:58 +01:00
diondiondion
01f7a6796f [Glitch] Fix dropdown menu not focusing first item when opened via keyboard
Port 1280792678 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-10 22:22:49 +01:00
Claire
d31aaf9ed8 Merge commit '1c3e7545cb6137025a6efd208d195352c54ffda8' into glitch-soc/merge-upstream 2025-11-10 22:18:01 +01:00
Matt Jankowski
1c3e7545cb Add spec for translation attempt on private status (#36810) 2025-11-10 16:05:20 +00:00
diondiondion
400943cb4e Fix scroll shift caused by fetch-all-replies alerts (#36807) 2025-11-10 15:33:56 +00:00
Echo
9a42d00c12 Ensure the boost button shows a numeric value (#36805) 2025-11-10 15:10:24 +00:00
Renaud Chaput
9addad8ce5 Update to latest eslint-plugin-react-hooks (#36702)
Co-authored-by: diondiondion <mail@diondiondion.com>
2025-11-10 14:50:04 +00:00
Echo
4ddddc2573 Centers GIFs in media modal (#36806) 2025-11-10 13:21:45 +00:00
diondiondion
1280792678 Fix dropdown menu not focusing first item when opened via keyboard (#36804) 2025-11-10 11:44:10 +00:00
Matt Jankowski
63e2ca5d27 Add spec for hitting max status pins limit in pin api (#36789) 2025-11-10 10:06:47 +00:00
github-actions[bot]
b0790d828c New Crowdin Translations (automated) (#36786)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-10 10:06:07 +00:00
renovate[bot]
89b5ceb5dc chore(deps): update dependency vite to v7.2.2 (#36751)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 09:03:33 +00:00
GitHub Actions
91d17b5891 New Crowdin translations 2025-11-09 04:34:42 +00:00
Claire
6153479bad Merge pull request #3271 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 441eb89537
2025-11-08 18:19:16 +01:00
Echo
474cab03bd [Glitch] Reset background color on media modal
Port 882afd7748 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-08 10:46:33 +01:00
Claire
08ef682995 [Glitch] Fix prepared quote not being discarded with contents when replying
Port 315833cb75 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-08 10:46:14 +01:00
Claire
c1ef6e31cb Merge commit '441eb895371cf8ed6a5b8e294367bbbfd0f4c037' into glitch-soc/merge-upstream 2025-11-08 10:44:43 +01:00
renovate[bot]
441eb89537 chore(deps): update dependency axios to v1.13.2 (#36726)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 13:25:41 +00:00
Echo
882afd7748 Reset background color on media modal (#36782) 2025-11-07 10:44:31 +00:00
Claire
8fb06ea0ca Update dependency rollup from 4.46.2 to 4.46.4 (#36781) 2025-11-07 10:29:05 +00:00
Matt Jankowski
c7dc5767d3 Rely on puma default environment setting (#36760) 2025-11-07 10:06:22 +00:00
Matt Jankowski
6833878f95 Clarify hint for bootstrap account instructions (#36771) 2025-11-07 10:05:52 +00:00
Claire
70d71c10c8 Fix /api/v1/statuses/:id/context sometimes returing Mastodon-Async-Refresh without result_count (#36779) 2025-11-07 10:02:25 +00:00
Claire
315833cb75 Fix prepared quote not being discarded with contents when replying (#36778) 2025-11-07 09:11:10 +00:00
github-actions[bot]
dcf7fc1028 New Crowdin Translations (automated) (#36777)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-07 08:34:18 +00:00
Claire
0ace564537 Add 4.5.x to the list of supported branches (#36762) 2025-11-06 16:23:56 +00:00
Matt Jankowski
16dfa32578 Update express to version 5.1.0 (#36669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 14:46:08 +00:00
Matt Jankowski
0d48005b8a Remove legacy AR connection boot from puma config (#36757) 2025-11-06 14:36:23 +00:00
renovate[bot]
59e0ead418 chore(deps): update dependency stoplight to v5.5.0 (#36731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 14:24:43 +00:00
renovate[bot]
76fb15dced chore(deps): update dependency aws-sdk-s3 to v1.203.0 (#36746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 14:17:04 +00:00
Claire
4d44f4c57e Merge pull request #3270 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 6f1a30c4a6
2025-11-06 15:15:19 +01:00
renovate[bot]
5d3c1cdc9b chore(deps): update libretranslate/libretranslate docker tag to v1.7.3 (#36724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 14:03:35 +00:00
github-actions[bot]
15c33a16f7 New Crowdin Translations (automated) (#36749)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-06 13:55:37 +00:00
Claire
152505dd9e Merge commit '6f1a30c4a6ea49ff7935f19f5a2cefab3eee9e7c' into glitch-soc/merge-upstream 2025-11-06 14:43:18 +01:00
Claire
6f1a30c4a6 Bump version to v4.5.0 (#36754) 2025-11-06 13:24:58 +00:00
Claire
1a890d2077 Merge pull request #3268 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 58b3fc0379
2025-11-06 13:09:22 +01:00
diondiondion
c8ebc974d1 [Glitch] Refactor "New/Edit list" page to avoid setting state in effect
Port 58b3fc0379 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:44:11 +01:00
Echo
53780dd49e [Glitch] Fix: correctly dismisses announcement when viewed
Port 41a4022988 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:43:47 +01:00
Echo
663f93ca6a [Glitch] Add default visualizer for audio upload without poster
Port 987104f435 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:43:23 +01:00
Echo
13395e2d4d [Glitch] Add new ESLint rule requiring explicit button types
Port 6337e036f3 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:42:17 +01:00
diondiondion
e1b7da0985 [Glitch] Fix spoiler toggle button being able to submit compose form
Port 1b8d1cd6e4 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:37:39 +01:00
diondiondion
ce080a1ca8 [Glitch] Add some outer page spacing when viewport width equals content width
Port 947dfcc548 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-06 12:37:17 +01:00
Claire
58b29be439 Merge commit '58b3fc03796dbde54dbba9a4c554c2d8d24f81fd' into glitch-soc/merge-upstream 2025-11-06 12:35:04 +01:00
diondiondion
58b3fc0379 Refactor "New/Edit list" page to avoid setting state in effect (#36753) 2025-11-06 11:00:37 +00:00
Echo
41a4022988 Fix: correctly dismisses announcement when viewed (#36750) 2025-11-06 09:48:34 +00:00
Emelia Smith
532bb7ea3c Add systemd service file for prometheus exporter (#35130) 2025-11-06 09:25:56 +00:00
Echo
987104f435 Add default visualizer for audio upload without poster (#36734) 2025-11-05 15:33:26 +00:00
Josh Soref
50e1320c8d Improve account migration warnings (#20387)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Co-authored-by: diondiondion <mail@diondiondion.com>
2025-11-05 15:27:40 +00:00
Echo
6337e036f3 Add new ESLint rule requiring explicit button types (#36738) 2025-11-05 15:11:04 +00:00
Yurii Izorkin
1a31c412ca nginx: optimize location blocks (part 2) (#19644) 2025-11-05 15:08:16 +00:00
Claire
4a6f479535 Update changelog (#36737) 2025-11-05 14:43:22 +00:00
Renaud Chaput
15a7abd581 Resume Renovate updates for tesseract (#36705) 2025-11-05 14:25:40 +00:00
diondiondion
1b8d1cd6e4 Fix spoiler toggle button being able to submit compose form (#36736) 2025-11-05 14:00:42 +00:00
Shlee
35bd985727 Increase nginx.conf proxy_read_timeout (#30599) 2025-11-05 13:36:14 +00:00
Oneric
c0c6f5ea32 Fix Accept headers when fetching AP objects to match spec (#30354) 2025-11-05 13:32:29 +00:00
github-actions[bot]
3aeae8cafd New Crowdin Translations (automated) (#36727)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-05 13:20:34 +00:00
diondiondion
947dfcc548 Add some outer page spacing when viewport width equals content width (#36733) 2025-11-05 12:17:42 +00:00
renovate[bot]
049dcebf9a chore(deps): update devdependencies (non-major) (#36722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 11:37:25 +00:00
renovate[bot]
f361a2c766 chore(deps): update dependency vite to v7.2.0 (#36729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 11:36:58 +00:00
renovate[bot]
d4ec991126 chore(deps): update dependency aws-sdk-s3 to v1.202.0 (#36488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 11:36:35 +00:00
Matt Jankowski
1f4fe91708 Relax aws-sdk-core version limit (#36719) 2025-11-05 10:43:10 +00:00
Claire
a18d96ae2d Merge pull request #3264 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 26e7fe9771
2025-11-04 21:30:24 +01:00
Claire
9475eeaada [Glitch] Change paste-link-to-quote loading state from generic loading bar to compose placeholder
Port 26e7fe9771 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 21:00:09 +01:00
Claire
e24151f688 [Glitch] Change quote action to error instead of insert link in Private Mentions
Port 1b795c12e9 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:59:48 +01:00
Echo
58158eba00 [Glitch] Quote Posts: Add notifications for DMs and private posts
Port 3ab5ae1e4a to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:58:52 +01:00
Claire
e011d0fc53 [Glitch] Fix Skeleton placeholders being animated when setting to reduce animations is enabled
Port 4a9460f7bd to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:50:18 +01:00
Echo
7d8dc68c5b [Glitch] Refactor: Media Modal
Port 90d4b3b943 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:49:58 +01:00
Echo
649187c30e [Glitch] Remove announcement margin when in Advanced Web Interface
Port 13457111d5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:44:14 +01:00
Claire
4b5282881a [Glitch] Fix quote dropdown menu item in detailed status view
Port 3a54d56fbd to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:43:54 +01:00
Claire
a116d11bc6 [Glitch] Remove option to disable access to local topic feeds for logged-in users
Port b5c550ff0b to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:43:34 +01:00
Jeong Arm
31b72c0600 [Glitch] Reverse 'Hide/Show all' eye icon for thread view
Port b999a626e5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-04 20:43:02 +01:00
Claire
a7ba4ba446 Merge commit '26e7fe97714d077930621f9111b7eaad2774df65' into glitch-soc/merge-upstream 2025-11-04 20:38:24 +01:00
Claire
26e7fe9771 Change paste-link-to-quote loading state from generic loading bar to compose placeholder (#36695) 2025-11-04 19:20:39 +00:00
Claire
1b795c12e9 Change quote action to error instead of insert link in Private Mentions (#36721) 2025-11-04 17:58:50 +00:00
mkljczk
afd5d5c2e5 fix spelling (#35738) 2025-11-04 16:49:12 +00:00
Echo
3ab5ae1e4a Quote Posts: Add notifications for DMs and private posts (#36696) 2025-11-04 16:32:52 +00:00
Claire
4a9460f7bd Fix Skeleton placeholders being animated when setting to reduce animations is enabled (#36716) 2025-11-04 15:30:10 +00:00
renovate[bot]
e7692d0de8 chore(deps): update artifact actions (major) (major) (#36711)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 15:28:26 +00:00
renovate[bot]
cc77844540 chore(deps): update github/codeql-action action to v4 (#36713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 15:28:17 +00:00
Matt Jankowski
337f16d33e Remove unused DomainControlHelper include from SignedRequest (#35856) 2025-11-04 15:27:42 +00:00
renovate[bot]
ef20dcbf95 chore(deps): update definitelytyped types (non-major) (#36706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 15:01:23 +00:00
renovate[bot]
0c101b47bf chore(deps): update dependency typescript to v5.9.3 (#36707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 14:59:42 +00:00
renovate[bot]
f221ce530b chore(deps): update actions/setup-node action to v6 (#36710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 14:30:29 +00:00
renovate[bot]
8f1c73ed99 chore(deps): update eslint (non-major) (#36708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 14:30:01 +00:00
renovate[bot]
1a698d3b35 chore(deps): update chromaui/action action to v13 (#36712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 14:29:19 +00:00
renovate[bot]
5a2edebc2b chore(deps): update actions/checkout action to v5 (#36709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 14:25:48 +00:00
Echo
90d4b3b943 Refactor: Media Modal (#36673) 2025-11-04 13:58:35 +00:00
Echo
13457111d5 Remove announcement margin when in Advanced Web Interface (#36714) 2025-11-04 13:37:34 +00:00
Claire
3a54d56fbd Fix quote dropdown menu item in detailed status view (#36704) 2025-11-04 11:01:25 +00:00
Claire
b5c550ff0b Remove option to disable access to local topic feeds for logged-in users (#36703) 2025-11-04 10:37:43 +00:00
renovate[bot]
6c176e56ee chore(deps): update dependency opentelemetry-instrumentation-pg to '~> 0.33.0' (#36701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 10:36:04 +00:00
Jeong Arm
b999a626e5 Reverse 'Hide/Show all' eye icon for thread view (#22301) 2025-11-04 10:35:50 +00:00
renovate[bot]
bb084da1f5 fix(deps): update dependency pino-http to v11 (#36359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 10:28:30 +00:00
renovate[bot]
84e351cc3a chore(deps): update dependency shoulda-matchers to v7 (#36680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 10:21:27 +00:00
renovate[bot]
7fced55ce7 fix(deps): update dependency pino to v10 (#36357)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 09:56:12 +00:00
Matt Jankowski
8f37f9d012 Update vitest to version 4.0.5 (#36652) 2025-11-04 09:47:30 +00:00
Matt Jankowski
8e4c9cf933 Update lint-staged to version 16.2.6 (#36668) 2025-11-04 09:43:38 +00:00
Matt Jankowski
cf87da25ad Update storybook to version 10.0.2 (#36657) 2025-11-04 09:42:27 +00:00
renovate[bot]
966aaaaf56 chore(deps): update dependency sidekiq to v8.0.9 (#36699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 09:41:59 +00:00
renovate[bot]
5b880a2046 chore(deps): update dependency brakeman to v7.1.1 (#35434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 09:32:38 +00:00
renovate[bot]
24aa5d0460 fix(deps): update dependency @rails/ujs to v7.1.600 (#36634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 08:41:19 +00:00
Claire
5ac3cceaf5 Increase number of quote approval job retries (#36698) 2025-11-04 08:29:27 +00:00
github-actions[bot]
e5fbb49033 New Crowdin Translations (automated) (#36697)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-04 08:10:20 +00:00
Claire
310ae6317e Merge pull request #3262 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to d1f57822af
2025-11-03 21:31:16 +01:00
Claire
5c7d22e60a [Glitch] Disable paste-link-to-quote flow when composing Private Mentions
Port bae5877c84 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-03 21:09:40 +01:00
Claire
8d1208224f Merge commit 'd1f57822af0da2b2836e33baab5f89469ac6fa25' into glitch-soc/merge-upstream 2025-11-03 21:04:45 +01:00
Rachael Wright-Munn
d1f57822af Move "Privacy and reach" from "Public profile" to top-level navigation (#27294) 2025-11-03 17:56:17 +00:00
Claire
9b3e92bf17 Prevent creation of Private Mentions quoting someone who is not mentioned (#36689) 2025-11-03 14:16:25 +00:00
Claire
e79e42f8f1 Fix issuance of quote approval for remote private statuses (#36693) 2025-11-03 14:15:18 +00:00
Claire
bae5877c84 Disable paste-link-to-quote flow when composing Private Mentions (#36690) 2025-11-03 13:56:07 +00:00
github-actions[bot]
61c0daffc9 New Crowdin Translations (automated) (#36676)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-11-03 12:21:04 +00:00
renovate[bot]
f10c79c8d1 chore(deps): update dependency irb to v1.15.3 (#36682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 11:05:26 +00:00
renovate[bot]
8781abf2bd chore(deps): update dependency sass to v1.93.3 (#36674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 09:26:54 +00:00
renovate[bot]
7faf2eaa79 chore(deps): update dependency jsdom to v27.1.0 (#36663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 09:23:25 +00:00
renovate[bot]
0bf974a758 chore(deps): update dependency haml_lint to v0.67.0 (#36645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 09:21:51 +00:00
renovate[bot]
5c0c77223b chore(deps): update node.js to 24.11 (#36630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 09:21:07 +00:00
renovate[bot]
5fe74d2092 chore(deps): update dependency rubyzip to v3.2.2 (#36687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 09:09:54 +00:00
Claire
6dff6ae7f3 Merge pull request #3259 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 055f581ca5
2025-10-31 18:55:20 +01:00
diondiondion
2ab482da18 [Glitch] Fix initially selected language in Rules panel, hide selector when no alternative translations exist
Port 055f581ca5 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:15:48 +01:00
diondiondion
a0686536c6 [Glitch] Add separate translation key for "About this server" string
Port 499ddfe8e1 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:15:25 +01:00
diondiondion
3d80e8b021 [Glitch] Show error when submitting empty post rather than failing silently
Port fcecbf31ed to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:14:58 +01:00
Echo
43fbff50b5 [Glitch] Fix: Ensure carousel focuses on wrapper
Port 28cb345131 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:13:59 +01:00
Echo
9f8e812c56 [Glitch] Refactor carousel components
Port e7cd5a430e to glitch-soc

Co-authored-by: diondiondion <mail@diondiondion.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-31 18:13:35 +01:00
Claire
6ff4dad89d Merge commit '055f581ca58b62132c1eff23453031d9df9dbf0e' into glitch-soc/merge-upstream 2025-10-31 18:03:21 +01:00
diondiondion
055f581ca5 Fix initially selected language in Rules panel, hide selector when no alternative translations exist (#36672) 2025-10-31 14:20:59 +00:00
Claire
8a2826604c Bump version to v4.6.0-alpha.1 (#36667) 2025-10-31 13:24:26 +00:00
Matt Jankowski
d865a095d0 Update eslint-plugin-jsdoc to version 61.1.11 (#36653) 2025-10-31 11:07:48 +00:00
renovate[bot]
35abaa7ff1 chore(deps): update dependency axios to v1.13.1 (#36633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 10:31:57 +00:00
renovate[bot]
fd4e51b3d8 chore(deps): update dependency libvips to v8.17.3 (#36654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 10:30:55 +00:00
renovate[bot]
2c4367bcfc chore(deps): update dependency rubocop to v1.81.7 (#36662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 10:24:36 +00:00
github-actions[bot]
d47ca1cc36 New Crowdin Translations (automated) (#36660)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-10-31 10:24:16 +00:00
diondiondion
499ddfe8e1 Add separate translation key for "About this server" string (#36664) 2025-10-31 10:07:10 +00:00
Matt Jankowski
7b61ad936d Update playwright-ruby-client to version 1.56.0 (#36655) 2025-10-31 09:52:23 +00:00
diondiondion
fcecbf31ed Show error when submitting empty post rather than failing silently (#36650) 2025-10-30 19:29:25 +00:00
Matt Jankowski
aefd728309 Use before_action to protect hidden collections in following/followers lists (#35783) 2025-10-30 16:34:19 +00:00
renovate[bot]
13a070f8d1 chore(deps): update dependency webmock to v3.26.1 (#36648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 16:03:47 +00:00
Echo
28cb345131 Fix: Ensure carousel focuses on wrapper (#36649) 2025-10-30 13:08:24 +00:00
Echo
f3d9a4ed44 Add CSS Module support (#36637) 2025-10-30 12:30:42 +00:00
Claire
762e87b121 Fix SMTP configuration with mail 2.9.0 (#36646) 2025-10-30 11:21:32 +00:00
Joshua Byrd
e5e9f8da93 Fix og:images from The Guardian (and possibly other CDNs that check URL hashes) (#36139)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2025-10-30 10:57:07 +00:00
Echo
ff1e19a506 Silence Storybook build warning (#36647) 2025-10-30 10:51:34 +00:00
renovate[bot]
2c5d3f934c chore(deps): update dependency oj to v3.16.12 (#36644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 08:13:50 +00:00
github-actions[bot]
a77038b288 New Crowdin Translations (automated) (#36641)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-10-30 07:52:39 +00:00
Dima
ebf5cee38e Fix media URL inconsistency when deleting statuses via API (#35880) 2025-10-29 16:46:16 +00:00
Echo
e7cd5a430e Refactor carousel components (#36425)
Co-authored-by: diondiondion <mail@diondiondion.com>
2025-10-29 14:44:46 +00:00
917 changed files with 17003 additions and 16913 deletions

View File

@@ -73,7 +73,7 @@ services:
hard: -1
libretranslate:
image: libretranslate/libretranslate:v1.6.2
image: libretranslate/libretranslate:v1.7.3
restart: unless-stopped
volumes:
- lt-data:/home/libretranslate/.local

View File

@@ -9,7 +9,7 @@ runs:
using: 'composite'
steps:
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'

View File

@@ -5,7 +5,6 @@
'customManagers:dockerfileVersions',
':labels(dependencies)',
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
':enableVulnerabilityAlertsWithLabel(security)',
],
rebaseWhen: 'conflicted',
@@ -23,8 +22,6 @@
// Require Dependency Dashboard Approval for major version bumps of these node packages
matchManagers: ['npm'],
matchPackageNames: [
'tesseract.js', // Requires code changes
// react-router: Requires manual upgrade
'history',
'react-router-dom',

View File

@@ -35,7 +35,7 @@ jobs:
- linux/arm64
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Prepare
env:
@@ -100,7 +100,7 @@ jobs:
- name: Upload digest
if: ${{ inputs.push_to_images != '' }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
# `hashFiles` is used to disambiguate between streaming and non-streaming images
name: digests-${{ hashFiles(inputs.file_to_build) }}-${{ env.PLATFORM_PAIR }}
@@ -119,10 +119,10 @@ jobs:
PUSH_TO_IMAGES: ${{ inputs.push_to_images }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v6
with:
path: ${{ runner.temp }}/digests
# `hashFiles` is used to disambiguate between streaming and non-streaming images

View File

@@ -18,7 +18,7 @@ jobs:
steps:
# Repository needs to be cloned so `git rev-parse` below works
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- id: version_vars
run: |
echo mastodon_version_metadata=pr-${{ github.event.pull_request.number }}-$(git rev-parse --short ${{github.event.pull_request.head.sha}}) >> $GITHUB_OUTPUT

View File

@@ -9,44 +9,7 @@ permissions:
packages: write
jobs:
check-latest-stable:
runs-on: ubuntu-latest
outputs:
latest: ${{ steps.check.outputs.is_latest_stable }}
steps:
# Repository needs to be cloned to list branches
- name: Clone repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check latest stable
shell: bash
id: check
run: |
ref="${GITHUB_REF#refs/tags/}"
if [[ "$ref" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?$ ]]; then
current="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
else
echo "tag $ref is not semver"
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
exit 0
fi
latest=$(git for-each-ref --format='%(refname:short)' "refs/remotes/origin/stable-*.*" \
| sed -E 's#^origin/stable-##' \
| sort -Vr \
| head -n1)
if [[ "$current" == "$latest" ]]; then
echo "is_latest_stable=true" >> "$GITHUB_OUTPUT"
else
echo "is_latest_stable=false" >> "$GITHUB_OUTPUT"
fi
build-image:
needs: check-latest-stable
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: Dockerfile
@@ -57,14 +20,13 @@ jobs:
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ needs.check-latest-stable.outputs.latest }}
latest=${{ startsWith(github.ref, 'refs/tags/v4.5.') }}
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
secrets: inherit
build-image-streaming:
needs: check-latest-stable
uses: ./.github/workflows/build-container-image.yml
with:
file_to_build: streaming/Dockerfile
@@ -75,7 +37,7 @@ jobs:
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ needs.check-latest-stable.outputs.latest }}
latest=${{ startsWith(github.ref, 'refs/tags/v4.5.') }}
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}

View File

@@ -28,7 +28,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Ruby
uses: ruby/setup-ruby@v1

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Ruby environment
uses: ./.github/actions/setup-ruby

View File

@@ -1,31 +1,51 @@
name: 'Chromatic'
permissions:
contents: read
on:
push:
branches-ignore:
- renovate/*
- stable-*
paths:
- 'package.json'
- 'yarn.lock'
- '**/*.js'
- '**/*.jsx'
- '**/*.ts'
- '**/*.tsx'
- '**/*.css'
- '**/*.scss'
- '.github/workflows/chromatic.yml'
jobs:
pathcheck:
name: Check for relevant changes
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.filter.outputs.src }}
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
src:
- 'package.json'
- 'yarn.lock'
- '**/*.js'
- '**/*.jsx'
- '**/*.ts'
- '**/*.tsx'
- '**/*.css'
- '**/*.scss'
- '.github/workflows/chromatic.yml'
chromatic:
name: Run Chromatic
runs-on: ubuntu-latest
if: github.repository == 'mastodon/mastodon'
needs: pathcheck
if: github.repository == 'mastodon/mastodon' && needs.pathcheck.outputs.changed == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
@@ -33,9 +53,10 @@ jobs:
run: yarn build-storybook
- name: Run Chromatic
uses: chromaui/action@v12
uses: chromaui/action@v13
with:
# ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
zip: true
storybookBuildDir: 'storybook-static'
exitZeroOnChanges: false # Fail workflow if changes are found
autoAcceptChanges: 'main' # Auto-accept changes on main branch only

View File

@@ -31,11 +31,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -61,6 +61,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: '/language:${{matrix.language}}'

View File

@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Increase Git http.postBuffer
# This is needed due to a bug in Ubuntu's cURL version?

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Increase Git http.postBuffer
# This is needed due to a bug in Ubuntu's cURL version?

View File

@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: crowdin action
uses: crowdin/github-action@v2

View File

@@ -13,7 +13,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript

View File

@@ -34,7 +34,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript

View File

@@ -33,7 +33,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Ruby
uses: ruby/setup-ruby@v1

View File

@@ -38,7 +38,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript

View File

@@ -35,7 +35,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Ruby
uses: ruby/setup-ruby@v1

View File

@@ -34,7 +34,7 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript

View File

@@ -72,7 +72,7 @@ jobs:
BUNDLE_RETRY: 3
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Ruby environment
uses: ./.github/actions/setup-ruby

View File

@@ -32,7 +32,7 @@ jobs:
SECRET_KEY_BASE_DUMMY: 1
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Ruby environment
uses: ./.github/actions/setup-ruby
@@ -65,7 +65,7 @@ jobs:
run: |
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tmp/cache/vite/last-build*.json
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: matrix.mode == 'test'
with:
path: |-
@@ -128,9 +128,9 @@ jobs:
- '3.3'
- '.ruby-version'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v6
with:
path: './'
name: ${{ github.sha }}
@@ -230,9 +230,9 @@ jobs:
- '3.3'
- '.ruby-version'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v6
with:
path: './'
name: ${{ github.sha }}
@@ -309,9 +309,9 @@ jobs:
- '.ruby-version'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v6
with:
path: './'
name: ${{ github.sha }}
@@ -350,14 +350,14 @@ jobs:
- run: bin/rspec spec/system --tag streaming --tag js
- name: Archive logs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: failure()
with:
name: e2e-logs-${{ matrix.ruby-version }}
path: log/
- name: Archive test screenshots
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: failure()
with:
name: e2e-screenshots-${{ matrix.ruby-version }}
@@ -447,9 +447,9 @@ jobs:
search-image: opensearchproject/opensearch:2
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v6
with:
path: './'
name: ${{ github.sha }}
@@ -469,14 +469,14 @@ jobs:
- run: bin/rspec --tag search
- name: Archive logs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: failure()
with:
name: test-search-logs-${{ matrix.ruby-version }}
path: log/
- name: Archive test screenshots
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: failure()
with:
name: test-search-screenshots

2
.nvmrc
View File

@@ -1 +1 @@
24.10
24.11

View File

@@ -95,6 +95,7 @@ AUTHORS.md
# Ignore glitch-soc vendored CSS reset
app/javascript/flavours/glitch/styles/reset.scss
app/javascript/flavours/glitch/styles_new/mastodon/reset.scss
# Ignore win95 theme
app/javascript/styles/win95.scss

View File

@@ -31,7 +31,7 @@ const config: StorybookConfig = {
viteFinal(config) {
// For an unknown reason, Storybook does not use the root
// from the Vite config so we need to set it manually.
config.root = resolve(__dirname, '../app/javascript');
config.root = resolve(import.meta.dirname, '../app/javascript');
return config;
},
};

View File

@@ -1,2 +1,2 @@
<html class="no-reduce-motion">
<html class="no-reduce-motion theme-light">
</html>

View File

@@ -7,7 +7,7 @@
* - Please do NOT modify this file.
*/
const PACKAGE_VERSION = '2.11.3'
const PACKAGE_VERSION = '2.12.1'
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
@@ -205,6 +205,7 @@ async function resolveMainClient(event) {
* @param {FetchEvent} event
* @param {Client | undefined} client
* @param {string} requestId
* @param {number} requestInterceptedAt
* @returns {Promise<Response>}
*/
async function getResponse(event, client, requestId, requestInterceptedAt) {

View File

@@ -1,13 +0,0 @@
diff --git a/lib/index.js b/lib/index.js
index 16ed6be8be8f555cc99096c2ff60954b42dc313d..d009c069770d066ad0db7ad02de1ea473a29334e 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -99,7 +99,7 @@ function lodash(_ref) {
var node = _ref3;
- if ((0, _types.isModuleDeclaration)(node)) {
+ if ((0, _types.isImportDeclaration)(node) || (0, _types.isExportDeclaration)(node)) {
isModule = true;
break;
}

View File

@@ -538,7 +538,7 @@ and provided thanks to the work of the following contributors:
* [Drew Schuster](mailto:dtschust@gmail.com)
* [Dryusdan](mailto:dryusdan@dryusdan.fr)
* [Eai](mailto:eai@mizle.net)
* [Eashwar Ranganathan](mailto:eranganathan@lyft.com)
* [Eashwar Ranganathan](mailto:eashwar@eashwar.com)
* [Ed Knutson](mailto:knutsoned@gmail.com)
* [Elizabeth Martín Campos](mailto:me@elizabeth.sh)
* [Elizabeth Myers](mailto:elizabeth@interlinked.me)

View File

@@ -2,78 +2,6 @@
All notable changes to this project will be documented in this file.
## [4.5.5] - 2026-01-20
### Security
- Fix missing limits on various federated properties [GHSA-gg8q-rcg7-p79g](https://github.com/mastodon/mastodon/security/advisories/GHSA-gg8q-rcg7-p79g)
- Fix remote user suspension bypass [GHSA-5h2f-wg8j-xqwp](https://github.com/mastodon/mastodon/security/advisories/GHSA-5h2f-wg8j-xqwp)
- Fix missing length limits on some user-provided fields [GHSA-6x3w-9g92-gvf3](https://github.com/mastodon/mastodon/security/advisories/GHSA-6x3w-9g92-gvf3)
- Fix missing access check for push notification settings update [GHSA-f3q8-7vw3-69v4](https://github.com/mastodon/mastodon/security/advisories/GHSA-f3q8-7vw3-69v4)
### Changed
- Skip tombstone creation on deleting from 404 (#37533 by @ClearlyClaire)
### Fixed
- Fix potential duplicate handling of quote accept/reject/delete (#37537 by @ClearlyClaire)
- Fix `FeedManager#filter_from_home` error when handling a reblog of a deleted status (#37486 by @ClearlyClaire)
- Fix needlessly complicated SQL query in status batch removal (#37469 by @ClearlyClaire)
- Fix `quote_approval_policy` being reset to user defaults when omitted in status update (#37436 and #37474 by @mjankowski and @shleeable)
- Fix `Vary` parsing in cache control enforcement (#37426 by @MegaManSec)
- Fix missing URI scheme test in `QuoteRequest` handling (#37425 by @MegaManSec)
- Fix thread-unsafe ActivityPub activity dispatch (#37423 by @MegaManSec)
- Fix URI generation for reblogs by accounts with numerical ActivityPub identifiers (#37415 by @oneiros)
- Fix SignatureParser accepting duplicate parameters in HTTP Signature header (#37375 by @shleeable)
- Fix emoji with variant selector not being rendered properly (#37320 by @ChaosExAnima)
- Fix mobile admin sidebar displaying under batch table toolbar (#37307 by @diondiondion)
## [4.5.4] - 2026-01-07
### Security
- Fix SSRF protection bypass ([GHSA](https://github.com/mastodon/mastodon/security/advisories/GHSA-xfrj-c749-jxxq))
- Fix missing ownership check in severed relationships controller ([GHSA](https://github.com/mastodon/mastodon/security/advisories/GHSA-ww85-x9cp-5v24))
### Changed
- Change HTTP Signature verification status from 401 to 503 on temporary failure to get remote actor (#37221 by @ClearlyClaire)
### Fixed
- Fix custom emojis not being rendered in profile fields (#37365 by @ClearlyClaire)
- Fix serialization of context pages (#37376 by @ClearlyClaire)
- Fix quotes with CWs but no text not having fallback link (#37361 by @ClearlyClaire)
- Fix outdated link target for “locked” warning (#37366 by @ClearlyClaire)
- Fix local custom emojis sometimes being rendered in remote posts (#37284 by @ChaosExAnima)
- Fix some assets not being loaded from configured CDN (#37310 by @ChaosExAnima)
- Fix notifications page error in Tor browser (#37285 by @diondiondion)
- Fix custom emojis not being displayed in CWs and fav/boost notifications (#37272 and #37306 by @ChaosExAnima and @ClearlyClaire)
- Fix default `Admin` role not including `view_feeds` permission (#37301 by @ClearlyClaire)
- Fix hashtag autocomplete replacing suggestion's first characters with input (#37281 by @ClearlyClaire)
- Fix mentions of domain-blocked users being processed (#37257 by @ClearlyClaire)
## [4.5.3] - 2025-12-08
### Security
- Fix inconsistent error handling leaking information on existence of private posts ([GHSA-gwhw-gcjx-72v8](https://github.com/mastodon/mastodon/security/advisories/GHSA-gwhw-gcjx-72v8))
### Fixed
- Fix “Delete and Redraft” on a non-quote being treated as a quote post in some cases (#37140 by @ClearlyClaire)
- Fix YouTube embeds by sending referer (#37126 by @ChaosExAnima)
- Fix streamed quoted polls not being hydrated correctly (#37118 by @ClearlyClaire)
- Fix creation of duplicate conversations (#37108 by @oneiros)
- Fix extraneous `noreferrer` in external links (#37107 by @ChaosExAnima)
- Fix edge case error handling in some database migrations (#37079 by @ClearlyClaire)
- Fix error handling when re-fetching already-known statuses (#37077 by @ClearlyClaire)
- Fix post navigation in single-column mode when Advanced UI is enabled (#37044 by @diondiondion)
- Fix `tootctl status remove` removing quoted posts and remote quotes of local posts (#37009 by @ClearlyClaire)
- Fix known expensive S3 batch delete operation failing because of short timeouts (#37004 by @ClearlyClaire)
- Fix compose autosuggest always lowercasing input token (#36995 by @ClearlyClaire)
## [4.5.2] - 2025-11-20
### Changed

View File

@@ -48,22 +48,3 @@ Mastodon requires all `POST` requests to be signed, and MAY require `GET` reques
### Additional documentation
- [Mastodon documentation](https://docs.joinmastodon.org/)
## Size limits
Mastodon imposes a few hard limits on federated content.
These limits are intended to be very generous and way above what the Mastodon user experience is optimized for, so as to accomodate future changes and unusual or unforeseen usage patterns, while still providing some limits for performance reasons.
The following table attempts to summary those limits.
| Limited property | Size limit | Consequence of exceeding the limit |
| ------------------------------------------------------------- | ---------- | ---------------------------------- |
| Serialized JSON-LD | 1MB | **Activity is rejected/dropped** |
| Profile fields (actor `PropertyValue` attachments) name/value | 2047 | Field name/value is truncated |
| Number of profile fields (actor `PropertyValue` attachments) | 50 | Fields list is truncated |
| Poll options (number of `anyOf`/`oneOf` in a `Question`) | 500 | Items list is truncated |
| Account username (actor `preferredUsername`) length | 2048 | **Actor will be rejected** |
| Account display name (actor `name`) length | 2048 | Display name will be truncated |
| Account note (actor `summary`) length | 20kB | Account note will be truncated |
| Account `attributionDomains` | 256 | List will be truncated |
| Account aliases (actor `alsoKnownAs`) | 256 | List will be truncated |
| Custom emoji shortcode (`Emoji` `name`) | 2048 | Emoji will be rejected |

12
Gemfile
View File

@@ -13,7 +13,7 @@ gem 'haml-rails', '~>3.0'
gem 'pg', '~> 1.5'
gem 'pghero'
gem 'aws-sdk-core', '< 3.216.0', require: false # TODO: https://github.com/mastodon/mastodon/pull/34173#issuecomment-2733378873
gem 'aws-sdk-core', require: false
gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'blurhash', '~> 0.1'
gem 'fog-core', '<= 2.6.0'
@@ -24,7 +24,7 @@ gem 'ruby-vips', '~> 2.2', require: false
gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.18.0', require: false
gem 'bootsnap', '~> 1.19.0', require: false
gem 'browser'
gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.3'
@@ -40,7 +40,7 @@ gem 'net-ldap', '~> 0.18'
gem 'omniauth', '~> 2.0'
gem 'omniauth-cas', '~> 3.0.0.beta.1'
gem 'omniauth_openid_connect', '~> 0.8.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
gem 'omniauth-rails_csrf_protection', '~> 2.0'
gem 'omniauth-saml', '~> 2.0'
gem 'color_diff', '~> 0.1'
@@ -71,7 +71,7 @@ gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'
gem 'parslet'
gem 'premailer-rails'
gem 'public_suffix', '~> 6.0'
gem 'public_suffix', '~> 7.0'
gem 'pundit', '~> 2.3'
gem 'rack-attack', '~> 6.6'
gem 'rack-cors', require: 'rack/cors'
@@ -114,7 +114,7 @@ group :opentelemetry do
gem 'opentelemetry-instrumentation-http', '~> 0.27.0', require: false
gem 'opentelemetry-instrumentation-http_client', '~> 0.26.0', require: false
gem 'opentelemetry-instrumentation-net_http', '~> 0.26.0', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.32.0', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.34.0', require: false
gem 'opentelemetry-instrumentation-rack', '~> 0.29.0', require: false
gem 'opentelemetry-instrumentation-rails', '~> 0.39.0', require: false
gem 'opentelemetry-instrumentation-redis', '~> 0.28.0', require: false
@@ -138,7 +138,7 @@ group :test do
# Browser integration testing
gem 'capybara', '~> 3.39'
gem 'capybara-playwright-driver'
gem 'playwright-ruby-client', '1.55.0', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package
gem 'playwright-ruby-client', '1.56.0', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package
# Used to reset the database between system tests
gem 'database_cleaner-active_record'

View File

@@ -86,8 +86,8 @@ GEM
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
addressable (2.8.8)
public_suffix (>= 2.0.2, < 8.0)
aes_key_wrap (1.1.0)
android_key_attestation (0.3.0)
annotaterb (4.20.0)
@@ -96,17 +96,20 @@ GEM
ast (2.4.3)
attr_required (1.0.2)
aws-eventstream (1.4.0)
aws-partitions (1.1168.0)
aws-sdk-core (3.215.1)
aws-partitions (1.1190.0)
aws-sdk-core (3.239.2)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.96.0)
aws-sdk-core (~> 3, >= 3.210.0)
logger
aws-sdk-kms (1.118.0)
aws-sdk-core (~> 3, >= 3.239.1)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.177.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-s3 (1.206.0)
aws-sdk-core (~> 3, >= 3.234.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
@@ -126,14 +129,14 @@ GEM
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
blurhash (0.1.8)
bootsnap (1.18.6)
bootsnap (1.19.0)
msgpack (~> 1.2)
brakeman (7.1.1)
racc
browser (6.2.0)
builder (3.3.0)
bundler-audit (0.9.2)
bundler (>= 1.2.0, < 3)
bundler-audit (0.9.3)
bundler (>= 1.2.0)
thor (~> 1.0)
capybara (3.40.0)
addressable
@@ -164,7 +167,7 @@ GEM
cocoon (1.2.15)
color_diff (0.1)
concurrent-ruby (1.3.5)
connection_pool (2.5.4)
connection_pool (2.5.5)
cose (1.3.1)
cbor (~> 0.5.9)
openssl-signature_algorithm (~> 1.0)
@@ -179,7 +182,7 @@ GEM
activerecord (>= 5.a)
database_cleaner-core (~> 2.0)
database_cleaner-core (2.0.1)
date (3.4.1)
date (3.5.0)
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
@@ -231,7 +234,7 @@ GEM
excon (1.3.0)
logger
fabrication (3.0.0)
faker (3.5.2)
faker (3.5.3)
i18n (>= 1.8.11, < 2)
faraday (2.14.0)
faraday-net_http (>= 2.0, < 3.5)
@@ -279,7 +282,7 @@ GEM
rake (>= 13)
googleapis-common-protos-types (1.22.0)
google-protobuf (~> 4.26)
haml (6.3.0)
haml (6.4.0)
temple (>= 0.8.2)
thor
tilt
@@ -288,7 +291,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.66.0)
haml_lint (0.67.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
@@ -301,8 +304,8 @@ GEM
highline (3.1.2)
reline
hiredis (0.6.3)
hiredis-client (0.26.1)
redis-client (= 0.26.1)
hiredis-client (0.26.2)
redis-client (= 0.26.2)
hkdf (0.3.0)
htmlentities (4.3.4)
http (5.3.1)
@@ -321,13 +324,14 @@ GEM
rainbow (>= 2.0.0)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
i18n-tasks (1.0.15)
i18n-tasks (1.1.2)
activesupport (>= 4.0.2)
ast (>= 2.1.0)
erubi
highline (>= 2.0.0)
highline (>= 3.0.0)
i18n
parser (>= 3.2.2.1)
prism
rails-i18n
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.8, >= 1.8.1)
@@ -346,7 +350,7 @@ GEM
azure-blob (~> 0.5.2)
hashie (~> 5.0)
jmespath (1.6.2)
json (2.15.1)
json (2.16.0)
json-canonicalization (1.0.0)
json-jwt (1.17.0)
activesupport (>= 4.2)
@@ -443,7 +447,7 @@ GEM
mime-types-data (3.2025.0924)
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (5.26.0)
minitest (5.26.2)
msgpack (1.8.0)
multi_json (1.17.0)
mutex_m (0.3.0)
@@ -465,7 +469,7 @@ GEM
nokogiri (1.18.10)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oj (3.16.11)
oj (3.16.13)
bigdecimal (>= 3.0)
ostruct (>= 0.2)
omniauth (2.1.4)
@@ -477,7 +481,7 @@ GEM
addressable (~> 2.8)
nokogiri (~> 1.12)
omniauth (~> 2.1)
omniauth-rails_csrf_protection (1.0.2)
omniauth-rails_csrf_protection (2.0.0)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth-saml (2.2.4)
@@ -512,9 +516,9 @@ GEM
opentelemetry-common (~> 0.20)
opentelemetry-sdk (~> 1.10)
opentelemetry-semantic_conventions
opentelemetry-helpers-sql (0.2.0)
opentelemetry-helpers-sql (0.3.0)
opentelemetry-api (~> 1.7)
opentelemetry-helpers-sql-obfuscation (0.4.0)
opentelemetry-helpers-sql-processor (0.3.1)
opentelemetry-common (~> 0.21)
opentelemetry-instrumentation-action_mailer (0.6.1)
opentelemetry-instrumentation-active_support (~> 0.10)
@@ -538,19 +542,19 @@ GEM
opentelemetry-registry (~> 0.1)
opentelemetry-instrumentation-concurrent_ruby (0.24.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-excon (0.26.0)
opentelemetry-instrumentation-excon (0.26.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-faraday (0.30.0)
opentelemetry-instrumentation-faraday (0.30.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-http (0.27.0)
opentelemetry-instrumentation-http (0.27.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-http_client (0.26.0)
opentelemetry-instrumentation-http_client (0.26.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-net_http (0.26.0)
opentelemetry-instrumentation-net_http (0.26.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-pg (0.32.0)
opentelemetry-instrumentation-pg (0.34.1)
opentelemetry-helpers-sql
opentelemetry-helpers-sql-obfuscation
opentelemetry-helpers-sql-processor
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-rack (0.29.0)
opentelemetry-instrumentation-base (~> 0.25)
@@ -565,7 +569,7 @@ GEM
opentelemetry-instrumentation-concurrent_ruby (~> 0.23)
opentelemetry-instrumentation-redis (0.28.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-sidekiq (0.28.0)
opentelemetry-instrumentation-sidekiq (0.28.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-registry (0.4.0)
opentelemetry-api (~> 1.1)
@@ -581,7 +585,7 @@ GEM
ox (2.14.23)
bigdecimal (>= 3.0)
parallel (1.27.0)
parser (3.3.9.0)
parser (3.3.10.0)
ast (~> 2.4.1)
racc
parslet (2.0.0)
@@ -590,7 +594,7 @@ GEM
pg (1.6.2)
pghero (3.7.0)
activerecord (>= 7.1)
playwright-ruby-client (1.55.0)
playwright-ruby-client (1.56.0)
concurrent-ruby (>= 1.1.6)
mime-types (>= 3.0)
pp (0.6.3)
@@ -604,8 +608,8 @@ GEM
net-smtp
premailer (~> 1.7, >= 1.7.9)
prettyprint (0.2.0)
prism (1.5.2)
prometheus_exporter (2.3.0)
prism (1.6.0)
prometheus_exporter (2.3.1)
webrick
propshaft (1.3.1)
actionpack (>= 7.0.0)
@@ -614,7 +618,7 @@ GEM
psych (5.2.6)
date
stringio
public_suffix (6.0.2)
public_suffix (7.0.0)
puma (7.1.0)
nio4r (~> 2.0)
pundit (2.5.2)
@@ -634,7 +638,7 @@ GEM
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (4.1.1)
rack-protection (4.2.1)
base64 (>= 0.1.0)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
@@ -668,7 +672,7 @@ GEM
rails-html-sanitizer (1.6.2)
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails-i18n (8.0.2)
rails-i18n (8.1.0)
i18n (>= 0.7, < 2)
railties (>= 8.0.0, < 9)
railties (8.0.3)
@@ -681,7 +685,7 @@ GEM
tsort (>= 0.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.3.0)
rake (13.3.1)
rdf (3.3.4)
bcp47_spec (~> 0.2)
bigdecimal (~> 3.1, >= 3.1.5)
@@ -699,10 +703,10 @@ GEM
reline
redcarpet (3.6.1)
redis (4.8.1)
redis-client (0.26.1)
redis-client (0.26.2)
connection_pool
regexp_parser (2.11.3)
reline (0.6.2)
reline (0.6.3)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
@@ -713,10 +717,10 @@ GEM
rotp (6.3.0)
rouge (4.6.1)
rpam2 (4.0.2)
rqrcode (3.1.0)
rqrcode (3.1.1)
chunky_png (~> 1.0)
rqrcode_core (~> 2.0)
rqrcode_core (2.0.0)
rqrcode_core (2.0.1)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
@@ -745,7 +749,7 @@ GEM
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 9)
rspec-support (3.13.6)
rubocop (1.81.6)
rubocop (1.81.7)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -756,7 +760,7 @@ GEM
rubocop-ast (>= 1.47.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.47.1)
rubocop-ast (1.48.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-capybara (2.22.1)
@@ -775,10 +779,10 @@ GEM
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rspec (3.7.0)
rubocop-rspec (3.8.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec_rails (2.31.0)
rubocop (~> 1.81)
rubocop-rspec_rails (2.32.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
@@ -803,9 +807,9 @@ GEM
activerecord (>= 4.0.0)
railties (>= 4.0.0)
securerandom (0.4.1)
shoulda-matchers (6.5.0)
activesupport (>= 5.2.0)
sidekiq (8.0.9)
shoulda-matchers (7.0.1)
activesupport (>= 7.1)
sidekiq (8.0.10)
connection_pool (>= 2.5.0)
json (>= 2.9.0)
logger (>= 1.6.2)
@@ -835,9 +839,9 @@ GEM
stackprof (0.2.27)
starry (0.2.0)
base64
stoplight (5.4.0)
stoplight (5.6.0)
zeitwerk
stringio (3.1.7)
stringio (3.1.8)
strong_migrations (2.5.1)
activerecord (>= 7.1)
swd (2.0.3)
@@ -883,7 +887,7 @@ GEM
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)
uri (1.0.4)
uri (1.1.1)
useragent (0.16.11)
validate_url (1.0.15)
activemodel (>= 3.0.0)
@@ -911,7 +915,7 @@ GEM
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webmock (3.26.0)
webmock (3.26.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -933,12 +937,12 @@ DEPENDENCIES
active_model_serializers (~> 0.10)
addressable (~> 2.8)
annotaterb (~> 4.13)
aws-sdk-core (< 3.216.0)
aws-sdk-core
aws-sdk-s3 (~> 1.123)
better_errors (~> 2.9)
binding_of_caller (~> 1.0)
blurhash (~> 0.1)
bootsnap (~> 1.18.0)
bootsnap (~> 1.19.0)
brakeman (~> 7.0)
browser
bundler-audit (~> 0.9)
@@ -1005,7 +1009,7 @@ DEPENDENCIES
oj (~> 3.14)
omniauth (~> 2.0)
omniauth-cas (~> 3.0.0.beta.1)
omniauth-rails_csrf_protection (~> 1.0)
omniauth-rails_csrf_protection (~> 2.0)
omniauth-saml (~> 2.0)
omniauth_openid_connect (~> 0.8.0)
opentelemetry-api (~> 1.7.0)
@@ -1018,7 +1022,7 @@ DEPENDENCIES
opentelemetry-instrumentation-http (~> 0.27.0)
opentelemetry-instrumentation-http_client (~> 0.26.0)
opentelemetry-instrumentation-net_http (~> 0.26.0)
opentelemetry-instrumentation-pg (~> 0.32.0)
opentelemetry-instrumentation-pg (~> 0.34.0)
opentelemetry-instrumentation-rack (~> 0.29.0)
opentelemetry-instrumentation-rails (~> 0.39.0)
opentelemetry-instrumentation-redis (~> 0.28.0)
@@ -1028,11 +1032,11 @@ DEPENDENCIES
parslet
pg (~> 1.5)
pghero
playwright-ruby-client (= 1.55.0)
playwright-ruby-client (= 1.56.0)
premailer-rails
prometheus_exporter (~> 2.2)
propshaft
public_suffix (~> 6.0)
public_suffix (~> 7.0)
puma (~> 7.0)
pundit (~> 2.3)
rack-attack (~> 6.6)

View File

@@ -18,4 +18,5 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
| 4.5.x | Yes |
| 4.4.x | Yes |
| 4.3.x | Until 2026-05-06 |
| < 4.3 | No |
| 4.2.x | Until 2026-01-08 |
| < 4.2 | No |

View File

@@ -36,8 +36,9 @@ class ActivityPub::ContextsController < ActivityPub::BaseController
def context_presenter
first_page = ActivityPub::CollectionPresenter.new(
id: items_context_url(@conversation, page_params),
type: :unordered,
part_of: context_url(@conversation),
part_of: items_context_url(@conversation),
next: next_page,
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
)
@@ -51,7 +52,7 @@ class ActivityPub::ContextsController < ActivityPub::BaseController
page = ActivityPub::CollectionPresenter.new(
id: items_context_url(@conversation, page_params),
type: :unordered,
part_of: context_url(@conversation),
part_of: items_context_url(@conversation),
next: next_page,
items: @items.map { |status| status.local? ? ActivityPub::TagManager.instance.uri_for(status) : status.uri }
)

View File

@@ -3,7 +3,6 @@
class ActivityPub::InboxesController < ActivityPub::BaseController
include JsonLdHelper
before_action :skip_large_payload
before_action :skip_unknown_actor_activity
before_action :require_actor_signature!
skip_before_action :authenticate_user!
@@ -17,10 +16,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
private
def skip_large_payload
head 413 if request.content_length > ActivityPub::Activity::MAX_JSON_SIZE
end
def skip_unknown_actor_activity
head 202 if unknown_affected_account?
end

View File

@@ -5,6 +5,15 @@ module Admin
def index
authorize :custom_emoji, :index?
# If filtering by local emojis, remove by_domain filter.
params.delete(:by_domain) if params[:local].present?
# If filtering by domain, ensure remote filter is set.
if params[:by_domain].present?
params.delete(:local)
params[:remote] = '1'
end
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
@form = Form::CustomEmojiBatch.new
end

View File

@@ -9,7 +9,7 @@ module Admin
@site_upload.destroy!
redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
redirect_back_or_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
end
private

View File

@@ -1,10 +1,12 @@
# frozen_string_literal: true
class Api::V1::AnnualReportsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
include AsyncRefreshesConcern
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:read, :generate]
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:read, :generate]
before_action :require_user!
before_action :set_annual_report, except: :index
before_action :set_annual_report, only: [:show, :read]
def index
with_read_replica do
@@ -28,14 +30,59 @@ class Api::V1::AnnualReportsController < Api::BaseController
relationships: @relationships
end
def state
render json: { state: report_state }
end
def generate
return render_empty unless year == AnnualReport.current_campaign
return render_empty if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year)
async_refresh = AsyncRefresh.new(refresh_key)
if async_refresh.running?
add_async_refresh_header(async_refresh, retry_seconds: 2)
return head 202
end
add_async_refresh_header(AsyncRefresh.create(refresh_key), retry_seconds: 2)
GenerateAnnualReportWorker.perform_async(current_account.id, year)
head 202
end
def read
@annual_report.view!
render_empty
end
def refresh_key
"wrapstodon:#{current_account.id}:#{year}"
end
private
def report_state
return 'available' if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year)
async_refresh = AsyncRefresh.new(refresh_key)
if async_refresh.running?
add_async_refresh_header(async_refresh, retry_seconds: 2)
'generating'
elsif AnnualReport.current_campaign == year && AnnualReport.new(current_account, year).eligible?
'eligible'
else
'ineligible'
end
end
def year
params[:id]&.to_i
end
def set_annual_report
@annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: params[:id])
@annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: year)
end
end

View File

@@ -107,7 +107,9 @@ class Api::V1::StatusesController < Api::BaseController
@status = Status.where(account: current_account).find(params[:id])
authorize @status, :update?
update_options = {
UpdateStatusService.new.call(
@status,
current_account.id,
text: status_params[:status],
media_ids: status_params[:media_ids],
media_attributes: status_params[:media_attributes],
@@ -115,12 +117,9 @@ class Api::V1::StatusesController < Api::BaseController
language: status_params[:language],
spoiler_text: status_params[:spoiler_text],
poll: status_params[:poll],
content_type: status_params[:content_type],
}
update_options[:quote_approval_policy] = quote_approval_policy if status_params[:quote_approval_policy].present?
UpdateStatusService.new.call(@status, current_account.id, update_options)
quote_approval_policy: quote_approval_policy,
content_type: status_params[:content_type]
)
render json: @status, serializer: REST::StatusSerializer
end
@@ -129,10 +128,11 @@ class Api::V1::StatusesController < Api::BaseController
@status = Status.where(account: current_account).find(params[:id])
authorize @status, :destroy?
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
@status.discard_with_reblogs
StatusPin.find_by(status: @status)&.destroy
@status.account.statuses_count = @status.account.statuses_count - 1
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })

View File

@@ -0,0 +1,114 @@
# frozen_string_literal: true
class Api::V1Alpha::CollectionsController < Api::BaseController
include Authorization
DEFAULT_COLLECTIONS_LIMIT = 40
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: ValidationErrorFormatter.new(e).as_json }, status: 422
end
before_action :check_feature_enabled
before_action -> { authorize_if_got_token! :read, :'read:collections' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create, :update, :destroy]
before_action :require_user!, only: [:create, :update, :destroy]
before_action :set_account, only: [:index]
before_action :set_collections, only: [:index]
before_action :set_collection, only: [:show, :update, :destroy]
after_action :insert_pagination_headers, only: [:index]
after_action :verify_authorized
def index
cache_if_unauthenticated!
authorize Collection, :index?
render json: @collections, each_serializer: REST::BaseCollectionSerializer
end
def show
cache_if_unauthenticated!
authorize @collection, :show?
render json: @collection, serializer: REST::CollectionSerializer
end
def create
authorize Collection, :create?
@collection = CreateCollectionService.new.call(collection_creation_params, current_user.account)
render json: @collection, serializer: REST::CollectionSerializer
end
def update
authorize @collection, :update?
@collection.update!(collection_update_params) # TODO: Create a service for this to federate changes
render json: @collection, serializer: REST::CollectionSerializer
end
def destroy
authorize @collection, :destroy?
@collection.destroy
head 200
end
private
def set_account
@account = Account.find(params[:account_id])
end
def set_collections
@collections = @account.collections
.with_tag
.order(created_at: :desc)
.offset(offset_param)
.limit(limit_param(DEFAULT_COLLECTIONS_LIMIT))
end
def set_collection
@collection = Collection.find(params[:id])
end
def collection_creation_params
params.permit(:name, :description, :sensitive, :discoverable, :tag_name, account_ids: [])
end
def collection_update_params
params.permit(:name, :description, :sensitive, :discoverable, :tag_name)
end
def check_feature_enabled
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
end
def next_path
return unless records_continue?
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param + limit_param(DEFAULT_COLLECTIONS_LIMIT)))
end
def prev_path
return if offset_param.zero?
api_v1_alpha_account_collections_url(@account, pagination_params(offset: offset_param - limit_param(DEFAULT_COLLECTIONS_LIMIT)))
end
def records_continue?
((offset_param * limit_param(DEFAULT_COLLECTIONS_LIMIT)) + @collections.size) < @account.collections.size
end
def offset_param
params[:offset].to_i
end
end

View File

@@ -62,7 +62,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
end
def set_push_subscription
@push_subscription = ::Web::PushSubscription.where(user_id: active_session.user_id).find(params[:id])
@push_subscription = ::Web::PushSubscription.find(params[:id])
end
def subscription_params

View File

@@ -135,7 +135,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
@accept_token = session[:accept_token] = SecureRandom.hex
@invite_code = invite_code
set_locale { render :rules }
render :rules
end
def is_flashing_format? # rubocop:disable Naming/PredicatePrefix

View File

@@ -19,7 +19,7 @@ module CacheConcern
# from being used as cache keys, while allowing to `Vary` on them (to not serve
# anonymous cached data to authenticated requests when authentication matters)
def enforce_cache_control!
vary = response.headers['Vary'].to_s.split(',').map { |x| x.strip.downcase }.reject(&:empty?)
vary = response.headers['Vary']&.split&.map { |x| x.strip.downcase }
return unless vary.present? && %w(cookie authorization signature).any? { |header| vary.include?(header) && request.headers[header].present? }
response.cache_control.replace(private: true, no_store: true)

View File

@@ -72,13 +72,10 @@ module SignatureVerification
rescue Mastodon::SignatureVerificationError => e
fail_with! e.message
rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
@signature_verification_failure_code ||= 503
fail_with! "Failed to fetch remote data: #{e.message}"
rescue Mastodon::UnexpectedResponseError
@signature_verification_failure_code ||= 503
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
rescue Stoplight::Error::RedLight
@signature_verification_failure_code ||= 503
fail_with! 'Fetching attempt skipped because of recent connection failure'
end

View File

@@ -7,6 +7,7 @@ class FollowerAccountsController < ApplicationController
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :protect_hidden_collections, if: -> { request.format.json? }
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :limited_federation_mode?
@@ -18,8 +19,6 @@ class FollowerAccountsController < ApplicationController
end
format.json do
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
render json: collection_presenter,
@@ -41,6 +40,10 @@ class FollowerAccountsController < ApplicationController
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
end
def protect_hidden_collections
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
end
def page_requested?
params[:page].present?
end

View File

@@ -7,6 +7,7 @@ class FollowingAccountsController < ApplicationController
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :protect_hidden_collections, if: -> { request.format.json? }
skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :limited_federation_mode?
@@ -18,11 +19,6 @@ class FollowingAccountsController < ApplicationController
end
format.json do
if page_requested? && @account.hide_collections?
forbidden
next
end
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
render json: collection_presenter,
@@ -44,6 +40,10 @@ class FollowingAccountsController < ApplicationController
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
end
def protect_hidden_collections
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
end
def page_requested?
params[:page].present?
end

View File

@@ -26,7 +26,7 @@ class SeveredRelationshipsController < ApplicationController
private
def set_event
@event = AccountRelationshipSeveranceEvent.where(account: current_account).find(params[:id])
@event = AccountRelationshipSeveranceEvent.find(params[:id])
end
def following_data

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
class WrapstodonController < ApplicationController
include WebAppControllerConcern
include Authorization
include AccountOwnedConcern
vary_by 'Accept, Accept-Language, Cookie'
before_action :set_generated_annual_report
skip_before_action :require_functional!, only: :show, unless: :limited_federation_mode?
def show
expires_in 10.seconds, public: true if current_account.nil?
end
private
def set_generated_annual_report
@generated_annual_report = GeneratedAnnualReport.find_by!(account: @account, year: params[:year], share_key: params[:share_key])
end
end

View File

@@ -153,11 +153,9 @@ module ApplicationHelper
tag.meta(content: content, property: property)
end
def body_classes
def html_classes
output = []
output << content_for(:body_classes)
output << "flavour-#{current_flavour.parameterize}"
output << "skin-#{current_skin.parameterize}"
output << content_for(:html_classes)
output << 'system-font' if current_account&.user&.setting_system_font_ui
output << 'custom-scrollbars' unless current_account&.user&.setting_system_scrollbars_ui
output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion')
@@ -165,6 +163,12 @@ module ApplicationHelper
output.compact_blank.join(' ')
end
def body_classes
output = []
output << content_for(:body_classes)
output.compact_blank.join(' ')
end
def cdn_host
Rails.configuration.action_controller.asset_host
end

View File

@@ -35,7 +35,7 @@ module LanguagesHelper
cy: ['Welsh', 'Cymraeg'].freeze,
da: ['Danish', 'dansk'].freeze,
de: ['German', 'Deutsch'].freeze,
dv: ['Divehi', 'Dhivehi'].freeze,
dv: ['Divehi', 'ދިވެހި'].freeze,
dz: ['Dzongkha', 'རྫོང་ཁ'].freeze,
ee: ['Ewe', 'Eʋegbe'].freeze,
el: ['Greek', 'Ελληνικά'].freeze,
@@ -100,7 +100,7 @@ module LanguagesHelper
lo: ['Lao', 'ລາວ'].freeze,
lt: ['Lithuanian', 'lietuvių kalba'].freeze,
lu: ['Luba-Katanga', 'Tshiluba'].freeze,
lv: ['Latvian', 'latviešu valoda'].freeze,
lv: ['Latvian', 'Latviski'].freeze,
mg: ['Malagasy', 'fiteny malagasy'].freeze,
mh: ['Marshallese', 'Kajin M̧ajeļ'].freeze,
mi: ['Māori', 'te reo Māori'].freeze,

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
module WrapstodonHelper
def render_wrapstodon_share_data(report)
json = ActiveModelSerializers::SerializableResource.new(
AnnualReportsPresenter.new([report]),
serializer: REST::AnnualReportsSerializer,
scope: nil,
scope_name: :current_user
).to_json
# rubocop:disable Rails/OutputSafety
content_tag(:script, json_escape(json).html_safe, type: 'application/json', id: 'wrapstodon-data')
# rubocop:enable Rails/OutputSafety
end
end

View File

@@ -1,31 +1,49 @@
# frozen_string_literal: true
class DateOfBirthInput < SimpleForm::Inputs::Base
OPTIONS = [
{ autocomplete: 'bday-day', maxlength: 2, pattern: '[0-9]+', placeholder: 'DD' }.freeze,
{ autocomplete: 'bday-month', maxlength: 2, pattern: '[0-9]+', placeholder: 'MM' }.freeze,
{ autocomplete: 'bday-year', maxlength: 4, pattern: '[0-9]+', placeholder: 'YYYY' }.freeze,
].freeze
OPTIONS = {
day: { autocomplete: 'bday-day', maxlength: 2, pattern: '[0-9]+', placeholder: 'DD' },
month: { autocomplete: 'bday-month', maxlength: 2, pattern: '[0-9]+', placeholder: 'MM' },
year: { autocomplete: 'bday-year', maxlength: 4, pattern: '[0-9]+', placeholder: 'YYYY' },
}.freeze
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
merged_input_options[:inputmode] = 'numeric'
values = (object.public_send(attribute_name) || '').split('.')
safe_join(Array.new(3) do |index|
options = merged_input_options.merge(OPTIONS[index]).merge id: generate_id(index), 'aria-label': I18n.t("simple_form.labels.user.date_of_birth_#{index + 1}i"), value: values[index]
@builder.text_field("#{attribute_name}(#{index + 1}i)", options)
end)
safe_join(
ordered_options.map do |option|
options = merged_input_options
.merge(OPTIONS[option])
.merge(
id: generate_id(option),
'aria-label': I18n.t("simple_form.labels.user.date_of_birth_#{param_for(option)}"),
value: values[option]
)
@builder.text_field("#{attribute_name}(#{param_for(option)})", options)
end
)
end
def label_target
"#{attribute_name}_1i"
"#{attribute_name}_#{param_for(ordered_options.first)}"
end
private
def generate_id(index)
"#{object_name}_#{attribute_name}_#{index + 1}i"
def ordered_options
I18n.t('date.order').map(&:to_sym)
end
def generate_id(option)
"#{object_name}_#{attribute_name}_#{param_for(option)}"
end
def param_for(option)
"#{ActionView::Helpers::DateTimeSelector::POSITION[option]}i"
end
def values
Date._parse((object.public_send(attribute_name) || '').to_s).transform_keys(mon: :month, mday: :day)
end
end

View File

@@ -1,7 +1,7 @@
import { createRoot } from 'react-dom/client';
import Rails from '@rails/ujs';
import { decode, ValidationError } from 'blurhash';
import { on } from 'delegated-events';
import ready from '../mastodon/ready';
@@ -24,10 +24,9 @@ const setAnnouncementEndsAttributes = (target: HTMLInputElement) => {
}
};
Rails.delegate(
document,
'input[type="datetime-local"]#announcement_starts_at',
on(
'change',
'input[type="datetime-local"]#announcement_starts_at',
({ target }) => {
if (target instanceof HTMLInputElement)
setAnnouncementEndsAttributes(target);
@@ -63,7 +62,7 @@ const hideSelectAll = () => {
if (hiddenField) hiddenField.value = '0';
};
Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
on('change', '#batch_checkbox_all', ({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
const selectAllMatchingElement = document.querySelector(
@@ -85,7 +84,7 @@ Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
}
});
Rails.delegate(document, '.batch-table__select-all button', 'click', () => {
on('click', '.batch-table__select-all button', () => {
const hiddenField = document.querySelector<HTMLInputElement>(
'#select_all_matching',
);
@@ -113,7 +112,7 @@ Rails.delegate(document, '.batch-table__select-all button', 'click', () => {
}
});
Rails.delegate(document, batchCheckboxClassName, 'change', () => {
on('change', batchCheckboxClassName, () => {
const checkAllElement = document.querySelector<HTMLInputElement>(
'input#batch_checkbox_all',
);
@@ -140,14 +139,9 @@ Rails.delegate(document, batchCheckboxClassName, 'change', () => {
}
});
Rails.delegate(
document,
'.filter-subset--with-select select',
'change',
({ target }) => {
if (target instanceof HTMLSelectElement) target.form?.submit();
},
);
on('change', '.filter-subset--with-select select', ({ target }) => {
if (target instanceof HTMLSelectElement) target.form?.submit();
});
const onDomainBlockSeverityChange = (target: HTMLSelectElement) => {
const rejectMediaDiv = document.querySelector(
@@ -168,11 +162,11 @@ const onDomainBlockSeverityChange = (target: HTMLSelectElement) => {
}
};
Rails.delegate(document, '#domain_block_severity', 'change', ({ target }) => {
on('change', '#domain_block_severity', ({ target }) => {
if (target instanceof HTMLSelectElement) onDomainBlockSeverityChange(target);
});
const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => {
function onEnableBootstrapTimelineAccountsChange(target: HTMLInputElement) {
const bootstrapTimelineAccountsField =
document.querySelector<HTMLInputElement>(
'#form_admin_settings_bootstrap_timeline_accounts',
@@ -194,12 +188,11 @@ const onEnableBootstrapTimelineAccountsChange = (target: HTMLInputElement) => {
);
}
}
};
}
Rails.delegate(
document,
'#form_admin_settings_enable_bootstrap_timeline_accounts',
on(
'change',
'#form_admin_settings_enable_bootstrap_timeline_accounts',
({ target }) => {
if (target instanceof HTMLInputElement)
onEnableBootstrapTimelineAccountsChange(target);
@@ -239,11 +232,11 @@ const onChangeRegistrationMode = (target: HTMLSelectElement) => {
});
};
const convertUTCDateTimeToLocal = (value: string) => {
function convertUTCDateTimeToLocal(value: string) {
const date = new Date(value + 'Z');
const twoChars = (x: number) => x.toString().padStart(2, '0');
return `${date.getFullYear()}-${twoChars(date.getMonth() + 1)}-${twoChars(date.getDate())}T${twoChars(date.getHours())}:${twoChars(date.getMinutes())}`;
};
}
function convertLocalDatetimeToUTC(value: string) {
const date = new Date(value);
@@ -251,14 +244,9 @@ function convertLocalDatetimeToUTC(value: string) {
return fullISO8601.slice(0, fullISO8601.indexOf('T') + 6);
}
Rails.delegate(
document,
'#form_admin_settings_registrations_mode',
'change',
({ target }) => {
if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target);
},
);
on('change', '#form_admin_settings_registrations_mode', ({ target }) => {
if (target instanceof HTMLSelectElement) onChangeRegistrationMode(target);
});
async function mountReactComponent(element: Element) {
const componentName = element.getAttribute('data-admin-component');
@@ -305,7 +293,7 @@ ready(() => {
if (registrationMode) onChangeRegistrationMode(registrationMode);
const checkAllElement = document.querySelector<HTMLInputElement>(
'input#batch_checkbox_all',
'#batch_checkbox_all',
);
if (checkAllElement) {
const allCheckboxes = Array.from(
@@ -318,7 +306,7 @@ ready(() => {
}
document
.querySelector('a#add-instance-button')
.querySelector<HTMLAnchorElement>('a#add-instance-button')
?.addEventListener('click', (e) => {
const domain = document.querySelector<HTMLInputElement>(
'input[type="text"]#by_domain',
@@ -342,7 +330,7 @@ ready(() => {
}
});
Rails.delegate(document, 'form', 'submit', ({ target }) => {
on('submit', 'form', ({ target }) => {
if (target instanceof HTMLFormElement)
target
.querySelectorAll<HTMLInputElement>('input[type="datetime-local"]')

View File

@@ -4,8 +4,8 @@ import { IntlMessageFormat } from 'intl-messageformat';
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
import { defineMessages } from 'react-intl';
import Rails from '@rails/ujs';
import axios from 'axios';
import { on } from 'delegated-events';
import { throttle } from 'lodash';
import { timeAgoString } from '../mastodon/components/relative_timestamp';
@@ -175,10 +175,9 @@ function loaded() {
});
}
Rails.delegate(
document,
'input#user_account_attributes_username',
on(
'input',
'input#user_account_attributes_username',
throttle(
({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
@@ -202,60 +201,47 @@ function loaded() {
),
);
Rails.delegate(
document,
'#user_password,#user_password_confirmation',
'input',
() => {
const password = document.querySelector<HTMLInputElement>(
'input#user_password',
);
const confirmation = document.querySelector<HTMLInputElement>(
'input#user_password_confirmation',
);
if (!confirmation || !password) return;
on('input', '#user_password,#user_password_confirmation', () => {
const password = document.querySelector<HTMLInputElement>(
'input#user_password',
);
const confirmation = document.querySelector<HTMLInputElement>(
'input#user_password_confirmation',
);
if (!confirmation || !password) return;
if (
confirmation.value &&
confirmation.value.length > password.maxLength
) {
confirmation.setCustomValidity(
formatMessage(messages.passwordExceedsLength),
);
} else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity(
formatMessage(messages.passwordDoesNotMatch),
);
} else {
confirmation.setCustomValidity('');
}
},
);
if (confirmation.value && confirmation.value.length > password.maxLength) {
confirmation.setCustomValidity(
formatMessage(messages.passwordExceedsLength),
);
} else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity(
formatMessage(messages.passwordDoesNotMatch),
);
} else {
confirmation.setCustomValidity('');
}
});
}
Rails.delegate(
document,
'#edit_profile input[type=file]',
'change',
({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
on('change', '#edit_profile input[type=file]', ({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
const avatar = document.querySelector<HTMLImageElement>(
`img#${target.id}-preview`,
);
const avatar = document.querySelector<HTMLImageElement>(
`img#${target.id}-preview`,
);
if (!avatar) return;
if (!avatar) return;
let file: File | undefined;
if (target.files) file = target.files[0];
let file: File | undefined;
if (target.files) file = target.files[0];
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
if (url) avatar.src = url;
},
);
if (url) avatar.src = url;
});
Rails.delegate(document, '.input-copy input', 'click', ({ target }) => {
on('click', '.input-copy input', ({ target }) => {
if (!(target instanceof HTMLInputElement)) return;
target.focus();
@@ -263,7 +249,7 @@ Rails.delegate(document, '.input-copy input', 'click', ({ target }) => {
target.setSelectionRange(0, target.value.length);
});
Rails.delegate(document, '.input-copy button', 'click', ({ target }) => {
on('click', '.input-copy button', ({ target }) => {
if (!(target instanceof HTMLButtonElement)) return;
const input = target.parentNode?.querySelector<HTMLInputElement>(
@@ -312,22 +298,22 @@ const toggleSidebar = () => {
sidebar.classList.toggle('visible');
};
Rails.delegate(document, '.sidebar__toggle__icon', 'click', () => {
on('click', '.sidebar__toggle__icon', () => {
toggleSidebar();
});
Rails.delegate(document, '.sidebar__toggle__icon', 'keydown', (e) => {
on('keydown', '.sidebar__toggle__icon', (e) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
toggleSidebar();
}
});
Rails.delegate(document, 'img.custom-emoji', 'mouseover', ({ target }) => {
on('mouseover', 'img.custom-emoji', ({ target }) => {
if (target instanceof HTMLImageElement && target.dataset.original)
target.src = target.dataset.original;
});
Rails.delegate(document, 'img.custom-emoji', 'mouseout', ({ target }) => {
on('mouseout', 'img.custom-emoji', ({ target }) => {
if (target instanceof HTMLImageElement && target.dataset.static)
target.src = target.dataset.static;
});
@@ -376,22 +362,17 @@ const setInputHint = (
}
};
Rails.delegate(
document,
'#account_statuses_cleanup_policy_enabled',
'change',
({ target }) => {
if (!(target instanceof HTMLInputElement) || !target.form) return;
on('change', '#account_statuses_cleanup_policy_enabled', ({ target }) => {
if (!(target instanceof HTMLInputElement) || !target.form) return;
target.form
.querySelectorAll<
HTMLInputElement | HTMLSelectElement
>('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select')
.forEach((input) => {
setInputDisabled(input, !target.checked);
});
},
);
target.form
.querySelectorAll<
HTMLInputElement | HTMLSelectElement
>('input:not([type=hidden], #account_statuses_cleanup_policy_enabled), select')
.forEach((input) => {
setInputDisabled(input, !target.checked);
});
});
const updateDefaultQuotePrivacyFromPrivacy = (
privacySelect: EventTarget | null,
@@ -414,18 +395,13 @@ const updateDefaultQuotePrivacyFromPrivacy = (
}
};
Rails.delegate(
document,
'#user_settings_attributes_default_privacy',
'change',
({ target }) => {
updateDefaultQuotePrivacyFromPrivacy(target);
},
);
on('change', '#user_settings_attributes_default_privacy', ({ target }) => {
updateDefaultQuotePrivacyFromPrivacy(target);
});
// Empty the honeypot fields in JS in case something like an extension
// automatically filled them.
Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => {
on('submit', '#registration_new_user,#new_user', () => {
[
'user_website',
'user_confirm_password',
@@ -439,7 +415,7 @@ Rails.delegate(document, '#registration_new_user,#new_user', 'submit', () => {
});
});
Rails.delegate(document, '.rules-list button', 'click', ({ target }) => {
on('click', '.rules-list button', ({ target }) => {
if (!(target instanceof HTMLElement)) {
return;
}

View File

@@ -0,0 +1,63 @@
import { createRoot } from 'react-dom/client';
import { Provider as ReduxProvider } from 'react-redux';
import { importFetchedStatuses } from '@/mastodon/actions/importer';
import { hydrateStore } from '@/mastodon/actions/store';
import type { ApiAnnualReportResponse } from '@/mastodon/api/annual_report';
import { Router } from '@/mastodon/components/router';
import { WrapstodonSharedPage } from '@/mastodon/features/annual_report/shared_page';
import { IntlProvider, loadLocale } from '@/mastodon/locales';
import { loadPolyfills } from '@/mastodon/polyfills';
import ready from '@/mastodon/ready';
import { setReport } from '@/mastodon/reducers/slices/annual_report';
import { store } from '@/mastodon/store';
function loaded() {
const mountNode = document.getElementById('wrapstodon');
if (!mountNode) {
throw new Error('Mount node not found');
}
const propsNode = document.getElementById('wrapstodon-data');
if (!propsNode) {
throw new Error('Initial state prop not found');
}
const initialState = JSON.parse(
propsNode.textContent,
) as ApiAnnualReportResponse;
const report = initialState.annual_reports[0];
if (!report) {
throw new Error('Initial state report not found');
}
// Set up store
store.dispatch(
hydrateStore({
meta: { locale: document.documentElement.lang },
accounts: initialState.accounts,
}),
);
store.dispatch(importFetchedStatuses(initialState.statuses));
store.dispatch(setReport(report));
const root = createRoot(mountNode);
root.render(
<IntlProvider>
<ReduxProvider store={store}>
<Router>
<WrapstodonSharedPage />
</Router>
</ReduxProvider>
</IntlProvider>,
);
}
loadPolyfills()
.then(loadLocale)
.then(() => ready(loaded))
.catch((err: unknown) => {
console.error(err);
});

View File

@@ -1,25 +0,0 @@
export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST';
export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS';
export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL';
export function fetchBundleRequest(skipLoading) {
return {
type: BUNDLE_FETCH_REQUEST,
skipLoading,
};
}
export function fetchBundleSuccess(skipLoading) {
return {
type: BUNDLE_FETCH_SUCCESS,
skipLoading,
};
}
export function fetchBundleFail(error, skipLoading) {
return {
type: BUNDLE_FETCH_FAIL,
error,
skipLoading,
};
}

View File

@@ -709,16 +709,7 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
dispatch(useEmoji(suggestion));
} else if (suggestion.type === 'hashtag') {
// TODO: it could make sense to keep the “most capitalized” of the two
const tokenName = token.slice(1); // strip leading '#'
const suggestionPrefix = suggestion.name.slice(0, tokenName.length);
const prefixMatchesSuggestion = suggestionPrefix.localeCompare(tokenName, undefined, { sensitivity: 'accent' }) === 0;
if (prefixMatchesSuggestion) {
completion = token + suggestion.name.slice(tokenName.length);
} else {
completion = `${token.slice(0, 1)}${suggestion.name}`;
}
completion = token + suggestion.name.slice(token.length - 1);
startPosition = position - 1;
} else if (suggestion.type === 'account') {
completion = `@${getState().getIn(['accounts', suggestion.id, 'acct'])}`;

View File

@@ -1,3 +1,5 @@
import { checkAnnualReport } from '@/flavours/glitch/reducers/slices/annual_report';
import api from '../api';
import { importFetchedAccount } from './importer';
@@ -29,6 +31,9 @@ export const fetchServer = () => (dispatch, getState) => {
.get('/api/v2/instance').then(({ data }) => {
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data));
if (data.wrapstodon) {
void dispatch(checkAnnualReport());
}
}).catch(err => dispatch(fetchServerFail(err)));
};

View File

@@ -37,7 +37,9 @@ export function hydrateStore(rawState) {
dispatch(hydrateCompose());
dispatch(hydrateSearch());
dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
if (rawState.accounts) {
dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
}
dispatch(saveSettings());
};
}

View File

@@ -0,0 +1,38 @@
import api, { apiRequestGet, getAsyncRefreshHeader } from '../api';
import type { ApiAccountJSON } from '../api_types/accounts';
import type { ApiStatusJSON } from '../api_types/statuses';
import type { AnnualReport } from '../models/annual_report';
export type ApiAnnualReportState =
| 'available'
| 'generating'
| 'eligible'
| 'ineligible';
export const apiGetAnnualReportState = async (year: number) => {
const response = await api().get<{ state: ApiAnnualReportState }>(
`/api/v1/annual_reports/${year}/state`,
);
return {
state: response.data.state,
refresh: getAsyncRefreshHeader(response),
};
};
export const apiRequestGenerateAnnualReport = async (year: number) => {
const response = await api().post(`/api/v1/annual_reports/${year}/generate`);
return {
refresh: getAsyncRefreshHeader(response),
};
};
export interface ApiAnnualReportResponse {
annual_reports: AnnualReport[];
accounts: ApiAccountJSON[];
statuses: ApiStatusJSON[];
}
export const apiGetAnnualReport = (year: number) =>
apiRequestGet<ApiAnnualReportResponse>(`v1/annual_reports/${year}`);

View File

@@ -1,8 +1,9 @@
// See app/serializers/rest/account_serializer.rb
// See app/serializers/rest/custom_emoji_serializer.rb
export interface ApiCustomEmojiJSON {
shortcode: string;
static_url: string;
url: string;
category?: string;
featured?: boolean;
visible_in_picker: boolean;
}

View File

@@ -51,7 +51,7 @@ export interface ApiPreviewCardJSON {
html: string;
width: number;
height: number;
image: string;
image: string | null;
image_description: string;
embed_url: string;
blurhash: string;

View File

@@ -1,9 +1,5 @@
import Rails from '@rails/ujs';
import { setupLinkListeners } from './utils/links';
export function start() {
try {
Rails.start();
} catch {
// If called twice
}
setupLinkListeners();
}

View File

@@ -6,6 +6,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import { Icon } from 'flavours/glitch/components/icon';
import type { Account } from 'flavours/glitch/models/account';
import { CustomEmojiProvider } from './emoji/context';
import { EmojiHTML } from './emoji/html';
import { useElementHandledLink } from './status/handled_link';
@@ -21,13 +22,12 @@ export const AccountFields: React.FC<Pick<Account, 'fields' | 'emojis'>> = ({
}
return (
<>
<CustomEmojiProvider emojis={emojis}>
{fields.map((pair, i) => (
<dl key={i} className={classNames({ verified: pair.verified_at })}>
<EmojiHTML
as='dt'
htmlString={pair.name_emojified}
extraEmojis={emojis}
className='translate'
{...htmlHandlers}
/>
@@ -52,13 +52,12 @@ export const AccountFields: React.FC<Pick<Account, 'fields' | 'emojis'>> = ({
<EmojiHTML
as='span'
htmlString={pair.value_emojified}
extraEmojis={emojis}
{...htmlHandlers}
/>
</dd>
</dl>
))}
</>
</CustomEmojiProvider>
);
};

View File

@@ -49,7 +49,11 @@ export const Alert: React.FC<{
</span>
{hasAction && (
<button className='notification-bar__action' onClick={onActionClick}>
<button
className='notification-bar__action'
onClick={onActionClick}
type='button'
>
{action}
</button>
)}

View File

@@ -47,7 +47,7 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
rootClose
onHide={handleClose}
show={open}
target={anchorRef.current}
target={anchorRef}
placement='top-end'
flip
offset={offset}

View File

@@ -159,8 +159,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
this.input.focus();
};
UNSAFE_componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
componentDidUpdate (prevProps) {
if (prevProps.suggestions !== this.props.suggestions && this.props.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
this.setState({ suggestionsHidden: false });
}
}

View File

@@ -50,6 +50,7 @@ const AutosuggestTextarea = forwardRef(({
onKeyUp,
onKeyDown,
onPaste,
onDrop,
onFocus,
autoFocus = true,
lang,
@@ -153,6 +154,12 @@ const AutosuggestTextarea = forwardRef(({
onPaste(e);
}, [onPaste]);
const handleDrop = useCallback((e) => {
if (onDrop) {
onDrop(e);
}
}, [onDrop]);
// Show the suggestions again whenever they change and the textarea is focused
useEffect(() => {
if (suggestions.size > 0 && textareaRef.current === document.activeElement) {
@@ -204,6 +211,7 @@ const AutosuggestTextarea = forwardRef(({
onFocus={handleFocus}
onBlur={handleBlur}
onPaste={handlePaste}
onDrop={handleDrop}
dir='auto'
aria-autocomplete='list'
aria-label={placeholder}
@@ -235,6 +243,7 @@ AutosuggestTextarea.propTypes = {
onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func,
onPaste: PropTypes.func.isRequired,
onDrop: PropTypes.func,
onFocus:PropTypes.func,
autoFocus: PropTypes.bool,
lang: PropTypes.string,

View File

@@ -78,6 +78,7 @@ export const Button: React.FC<Props> = ({
aria-live={loading !== undefined ? 'polite' : undefined}
onClick={handleClick}
title={title}
// eslint-disable-next-line react/button-has-type -- set correctly via TS
type={type}
{...props}
>

View File

@@ -0,0 +1,126 @@
import type { FC } from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { fn, userEvent, expect } from 'storybook/test';
import type { CarouselProps } from './index';
import { Carousel } from './index';
interface TestSlideProps {
id: number;
text: string;
color: string;
}
const TestSlide: FC<TestSlideProps & { active: boolean }> = ({
active,
text,
color,
}) => (
<div
className='test-slide'
style={{
backgroundColor: active ? color : undefined,
}}
>
{text}
</div>
);
const slides: TestSlideProps[] = [
{
id: 1,
text: 'first',
color: 'red',
},
{
id: 2,
text: 'second',
color: 'pink',
},
{
id: 3,
text: 'third',
color: 'orange',
},
];
type StoryProps = Pick<
CarouselProps<TestSlideProps>,
'items' | 'renderItem' | 'emptyFallback' | 'onChangeSlide'
>;
const meta = {
title: 'Components/Carousel',
args: {
items: slides,
renderItem(item, active) {
return <TestSlide {...item} active={active} key={item.id} />;
},
onChangeSlide: fn(),
emptyFallback: 'No slides available',
},
render(args) {
return (
<>
<Carousel {...args} />
<style>
{`.test-slide {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
font-weight: bold;
min-height: 100px;
transition: background-color 0.3s;
background-color: black;
}`}
</style>
</>
);
},
argTypes: {
emptyFallback: {
type: 'string',
},
},
tags: ['test'],
} satisfies Meta<StoryProps>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
async play({ args, canvas }) {
const nextButton = await canvas.findByRole('button', { name: /next/i });
const slides = await canvas.findAllByRole('group');
await expect(slides).toHaveLength(slides.length);
await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(1, slides[1]);
await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(2, slides[2]);
// Wrap around
await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(0, slides[0]);
},
};
export const DifferentHeights: Story = {
args: {
items: slides.map((props, index) => ({
...props,
styles: { height: 100 + index * 100 },
})),
},
};
export const NoSlides: Story = {
args: {
items: [],
},
};

View File

@@ -0,0 +1,244 @@
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import type {
ComponentPropsWithoutRef,
ComponentType,
ReactElement,
ReactNode,
} from 'react';
import type { MessageDescriptor } from 'react-intl';
import { defineMessages, useIntl } from 'react-intl';
import classNames from 'classnames';
import { usePrevious } from '@dnd-kit/utilities';
import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import type { CarouselPaginationProps } from './pagination';
import { CarouselPagination } from './pagination';
import './styles.scss';
const defaultMessages = defineMessages({
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
next: { id: 'lightbox.next', defaultMessage: 'Next' },
current: {
id: 'carousel.current',
defaultMessage: '<sr>Slide</sr> {current, number} / {max, number}',
},
slide: {
id: 'carousel.slide',
defaultMessage: 'Slide {current, number} of {max, number}',
},
});
export type MessageKeys = keyof typeof defaultMessages;
export interface CarouselSlideProps {
id: string | number;
}
export type RenderSlideFn<
SlideProps extends CarouselSlideProps = CarouselSlideProps,
> = (item: SlideProps, active: boolean, index: number) => ReactElement;
export interface CarouselProps<
SlideProps extends CarouselSlideProps = CarouselSlideProps,
> {
items: SlideProps[];
renderItem: RenderSlideFn<SlideProps>;
onChangeSlide?: (index: number, ref: Element) => void;
paginationComponent?: ComponentType<CarouselPaginationProps> | null;
paginationProps?: Partial<CarouselPaginationProps>;
messages?: Record<MessageKeys, MessageDescriptor>;
emptyFallback?: ReactNode;
classNamePrefix?: string;
slideClassName?: string;
}
export const Carousel = <
SlideProps extends CarouselSlideProps = CarouselSlideProps,
>({
items,
renderItem,
onChangeSlide,
paginationComponent: Pagination = CarouselPagination,
paginationProps = {},
messages = defaultMessages,
children,
emptyFallback = null,
className,
classNamePrefix = 'carousel',
slideClassName,
...wrapperProps
}: CarouselProps<SlideProps> & ComponentPropsWithoutRef<'div'>) => {
// Handle slide change
const [slideIndex, setSlideIndex] = useState(0);
const wrapperRef = useRef<HTMLDivElement>(null);
// Handle slide heights
const [currentSlideHeight, setCurrentSlideHeight] = useState(
() => wrapperRef.current?.scrollHeight ?? 0,
);
const previousSlideHeight = usePrevious(currentSlideHeight);
const handleSlideChange = useCallback(
(direction: number) => {
setSlideIndex((prev) => {
const max = items.length - 1;
let newIndex = prev + direction;
if (newIndex < 0) {
newIndex = max;
} else if (newIndex > max) {
newIndex = 0;
}
const slide = wrapperRef.current?.children[newIndex];
if (slide) {
setCurrentSlideHeight(slide.scrollHeight);
if (slide instanceof HTMLElement) {
onChangeSlide?.(newIndex, slide);
}
}
return newIndex;
});
},
[items.length, onChangeSlide],
);
const observerRef = useRef<ResizeObserver | null>(null);
observerRef.current ??= new ResizeObserver(() => {
handleSlideChange(0);
});
const wrapperStyles = useSpring({
x: `-${slideIndex * 100}%`,
height: currentSlideHeight,
// Don't animate from zero to the height of the initial slide
immediate: !previousSlideHeight,
});
useLayoutEffect(() => {
// Update slide height when the component mounts
if (currentSlideHeight === 0) {
handleSlideChange(0);
}
}, [currentSlideHeight, handleSlideChange]);
// Handle swiping animations
const bind = useDrag(
({ swipe: [swipeX] }) => {
handleSlideChange(swipeX * -1); // Invert swipe as swiping left loads the next slide.
},
{ pointer: { capture: false } },
);
const handlePrev = useCallback(() => {
handleSlideChange(-1);
// We're focusing on the wrapper as the child slides can potentially be inert.
// Because of that, only the active slide can be focused anyway.
wrapperRef.current?.focus();
}, [handleSlideChange]);
const handleNext = useCallback(() => {
handleSlideChange(1);
wrapperRef.current?.focus();
}, [handleSlideChange]);
const intl = useIntl();
if (items.length === 0) {
return emptyFallback;
}
return (
<div
{...bind()}
aria-roledescription='carousel'
role='region'
className={classNames(classNamePrefix, className)}
{...wrapperProps}
>
<div className={`${classNamePrefix}__header`}>
{children}
{Pagination && items.length > 1 && (
<Pagination
current={slideIndex}
max={items.length}
onNext={handleNext}
onPrev={handlePrev}
className={`${classNamePrefix}__pagination`}
messages={messages}
{...paginationProps}
/>
)}
</div>
<animated.div
className={`${classNamePrefix}__slides`}
ref={wrapperRef}
style={wrapperStyles}
aria-label={intl.formatMessage(messages.slide, {
current: slideIndex + 1,
max: items.length,
})}
tabIndex={-1}
>
{items.map((itemsProps, index) => (
<CarouselSlideWrapper<SlideProps>
item={itemsProps}
renderItem={renderItem}
observer={observerRef.current}
index={index}
key={`slide-${itemsProps.id}`}
className={classNames(`${classNamePrefix}__slide`, slideClassName, {
active: index === slideIndex,
})}
active={index === slideIndex}
/>
))}
</animated.div>
</div>
);
};
type CarouselSlideWrapperProps<SlideProps extends CarouselSlideProps> = {
observer: ResizeObserver | null;
className: string;
active: boolean;
item: SlideProps;
index: number;
} & Pick<CarouselProps<SlideProps>, 'renderItem'>;
const CarouselSlideWrapper = <SlideProps extends CarouselSlideProps>({
observer,
className,
active,
renderItem,
item,
index,
}: CarouselSlideWrapperProps<SlideProps>) => {
const handleRef = useCallback(
(instance: HTMLDivElement | null) => {
if (observer && instance) {
observer.observe(instance);
}
},
[observer],
);
const children = useMemo(
() => renderItem(item, active, index),
[renderItem, item, active, index],
);
return (
<div
ref={handleRef}
className={className}
role='group'
aria-roledescription='slide'
inert={active ? undefined : ''}
data-index={index}
>
{children}
</div>
);
};

View File

@@ -0,0 +1,54 @@
import type { FC, MouseEventHandler } from 'react';
import type { MessageDescriptor } from 'react-intl';
import { useIntl } from 'react-intl';
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { IconButton } from '../icon_button';
import type { MessageKeys } from './index';
export interface CarouselPaginationProps {
onNext: MouseEventHandler;
onPrev: MouseEventHandler;
current: number;
max: number;
className?: string;
messages: Record<MessageKeys, MessageDescriptor>;
}
export const CarouselPagination: FC<CarouselPaginationProps> = ({
onNext,
onPrev,
current,
max,
className = '',
messages,
}) => {
const intl = useIntl();
return (
<div className={className}>
<IconButton
title={intl.formatMessage(messages.previous)}
icon='chevron-left'
iconComponent={ChevronLeftIcon}
onClick={onPrev}
/>
<span aria-live='polite'>
{intl.formatMessage(messages.current, {
current: current + 1,
max,
sr: (chunk) => <span className='sr-only'>{chunk}</span>,
})}
</span>
<IconButton
title={intl.formatMessage(messages.next)}
icon='chevron-right'
iconComponent={ChevronRightIcon}
onClick={onNext}
/>
</div>
);
};

View File

@@ -0,0 +1,28 @@
.carousel {
gap: 16px;
overflow: hidden;
touch-action: pan-y;
&__header {
padding: 8px 16px;
}
&__pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
&__slides {
display: flex;
flex-wrap: nowrap;
align-items: start;
}
&__slide {
flex: 0 0 100%;
width: 100%;
overflow: hidden;
}
}

View File

@@ -30,7 +30,7 @@ export const ColumnBackButton: React.FC<{ onClick?: OnClickCallback }> = ({
const handleClick = useHandleClick(onClick);
const component = (
<button onClick={handleClick} className='column-back-button'>
<button onClick={handleClick} className='column-back-button' type='button'>
<Icon
id='chevron-left'
icon={ArrowBackIcon}

View File

@@ -53,6 +53,7 @@ const BackButton: React.FC<{
compact: onlyIcon,
})}
aria-label={intl.formatMessage(messages.back)}
type='button'
>
<Icon
id='chevron-left'
@@ -172,6 +173,7 @@ export const ColumnHeader: React.FC<Props> = ({
<button
className='text-btn column-header__setting-btn'
onClick={handlePin}
type='button'
>
<Icon id='times' icon={CloseIcon} />{' '}
<FormattedMessage id='column_header.unpin' defaultMessage='Unpin' />
@@ -185,6 +187,7 @@ export const ColumnHeader: React.FC<Props> = ({
aria-label={intl.formatMessage(messages.moveLeft)}
className='icon-button column-header__setting-btn'
onClick={handleMoveLeft}
type='button'
>
<Icon id='chevron-left' icon={ChevronLeftIcon} />
</button>
@@ -193,6 +196,7 @@ export const ColumnHeader: React.FC<Props> = ({
aria-label={intl.formatMessage(messages.moveRight)}
className='icon-button column-header__setting-btn'
onClick={handleMoveRight}
type='button'
>
<Icon id='chevron-right' icon={ChevronRightIcon} />
</button>
@@ -203,6 +207,7 @@ export const ColumnHeader: React.FC<Props> = ({
<button
className='text-btn column-header__setting-btn'
onClick={handlePin}
type='button'
>
<Icon id='plus' icon={AddIcon} />{' '}
<FormattedMessage id='column_header.pin' defaultMessage='Pin' />
@@ -237,6 +242,7 @@ export const ColumnHeader: React.FC<Props> = ({
collapsed ? messages.show : messages.hide,
)}
onClick={handleToggleClick}
type='button'
>
<i className='icon-with-badge'>
<Icon
@@ -259,7 +265,11 @@ export const ColumnHeader: React.FC<Props> = ({
<>
{backButton}
<button onClick={handleTitleClick} className='column-header__title'>
<button
onClick={handleTitleClick}
className='column-header__title'
type='button'
>
{!backButton && (
<Icon
id={icon}

View File

@@ -1,4 +1,4 @@
import { useCallback, useState, useEffect, useRef } from 'react';
import { useCallback, useState, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
@@ -12,11 +12,15 @@ export const ColumnSearchHeader: React.FC<{
const inputRef = useRef<HTMLInputElement>(null);
const [value, setValue] = useState('');
useEffect(() => {
// Reset the component when it turns from active to inactive.
// [More on this pattern](https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes)
const [previousActive, setPreviousActive] = useState(active);
if (active !== previousActive) {
setPreviousActive(active);
if (!active) {
setValue('');
}
}, [active]);
}
const handleChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {

View File

@@ -41,7 +41,7 @@ export const ContentWarning: React.FC<{
<EmojiHTML
as='span'
htmlString={text}
extraEmojis={status.get('emojis') as List<CustomEmoji>}
extraEmojis={status.get('emoji') as List<CustomEmoji>}
/>
</StatusBanner>
);

View File

@@ -74,7 +74,7 @@ export const CopyPasteText: React.FC<{ value: string }> = ({ value }) => {
onBlur={handleBlur}
/>
<button className='button' onClick={handleButtonClick}>
<button className='button' onClick={handleButtonClick} type='button'>
<Icon id='copy' icon={ContentCopyIcon} />{' '}
{copied ? (
<FormattedMessage id='copypaste.copied' defaultMessage='Copied' />

View File

@@ -109,7 +109,7 @@ export const Dropdown: FC<
placement='bottom-start'
onHide={handleClose}
flip
target={buttonRef.current}
target={buttonRef}
popperConfig={{
strategy: 'fixed',
modifiers: [matchWidth],

View File

@@ -216,6 +216,7 @@ export const DropdownMenu = <Item = MenuItem,>({
onClick={handleItemClick}
data-index={i}
aria-disabled={disabled}
type='button'
>
<DropdownMenuItemContent item={option} />
</button>

View File

@@ -108,7 +108,7 @@ export const EditedTimestamp: React.FC<{
onItemClick={handleItemClick}
forceDropdown
>
<button className='dropdown-menu__text-button'>
<button className='dropdown-menu__text-button' type='button'>
<FormattedMessage
id='status.edited'
defaultMessage='Edited {date}'

View File

@@ -1,9 +1,14 @@
import type { FC } from 'react';
import { useContext, useEffect, useState } from 'react';
import classNames from 'classnames';
import { EMOJI_TYPE_CUSTOM } from '@/flavours/glitch/features/emoji/constants';
import { useEmojiAppState } from '@/flavours/glitch/features/emoji/mode';
import { unicodeHexToUrl } from '@/flavours/glitch/features/emoji/normalize';
import {
emojiToInversionClassName,
unicodeHexToUrl,
} from '@/flavours/glitch/features/emoji/normalize';
import {
isStateLoaded,
loadEmojiDataToState,
@@ -41,6 +46,9 @@ export const Emoji: FC<EmojiProps> = ({
}, [appState.currentLocale, state]);
const animate = useContext(AnimateEmojiContext);
const inversionClass = emojiToInversionClassName(code);
const fallback = showFallback ? code : null;
// If the code is invalid or we otherwise know it's not valid, show the fallback.
@@ -79,7 +87,7 @@ export const Emoji: FC<EmojiProps> = ({
src={src}
alt={state.data.unicode}
title={state.data.label}
className='emojione'
className={classNames('emojione', inversionClass)}
loading='lazy'
/>
);

View File

@@ -27,22 +27,23 @@ export const ExitAnimationWrapper: React.FC<{
*/
children: (delayedIsActive: boolean) => React.ReactNode;
}> = ({ isActive = false, delayMs = 500, withEntryDelay, children }) => {
const [delayedIsActive, setDelayedIsActive] = useState(false);
const [delayedIsActive, setDelayedIsActive] = useState(
isActive && !withEntryDelay,
);
useEffect(() => {
if (isActive && !withEntryDelay) {
setDelayedIsActive(true);
const withDelay = !isActive || withEntryDelay;
return () => '';
} else {
const timeout = setTimeout(() => {
const timeout = setTimeout(
() => {
setDelayedIsActive(isActive);
}, delayMs);
},
withDelay ? delayMs : 0,
);
return () => {
clearTimeout(timeout);
};
}
return () => {
clearTimeout(timeout);
};
}, [isActive, delayMs, withEntryDelay]);
if (!isActive && !delayedIsActive) {

View File

@@ -1,38 +1,43 @@
import type { ComponentPropsWithRef } from 'react';
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
useId,
} from 'react';
import { useCallback, useEffect, useId } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { defineMessages, FormattedMessage } from 'react-intl';
import type { Map as ImmutableMap } from 'immutable';
import { List as ImmutableList } from 'immutable';
import type { AnimatedProps } from '@react-spring/web';
import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import { expandAccountFeaturedTimeline } from '@/flavours/glitch/actions/timelines';
import { Icon } from '@/flavours/glitch/components/icon';
import { IconButton } from '@/flavours/glitch/components/icon_button';
import { StatusQuoteManager } from '@/flavours/glitch/components/status_quoted';
import { usePrevious } from '@/flavours/glitch/hooks/usePrevious';
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import {
createAppSelector,
useAppDispatch,
useAppSelector,
} from '@/flavours/glitch/store';
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
import { Carousel } from './carousel';
const pinnedStatusesSelector = createAppSelector(
[
(state, accountId: string, tagged?: string) =>
(state.timelines as ImmutableMap<string, unknown>).getIn(
[`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'],
ImmutableList(),
) as ImmutableList<string>,
],
(items) => items.toArray().map((id) => ({ id })),
);
const messages = defineMessages({
previous: { id: 'featured_carousel.previous', defaultMessage: 'Previous' },
next: { id: 'featured_carousel.next', defaultMessage: 'Next' },
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
next: { id: 'lightbox.next', defaultMessage: 'Next' },
current: {
id: 'featured_carousel.current',
defaultMessage: '<sr>Post</sr> {current, number} / {max, number}',
},
slide: {
id: 'featured_carousel.slide',
defaultMessage: '{index} of {total}',
defaultMessage: 'Post {current, number} of {max, number}',
},
});
@@ -40,7 +45,6 @@ export const FeaturedCarousel: React.FC<{
accountId: string;
tagged?: string;
}> = ({ accountId, tagged }) => {
const intl = useIntl();
const accessibilityId = useId();
// Load pinned statuses
@@ -50,175 +54,37 @@ export const FeaturedCarousel: React.FC<{
void dispatch(expandAccountFeaturedTimeline(accountId, { tagged }));
}
}, [accountId, dispatch, tagged]);
const pinnedStatuses = useAppSelector(
(state) =>
(state.timelines as ImmutableMap<string, unknown>).getIn(
[`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'],
ImmutableList(),
) as ImmutableList<string>,
const pinnedStatuses = useAppSelector((state) =>
pinnedStatusesSelector(state, accountId, tagged),
);
// Handle slide change
const [slideIndex, setSlideIndex] = useState(0);
const wrapperRef = useRef<HTMLDivElement>(null);
const handleSlideChange = useCallback(
(direction: number) => {
setSlideIndex((prev) => {
const max = pinnedStatuses.size - 1;
let newIndex = prev + direction;
if (newIndex < 0) {
newIndex = max;
} else if (newIndex > max) {
newIndex = 0;
}
const slide = wrapperRef.current?.children[newIndex];
if (slide) {
setCurrentSlideHeight(slide.scrollHeight);
}
return newIndex;
});
},
[pinnedStatuses.size],
const renderSlide = useCallback(
({ id }: { id: string }) => (
<StatusQuoteManager id={id} contextType='account' withCounters />
),
[],
);
// Handle slide heights
const [currentSlideHeight, setCurrentSlideHeight] = useState(
wrapperRef.current?.scrollHeight ?? 0,
);
const previousSlideHeight = usePrevious(currentSlideHeight);
const observerRef = useRef<ResizeObserver>(
new ResizeObserver(() => {
handleSlideChange(0);
}),
);
const wrapperStyles = useSpring({
x: `-${slideIndex * 100}%`,
height: currentSlideHeight,
// Don't animate from zero to the height of the initial slide
immediate: !previousSlideHeight,
});
useLayoutEffect(() => {
// Update slide height when the component mounts
if (currentSlideHeight === 0) {
handleSlideChange(0);
}
}, [currentSlideHeight, handleSlideChange]);
// Handle swiping animations
const bind = useDrag(({ swipe: [swipeX] }) => {
handleSlideChange(swipeX * -1); // Invert swipe as swiping left loads the next slide.
});
const handlePrev = useCallback(() => {
handleSlideChange(-1);
}, [handleSlideChange]);
const handleNext = useCallback(() => {
handleSlideChange(1);
}, [handleSlideChange]);
if (!accountId || pinnedStatuses.isEmpty()) {
if (!accountId || pinnedStatuses.length === 0) {
return null;
}
return (
<div
className='featured-carousel'
{...bind()}
aria-roledescription='carousel'
<Carousel
items={pinnedStatuses}
renderItem={renderSlide}
aria-labelledby={`${accessibilityId}-title`}
role='region'
classNamePrefix='featured-carousel'
messages={messages}
>
<div className='featured-carousel__header'>
<h4
className='featured-carousel__title'
id={`${accessibilityId}-title`}
>
<Icon id='thumb-tack' icon={PushPinIcon} />
<FormattedMessage
id='featured_carousel.header'
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
values={{ count: pinnedStatuses.size }}
/>
</h4>
{pinnedStatuses.size > 1 && (
<>
<IconButton
title={intl.formatMessage(messages.previous)}
icon='chevron-left'
iconComponent={ChevronLeftIcon}
onClick={handlePrev}
/>
<span aria-live='polite'>
<FormattedMessage
id='featured_carousel.post'
defaultMessage='Post'
>
{(text) => <span className='sr-only'>{text}</span>}
</FormattedMessage>
{slideIndex + 1} / {pinnedStatuses.size}
</span>
<IconButton
title={intl.formatMessage(messages.next)}
icon='chevron-right'
iconComponent={ChevronRightIcon}
onClick={handleNext}
/>
</>
)}
</div>
<animated.div
className='featured-carousel__slides'
ref={wrapperRef}
style={wrapperStyles}
aria-atomic='false'
aria-live='polite'
>
{pinnedStatuses.map((statusId, index) => (
<FeaturedCarouselItem
key={`f-${statusId}`}
data-index={index}
aria-label={intl.formatMessage(messages.slide, {
index: index + 1,
total: pinnedStatuses.size,
})}
statusId={statusId}
observer={observerRef.current}
active={index === slideIndex}
/>
))}
</animated.div>
</div>
);
};
interface FeaturedCarouselItemProps {
statusId: string;
active: boolean;
observer: ResizeObserver;
}
const FeaturedCarouselItem: React.FC<
FeaturedCarouselItemProps & AnimatedProps<ComponentPropsWithRef<'div'>>
> = ({ statusId, active, observer, ...props }) => {
const handleRef = useCallback(
(instance: HTMLDivElement | null) => {
if (instance) {
observer.observe(instance);
}
},
[observer],
);
return (
<animated.div
className='featured-carousel__slide'
// @ts-expect-error inert in not in this version of React
inert={!active ? 'true' : undefined}
aria-roledescription='slide'
role='group'
ref={handleRef}
{...props}
>
<StatusQuoteManager id={statusId} contextType='account' withCounters />
</animated.div>
<h4 className='featured-carousel__title' id={`${accessibilityId}-title`}>
<Icon id='thumb-tack' icon={PushPinIcon} />
<FormattedMessage
id='featured_carousel.header'
defaultMessage='{count, plural, one {Pinned Post} other {Pinned Posts}}'
values={{ count: pinnedStatuses.length }}
/>
</h4>
</Carousel>
);
};

View File

@@ -235,7 +235,7 @@ const HashtagBar: React.FC<{
))}
{!expanded && hashtags.length > VISIBLE_HASHTAGS && (
<button className='link-button' onClick={handleClick}>
<button className='link-button' onClick={handleClick} type='button'>
<FormattedMessage
id='hashtags.and_other'
defaultMessage='…and {count, plural, other {# more}}'

View File

@@ -112,6 +112,7 @@ const hotkeyMatcherMap = {
openProfile: just('p'),
moveDown: just('j'),
moveUp: just('k'),
moveToTop: just('0'),
toggleHidden: just('x'),
toggleSensitive: just('h'),
toggleComposeSpoilers: optionPlus('x'),

View File

@@ -27,7 +27,6 @@ export const HoverCardController: React.FC = () => {
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
const [setScrollTimeout] = useTimeout();
const location = useLocation();
const handleClose = useCallback(() => {
cancelEnterTimeout();
@@ -36,9 +35,12 @@ export const HoverCardController: React.FC = () => {
setAnchor(null);
}, [cancelEnterTimeout, cancelLeaveTimeout, setOpen, setAnchor]);
useEffect(() => {
const location = useLocation();
const [previousLocation, setPreviousLocation] = useState(location);
if (location !== previousLocation) {
setPreviousLocation(location);
handleClose();
}, [handleClose, location]);
}
useEffect(() => {
let isScrolling = false;

View File

@@ -1,7 +1,9 @@
import { useState, useEffect, useCallback, forwardRef } from 'react';
import { useCallback, forwardRef } from 'react';
import classNames from 'classnames';
import { usePrevious } from '../hooks/usePrevious';
import { AnimatedNumber } from './animated_number';
import type { IconProp } from './icon';
import { Icon } from './icon';
@@ -59,23 +61,6 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
},
buttonRef,
) => {
const [activate, setActivate] = useState(false);
const [deactivate, setDeactivate] = useState(false);
useEffect(() => {
if (!animate) {
return;
}
if (activate && !active) {
setActivate(false);
setDeactivate(true);
} else if (!activate && active) {
setActivate(true);
setDeactivate(false);
}
}, [setActivate, setDeactivate, animate, active, activate]);
const handleClick: React.MouseEventHandler<HTMLButtonElement> = useCallback(
(e) => {
e.preventDefault();
@@ -112,12 +97,15 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(
...(active ? activeStyle : {}),
};
const previousActive = usePrevious(active) ?? active;
const shouldAnimate = animate && active !== previousActive;
const classes = classNames(className, 'icon-button', {
active,
disabled,
inverted,
activate,
deactivate,
activate: shouldAnimate && active,
deactivate: shouldAnimate && !active,
overlayed: overlay,
'icon-button--with-counter': typeof counter !== 'undefined',
});

View File

@@ -23,6 +23,7 @@ export const LearnMoreLink: React.FC<{ children: React.ReactNode }> = ({
onClick={handleClick}
aria-expanded={open}
aria-controls={accessibilityId}
type='button'
>
<FormattedMessage
id='learn_more_link.learn_more'
@@ -48,7 +49,11 @@ export const LearnMoreLink: React.FC<{ children: React.ReactNode }> = ({
<div className='learn-more__popout__content'>{children}</div>
<div>
<button className='link-button' onClick={handleClick}>
<button
className='link-button'
onClick={handleClick}
type='button'
>
<FormattedMessage
id='learn_more_link.got_it'
defaultMessage='Got it'

View File

@@ -32,6 +32,7 @@ export const LoadGap = <T,>({ disabled, param, onClick }: Props<T>) => {
onClick={handleClick}
aria-label={intl.formatMessage(messages.load_more)}
title={intl.formatMessage(messages.load_more)}
type='button'
>
{loading ? (
<LoadingIndicator />

View File

@@ -7,7 +7,7 @@ interface Props {
export const LoadPending: React.FC<Props> = ({ onClick, count }) => {
return (
<button className='load-more load-gap' onClick={onClick}>
<button className='load-more load-gap' onClick={onClick} type='button'>
<FormattedMessage
id='load_pending'
defaultMessage='{count, plural, one {# new item} other {# new items}}'

View File

@@ -1,3 +1,5 @@
import classNames from 'classnames';
import logo from '@/images/logo.svg';
export const WordmarkLogo: React.FC = () => (
@@ -7,8 +9,12 @@ export const WordmarkLogo: React.FC = () => (
</svg>
);
export const IconLogo: React.FC = () => (
<svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
export const IconLogo: React.FC<{ className?: string }> = ({ className }) => (
<svg
viewBox='0 0 79 79'
className={classNames('logo logo--icon', className)}
role='img'
>
<title>Mastodon</title>
<use xlinkHref='#logo-symbol-icon' />
</svg>

View File

@@ -251,15 +251,13 @@ class MediaGallery extends PureComponent {
window.removeEventListener('resize', this.handleResize);
}
UNSAFE_componentWillReceiveProps (nextProps) {
if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
} else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
this.setState({ visible: nextProps.visible });
componentDidUpdate (prevProps) {
if (!is(prevProps.media, this.props.media) && this.props.visible === undefined) {
this.setState({ visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all' });
} else if (!is(prevProps.visible, this.props.visible) && this.props.visible !== undefined) {
this.setState({ visible: this.props.visible });
}
}
componentDidUpdate () {
if (this.node) {
this.handleResize();
}

View File

@@ -66,14 +66,6 @@ class ModalRoot extends PureComponent {
}
}
UNSAFE_componentWillReceiveProps (nextProps) {
if (!!nextProps.children && !this.props.children) {
this.activeElement = document.activeElement;
this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
}
}
componentDidUpdate (prevProps) {
if (!this.props.children && !!prevProps.children) {
this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
@@ -90,9 +82,15 @@ class ModalRoot extends PureComponent {
this._handleModalClose();
}
if (this.props.children && !prevProps.children) {
this.activeElement = document.activeElement;
this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
this._handleModalOpen();
}
if (this.props.children) {
this._ensureHistoryBuffer();
}

View File

@@ -35,6 +35,9 @@ const messages = defineMessages({
},
});
const isPollExpired = (expiresAt: Model.Poll['expires_at']) =>
new Date(expiresAt).getTime() < Date.now();
interface PollProps {
pollId: string;
status: Status;
@@ -58,8 +61,7 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
if (!poll) {
return false;
}
const expiresAt = poll.expires_at;
return poll.expired || new Date(expiresAt).getTime() < Date.now();
return poll.expired || isPollExpired(poll.expires_at);
}, [poll]);
const timeRemaining = useMemo(() => {
if (!poll) {
@@ -171,13 +173,14 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
className='button button-secondary'
disabled={voteDisabled}
onClick={handleVote}
type='button'
>
<FormattedMessage id='poll.vote' defaultMessage='Vote' />
</button>
)}
{!showResults && (
<>
<button className='poll__link' onClick={handleReveal}>
<button className='poll__link' onClick={handleReveal} type='button'>
<FormattedMessage id='poll.reveal' defaultMessage='See results' />
</button>{' '}
·{' '}
@@ -185,7 +188,11 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
)}
{showResults && !disabled && (
<>
<button className='poll__link' onClick={handleRefresh}>
<button
className='poll__link'
onClick={handleRefresh}
type='button'
>
<FormattedMessage id='poll.refresh' defaultMessage='Refresh' />
</button>{' '}
·{' '}

View File

@@ -115,6 +115,7 @@ class Status extends ImmutablePureComponent {
muted: PropTypes.bool,
hidden: PropTypes.bool,
unread: PropTypes.bool,
showActions: PropTypes.bool,
prepend: PropTypes.string,
withDismiss: PropTypes.bool,
isQuotedPost: PropTypes.bool,
@@ -465,7 +466,8 @@ class Status extends ImmutablePureComponent {
onOpenMedia,
notification,
history,
isQuotedPost,
showActions = true,
isQuotedPost = false,
...other
} = this.props;
let attachments = null;
@@ -634,7 +636,7 @@ class Status extends ImmutablePureComponent {
} else if (status.get('card') && settings.get('inline_preview_cards') && !this.props.muted && !status.get('quote')) {
media.push(
<Card
onOpenMedia={this.handleOpenMedia}
key={`${status.get('id')}-${status.get('edited_at')}`}
card={status.get('card')}
sensitive={status.get('sensitive')}
/>,
@@ -763,7 +765,7 @@ class Status extends ImmutablePureComponent {
{/* This is a glitch-soc addition to have a placeholder */}
{!expanded && <MentionsPlaceholder status={status} />}
{!isQuotedPost &&
{(showActions && !isQuotedPost) &&
<StatusActionBar
status={status}
account={status.get('account')}

View File

@@ -224,7 +224,12 @@ const ReblogMenuItem: FC<ReblogMenuItemProps> = ({ index, item, onClick }) => {
})}
key={`${text}-${index}`}
>
<button onClick={onClick} aria-disabled={disabled} data-index={index}>
<button
onClick={onClick}
aria-disabled={disabled}
data-index={index}
type='button'
>
<DropdownMenuItemContent item={item} />
</button>
</li>

View File

@@ -1,3 +0,0 @@
.inlineIcon {
vertical-align: middle;
}

View File

@@ -12,8 +12,6 @@ import { Button } from '../button';
import { useDismissableBannerState } from '../dismissable_banner';
import { Icon } from '../icon';
import classes from './remove_quote_hint.module.css';
const DISMISSABLE_BANNER_ID = 'notifications/remove_quote_hint';
/**
@@ -46,6 +44,7 @@ export const RemoveQuoteHint: React.FC<{
if (!firstHintId) {
firstHintId = uniqueId;
// eslint-disable-next-line react-hooks/set-state-in-effect
setIsOnlyHint(true);
}
@@ -66,8 +65,8 @@ export const RemoveQuoteHint: React.FC<{
flip
offset={[12, 10]}
placement='bottom-end'
target={anchorRef.current}
container={anchorRef.current}
target={anchorRef}
container={anchorRef}
>
{({ props, placement }) => (
<div
@@ -95,7 +94,7 @@ export const RemoveQuoteHint: React.FC<{
id: 'status.more',
defaultMessage: 'More',
})}
className={classes.inlineIcon}
style={{ verticalAlign: 'middle' }}
/>
),
}}

View File

@@ -8,6 +8,8 @@ import { debounce } from 'lodash';
import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'flavours/glitch/actions/timelines';
import { RegenerationIndicator } from 'flavours/glitch/components/regeneration_indicator';
import { InlineFollowSuggestions } from 'flavours/glitch/features/home_timeline/components/inline_follow_suggestions';
import { AnnualReportTimeline } from 'flavours/glitch/features/annual_report/timeline';
import { TIMELINE_WRAPSTODON } from '@/flavours/glitch/reducers/slices/annual_report';
import { StatusQuoteManager } from '../components/status_quoted';
@@ -64,10 +66,12 @@ export default class StatusList extends ImmutablePureComponent {
switch(statusId) {
case TIMELINE_SUGGESTIONS:
return (
<InlineFollowSuggestions
key='inline-follow-suggestions'
/>
<InlineFollowSuggestions key={TIMELINE_SUGGESTIONS} />
);
case TIMELINE_WRAPSTODON:
return (
<AnnualReportTimeline key={TIMELINE_WRAPSTODON} />
)
case TIMELINE_GAP:
return (
<LoadGap

Some files were not shown because too many files have changed in this diff Show More