Compare commits

..

171 Commits

Author SHA1 Message Date
Claire
f0b875f9c3 Merge pull request #3419 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 1e5ac0f491 into stable-4.4
2026-02-24 15:48:00 +01:00
Claire
f926bf9a42 Merge commit '1e5ac0f4917f12f8caaaa9c13762a9d3e182c0f7' into glitch-soc/merge-4.4 2026-02-24 15:02:23 +01:00
Claire
1e5ac0f491 Bump version to v4.4.14 (#37964) 2026-02-24 14:44:49 +01:00
Matt Jankowski
e4e5d0cd2d Capture output in cli/emoji spec (#37861) 2026-02-24 10:35:48 +01:00
Claire
19edc6a264 Add --suspended-only option to tootctl emoji purge (#37828) 2026-02-24 10:35:48 +01:00
Claire
5bcbc77e75 Purge custom emojis on domain suspension (#37808) 2026-02-24 10:35:48 +01:00
Claire
bae1da6f73 Fix processing of object updates with duplicate hashtags (#37756) 2026-02-24 10:35:48 +01:00
David Roetzel
b89d6e256b Reject unconfirmed FASPs (#37926) 2026-02-20 16:19:09 +01:00
David Roetzel
7de301922b Re-use custom socket class for FASP requests (#37925) 2026-02-20 16:19:09 +01:00
Claire
418370d561 Merge pull request #3379 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 3871f3a399 into stable-4.4
2026-02-03 16:29:16 +01:00
Claire
eb453afe8d Merge commit '3871f3a3996d2832f66b1c219ae2f334e3e4c205' into glitch-soc/merge-4.4 2026-02-03 15:35:44 +01:00
Claire
3871f3a399 Bump version to v4.4.13 (#37716) 2026-02-03 15:27:07 +01:00
Claire
9eeeb1b31d Merge commit from fork 2026-02-03 14:59:52 +01:00
Claire
fa51ec5364 Merge pull request #3374 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 8a46c747db into stable-4.4
2026-01-30 19:22:44 +01:00
Claire
5419594d3d Merge commit '8a46c747dbca2999bab4493826451e4a4fa96ff8' into glitch-soc/merge-4.4 2026-01-30 18:21:18 +01:00
Claire
8a46c747db Fix connection recycling pushing symbols to connection pool (#37674) 2026-01-30 12:18:42 +01:00
Claire
f6ac245a84 Clear affected relationship cache on Move activities (#37664) 2026-01-30 12:18:42 +01:00
Claire
1b0be1a725 Fix error when encountering invalid tag in updated object (#37635) 2026-01-29 14:55:32 +01:00
Shlee
68c08114b9 Unclosed connection leak when replacing pooled connection in SharedTimedStack.try_create (#37335) 2026-01-29 14:55:32 +01:00
Claire
04903a45ce Merge pull request #3358 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to cbb1085855 into stable-4.4
2026-01-20 16:25:22 +01:00
Claire
d9bad1c407 Merge commit 'cbb10858555d3cbfde24d52859e2d690526c8173' into glitch-soc/merge-4.4 2026-01-20 15:58:26 +01:00
Claire
cbb1085855 Bump version to v4.4.12 (#37547) 2026-01-20 15:53:43 +01:00
Claire
3920feb8bd Merge commit from fork
* Add limit on inbox payload size

The 1MB limit is consistent with the limit we use when fetching remote resources

* Add limit to number of options from federated polls

* Add a limit to the number of federated profile fields

* Add limit on federated username length

* Add hard limits for federated display name and account bio

* Add hard limits for `alsoKnownAs` and `attributionDomains`

* Add hard limit on federated custom emoji shortcode

* Highlight most destructive limits and expand on their reasoning
2026-01-20 15:14:45 +01:00
Claire
4dbe15654a Merge commit from fork 2026-01-20 15:13:42 +01:00
Claire
27e06cdf20 Merge commit from fork 2026-01-20 15:13:10 +01:00
Claire
6ac8b52ccc Merge commit from fork 2026-01-20 15:10:38 +01:00
Claire
7ee99bbe81 Fix potential duplicate handling of quote accept/reject/delete (#37537) 2026-01-20 08:57:40 +01:00
Claire
6c1e77ff1f Skip tombstone creation on deleting from 404 (#37533) 2026-01-20 08:57:40 +01:00
Claire
2588d1ab47 Merge pull request #3354 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 92be0fd12e into stable-4.4
2026-01-19 19:30:24 +01:00
Claire
c0f8640252 Merge commit '92be0fd12e78e7875107c04f239c57043e1876f5' into glitch-soc/merge-4.4 2026-01-19 18:51:52 +01:00
Claire
92be0fd12e Disable rubocop rule disabled in main 2026-01-19 11:37:44 +01:00
Claire
8450ebc7e8 Fix FeedManager#filter_from_home error when handling a reblog of a deleted status (#37486) 2026-01-19 11:37:44 +01:00
Claire
3d27ec34ac Simplify status batch removal SQL query (#37469) 2026-01-19 11:37:44 +01:00
Joshua Rogers
d3551e1ab6 Fix Vary parsing in cache control enforcement (#37426) 2026-01-19 11:37:44 +01:00
Joshua Rogers
0b9c741dac Fix thread-unsafe ActivityPub activity dispatch (#37423) 2026-01-19 11:37:44 +01:00
Shlee
a8c9923df9 Fix SignatureParser accepting duplicate parameters in HTTP Signature header (#37375)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2026-01-19 11:37:44 +01:00
Shlee
f32067dc56 SharedConnectionPool - NoMethodError: undefined method 'site' for Integer (#37374) 2026-01-19 11:37:44 +01:00
Claire
36981fadf0 Update SECURITY.md (#37504) 2026-01-15 14:17:36 +01:00
Claire
b60edf59d8 Merge pull request #3337 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to ef4d722d6a to stable-4.4
2026-01-07 15:22:45 +01:00
Claire
3a87a63abf Merge commit 'ef4d722d6af8e0e9a25e30bb6c33aa0e14f6951f' into glitch-soc/merge-4.4 2026-01-07 14:46:45 +01:00
Claire
ef4d722d6a Bump version to v4.4.11 (#37410) 2026-01-07 14:45:02 +01:00
Claire
68e30985ca Merge commit from fork 2026-01-07 14:15:13 +01:00
Claire
1702290786 Merge commit from fork 2026-01-07 14:14:42 +01:00
Claire
abb3a02ce2 Merge pull request #3325 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to f5890040e1
2025-12-28 19:47:33 +01:00
Claire
25c79f526a Merge commit 'f5890040e12169e2ec27be68033ab0f1ad744c6e' into glitch-soc/merge-4.4 2025-12-28 11:19:25 +01:00
Claire
f5890040e1 Fix mentions of domain-blocked users being processed (#37257) 2025-12-19 11:00:14 +01:00
Claire
740f262e38 Change HTTP Signature verification status from 401 to 503 on temporary failure to get remote actor (#37221) 2025-12-19 11:00:14 +01:00
Claire
4d2b6795a4 Fix stable-4.4 branches being built with the latest tag (#37170) 2025-12-08 18:30:42 +01:00
Claire
6558a1e07a Merge pull request #3309 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to d5f12debe0 into stable-4.4
2025-12-08 17:29:58 +01:00
Echo
b64101ee64 [Glitch] Fixes YouTube embeds
Port 9bc9ebc59e to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-08 16:56:28 +01:00
Bruno Viveiros
0bde273b3d [Glitch] fix: YouTube iframe being able to start at a defined time
Port bdff970a5e to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-12-08 16:56:20 +01:00
Claire
22fe977ffe [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-08 16:55:59 +01:00
Claire
8e945bef2a Merge commit 'd5f12debe0fdd9033f3ef7ee84f9ddf366ffc2e3' into glitch-soc/merge-4.4 2025-12-08 16:54:12 +01:00
Claire
d5f12debe0 Bump version to v4.4.10 (#37162) 2025-12-08 16:20:18 +01:00
Claire
5d0ec718fd Merge commit from fork 2025-12-08 15:44:08 +01:00
Echo
c7aa312307 Fixes YouTube embeds (#37126) 2025-12-05 11:15:08 +01:00
Bruno Viveiros
dc1d4eda7c fix: YouTube iframe being able to start at a defined time (#26584) 2025-12-05 11:15:08 +01:00
Claire
931a29b4f3 Fix streamed quoted polls not being hydrated correctly (#37118) 2025-12-05 11:15:08 +01:00
Claire
99b2307350 Fix error handling when re-fetching already-known statuses (#37077) 2025-12-05 11:15:08 +01:00
Claire
375f2e6ebf Increase HTTP read timeout for expensive S3 batch delete operation (#37004) 2025-12-05 11:15:08 +01:00
Matt Jankowski
f0a1da78ba Suggest ES image version 7.17.29 in docker compose (#36972) 2025-12-05 11:15:08 +01:00
Claire
b554ecfcb4 Merge pull request #3292 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 01cf5c103d into stable-4.4
2025-11-20 15:25:12 +01:00
Claire
50244ba682 Merge commit '01cf5c103de6c46b649d4db38b517b51f8bdfb56' into glitch-soc/merge-4.4 2025-11-20 14:58:00 +01:00
Claire
01cf5c103d Bump version to v4.4.9 (#36946) 2025-11-20 14:41:12 +01:00
Claire
5bda54d15a Merge pull request #3290 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to c49e261ad0 into stable-4.4
2025-11-19 22:59:55 +01:00
Claire
07f5573cd6 [Glitch] Fix filters not being applied to quotes in detailed view
Port 16ee628d24 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-11-19 22:30:31 +01:00
Claire
2b0b537152 Merge commit 'c49e261ad07d46515af50cdf103524ecf554b2f8' into glitch-soc/merge-4.4 2025-11-19 22:29:14 +01:00
Claire
c49e261ad0 Update dependency glob (#36942) 2025-11-19 16:29:45 +01:00
Shugo Maeda
915bcb267f Fix ArgumentError of tootctl upgrade storage-schema (#36914) 2025-11-19 13:55:30 +01:00
Claire
ff37011057 update dependency js-yaml to v4.1.1 2025-11-19 13:54:58 +01:00
Claire
8f5e95a159 Fix Update importing old previously-unknown activities and treating them as recent ones (#36848) 2025-11-19 13:50:16 +01:00
Claire
16ee628d24 Fix filters not being applied to quotes in detailed view (#36843) 2025-11-19 13:47:19 +01:00
Claire
64a0b060a8 Update security policy for 4.3 (#36755) 2025-11-06 14:58:24 +01:00
Claire
fa52f4361a Merge pull request #3248 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to c2fb12d22d to stable-4.4
2025-10-21 15:33:28 +02:00
Claire
7f7d6697c1 Merge commit 'c2fb12d22d52872b5905822c3d05f65516fa7f1d' into glitch-soc/merge-4.4 2025-10-21 15:14:15 +02:00
Claire
c2fb12d22d Bump version to v4.4.8 (#36542) 2025-10-21 15:12:37 +02:00
Claire
2dc4552229 Merge commit from fork
* Add validation to reject quotes of reblogs

* Do not process quotes of reblogs as potentially valid quotes

* Refuse to serve quoted reblogs over REST API
2025-10-21 15:00:28 +02:00
Claire
95868643a2 Merge pull request #3237 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 8965e1bfa9 into stable-4.4
2025-10-15 11:32:36 +02:00
Claire
7c46fdfbf1 Merge commit '8965e1bfa9ce958ab16083a555ec6677b5f0f690' into glitch-soc/merge-4.4 2025-10-15 11:16:53 +02:00
Claire
8965e1bfa9 Bump version to v4.4.7 (#36473) 2025-10-15 10:12:23 +02:00
Claire
1e27ab0885 Fix moderation warning e-mails that include posts (#36462) 2025-10-14 17:15:58 +02:00
Jonathan de Jong
cef2c50a71 Fix allow_referrer_origin typo (#36460) 2025-10-14 17:15:58 +02:00
Claire
a54af6b06c Merge pull request #3232 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to d7f4eca801 into stable-4.4
2025-10-13 16:13:31 +02:00
Claire
81cf6715de Merge commit 'd7f4eca801bb702f487287eb218a9e7a133ee341' into glitch-soc/merge-4.4 2025-10-13 15:56:31 +02:00
Claire
d7f4eca801 Fix streaming still being authorized for suspended accounts (#36449) 2025-10-13 15:35:58 +02:00
Claire
adf291631e Bump version to v4.4.6 (#36444) 2025-10-13 14:43:01 +02:00
Emelia Smith
cbef4c9e65 Merge commit from fork 2025-10-13 14:20:57 +02:00
Claire
1631fb80e8 Merge commit from fork
* Ensure tootctl revokes sessions, access tokens and web push subscriptions

* Fix test coverage

---------

Co-authored-by: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
2025-10-13 14:20:23 +02:00
Claire
8477bec2f2 Merge commit from fork
* Streaming: Ensure disabled users cannot connect to streaming

* Streaming: Disconnect when the user is disabled

---------

Co-authored-by: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
2025-10-13 14:19:14 +02:00
Claire
6796765363 Update dependency openssl 2025-10-13 11:03:33 +02:00
Claire
044a20f12d Update dependency rack 2025-10-13 11:03:33 +02:00
Claire
15d05121df Merge pull request #3228 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to e4bdbccba8 into stable-4.4
2025-10-10 20:27:24 +02:00
Claire
07dea4e140 Merge commit 'e4bdbccba802318ebde6ab17f5adc1959bd82374' into glitch-soc/merge-4.4 2025-10-10 19:19:16 +02:00
github-actions[bot]
3c2570c88a New Crowdin Translations for stable-4.4 (automated) (#3226)
* New Crowdin translations

* Fix bogus no.yml

* Fix bogus simple_form.no.yml

---------

Co-authored-by: GitHub Actions <noreply@github.com>
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2025-10-10 19:08:08 +02:00
Claire
81955c10b1 Fix crowdin download script for glitch-soc stable branches 2025-10-10 18:45:27 +02:00
github-actions[bot]
e4bdbccba8 New Crowdin Translations for stable-4.4 (automated) (#36431)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-10-10 18:19:25 +02:00
Claire
958d4df6cf Merge pull request #3222 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to c858fc77ef to stable-4.4
2025-10-10 10:29:48 +02:00
Claire
21d4abf7cc Merge commit 'd7d6407d4196ab6783485b20a3d9e2fbfc00ef01' into glitch-soc/merge-4.4 2025-10-09 18:15:33 +02:00
Claire
d7d6407d41 Explicitly record Tombstone quotes as deleted
This adds a `deleted` state to the internal representation, but this does
not change the API, which already included such a state.
2025-10-09 17:37:23 +02:00
Claire
a186bad399 Fix "quote": { "type": "Tombstone" } being ignored 2025-10-09 17:37:23 +02:00
Claire
67575e59e6 Fix quote post state sometimes not being updated through streaming server (#36408) 2025-10-09 17:37:23 +02:00
Matt Jankowski
d9113976c8 Use tag filter for pending tag count on admin dashboard (#36404) 2025-10-09 17:37:23 +02:00
Claire
3386f225e4 Merge pull request #3218 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 670316499f into stable-4.4
2025-10-09 14:08:25 +02:00
diondiondion
97bc82c710 [Glitch] Allow quotes to be displayed in the featured carousel
Port 0d7af7e1fe to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-08 20:24:22 +02:00
Claire
0e7cb713d1 Merge commit '670316499fb4439a28d93aca2f40617dfb9bb2b0' into glitch-soc/merge-4.4 2025-10-08 18:00:57 +02:00
Claire
670316499f Update dependency uri 2025-10-08 16:26:13 +02:00
Claire
3c725240fd Update dependency rack 2025-10-08 16:26:13 +02:00
Claire
d8ddf95485 Fix JSON payload being potentially mutated when processing interaction policies (#36392) 2025-10-08 16:26:13 +02:00
Emelia Smith
4c12c2ed60 Add integration tests for mastodon-streaming (#36025)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Co-authored-by: David Roetzel <david@roetzel.de>
2025-10-08 16:26:13 +02:00
diondiondion
636ecd1d03 Display quotes in email notifications (#36379) 2025-10-08 16:26:13 +02:00
Claire
cb0065cfe9 Fix redirect to external object when URL is missing or malformed (#36347) 2025-10-08 16:26:13 +02:00
diondiondion
6ae1b4fae9 Allow quotes to be displayed in the featured carousel (#36335) 2025-10-08 16:26:13 +02:00
Eugen Rochko
fd7adcc9d4 [Glitch] Fix wrong styles on rules and buttons in the sign-up form
Port 2df86d6413 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-10-06 20:12:00 +02:00
Claire
3c5f07c28e Merge pull request #3195 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to dc6d8f8825 in stable-4.4
2025-09-23 18:51:40 +02:00
Claire
048a42b8a7 [Glitch] Add click-through for quoted limited accounts
Port c8551a3eca to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-23 18:17:30 +02:00
Claire
c475623418 Merge commit 'dc6d8f882528803c06b2a71c23d6c1467880a7e1' into glitch-soc/merge-4.4 2025-09-23 18:10:55 +02:00
Claire
dc6d8f8825 Bump version to v4.4.5 2025-09-23 14:33:16 +02:00
Claire
dd0647ca45 Update dependency rexml 2025-09-23 14:33:16 +02:00
Claire
70e2eb49df Add support for has:quote in search (#36217) 2025-09-23 14:33:16 +02:00
Claire
bef28b2e51 Fix processing of out-of-order Update as implicit updates (#36190) 2025-09-22 16:54:04 +02:00
Claire
0b66bd591f Fix getting Create and Update out of order (#36176) 2025-09-22 16:54:04 +02:00
Claire
a94d7bf520 Change quoted posts from silenced accounts not to be hidden (#36166) 2025-09-22 16:54:04 +02:00
Claire
c8551a3eca Add click-through for quoted limited accounts (#36167) 2025-09-22 16:54:04 +02:00
Claire
df322e50c0 Merge pull request #3186 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to 06c2393805 into stable-4.4
2025-09-17 19:12:14 +02:00
Claire
1f3588a5a7 Merge commit '06c2393805eee6b2e4b4aa17787129a2d542b71d' into glitch-soc/merge-4.4 2025-09-17 18:09:26 +02:00
Claire
06c2393805 Fix quote with CW but no text being shown without CW (#36150) 2025-09-17 18:01:19 +02:00
Claire
4e85b9073b Fix typo in changelog (#36140) 2025-09-16 17:39:45 +02:00
Claire
cd573a346d Merge pull request #3182 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to c966d75600 in stable-4.4
2025-09-16 15:13:03 +02:00
Claire
793296b5eb [Glitch] Fix unresponsive areas around GIFV modals in some cases
Port 2347354bba to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-16 14:14:10 +02:00
Claire
661dbede1c [Glitch] Move quote post fallback removal import-time
Port 1c8990927a to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-16 14:14:10 +02:00
Claire
89f423aa00 [Glitch] Fix missing beforeUnload confirmation when a poll is being authored
Port 0d93801bde to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-16 14:14:10 +02:00
Claire
19c89f1bbd Merge commit 'c966d756007e0e4c4eb020055c999e4cf7a07ed0' into glitch-soc/merge-4.4 2025-09-16 14:14:05 +02:00
Claire
c966d75600 Bump version to v4.4.4 2025-09-16 13:54:27 +02:00
Claire
6f1fd0c2a7 Update axios dependency 2025-09-16 13:54:27 +02:00
Claire
68c219e753 Update vite dependency 2025-09-16 13:54:27 +02:00
Claire
a795743c3f Update rails dependencies 2025-09-16 13:54:27 +02:00
github-actions[bot]
1ab5ea9bfb New Crowdin Translations for stable-4.4 (automated) (#36122)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-09-15 14:31:53 +02:00
Claire
48f55e3224 Fix quote posts with CW and no text being rejected
Fixes #36077
2025-09-12 16:27:29 +02:00
Claire
6044270d69 Fix missing memoization in Web::PushNotificationWorker (#36085) 2025-09-12 16:27:29 +02:00
Claire
be1bd91e6d Fix unresponsive areas around GIFV modals in some cases (#36059) 2025-09-12 16:27:29 +02:00
Claire
34cd5a716f Move quote post fallback removal import-time (#36055) 2025-09-12 16:27:29 +02:00
Claire
ec5128bc1f Fix missing beforeUnload confirmation when a poll is being authored (#36030) 2025-09-12 16:27:29 +02:00
Claire
28a79eb239 Merge pull request #3174 from ClearlyClaire/glitch-soc/merge-4.4
Merge upstream changes up to c0f9e7f4c3 into stable-4.4
2025-09-05 19:24:43 +02:00
Emelia Smith
1e2a9167bc [Glitch] Support displaying polls in Admin UI
Port 1137a0ca3a to glitch-soc

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
diondiondion
1947f3a18b [Glitch] Fix error alerts for deleted quotes
Port 1faf520ce4 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
Claire
5a46e3a234 [Glitch] Fix API return types for interaction API helpers
Port 8777443c9b to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
Claire
75ba0f757a [Glitch] Fix WebUI fetching deleted quote in an endless loop
Port bd6d1f0e3f to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
Claire
62ed8d633e [Glitch] Fix Edit as well as “Delete & Redraft” on a poll not inserting empty option
Port a48567784c to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
Echo
02d92d3b68 [Glitch] Redirect on success for standalone compose
Port 1675eab561 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 19:06:10 +02:00
Emelia Smith
2a87fe5860 [Glitch] Refactor to reuse the one status partial across moderation tools
Port cbb9a4dbe3 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 18:38:00 +02:00
Claire
00c61317d3 Merge remote-tracking branch 'upstream/stable-4.4' into HEAD
Conflicts:
- `app/views/layouts/application.html.haml`:
  Conflict because of glitch-soc's theming system.
  Updated the line as upstream did.
2025-09-04 18:33:21 +02:00
fiona
c0f9e7f4c3 Fix handling of edited status with new media and no text (#35970) 2025-09-04 10:45:54 +02:00
Emelia Smith
1137a0ca3a Support displaying polls in Admin UI (#35933)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2025-09-04 10:45:54 +02:00
diondiondion
1faf520ce4 Fix error alerts for deleted quotes (#35918) 2025-09-04 10:45:54 +02:00
Claire
8777443c9b Fix API return types for interaction API helpers (#35915) 2025-09-04 10:45:54 +02:00
Claire
bd6d1f0e3f Fix WebUI fetching deleted quote in an endless loop (#35909) 2025-09-04 10:45:54 +02:00
Claire
1a1a23f6f0 Consolidate labels for quote policy settings (#35893) 2025-09-04 10:45:54 +02:00
Claire
a48567784c Fix Edit as well as “Delete & Redraft” on a poll not inserting empty option (#35892) 2025-09-04 10:45:54 +02:00
Shlee
b71216a08a Add crossorigin back to inert css (regression? of #30687) (#35876) 2025-09-04 10:45:54 +02:00
Matt Jankowski
36974aaa99 Use debug? query method on httplog initializer check (#35833) 2025-09-04 10:45:54 +02:00
Claire
567f337db3 Fix self-destruct scheduler behavior on some Redis setups (#35823) 2025-09-04 10:45:54 +02:00
Matt Jankowski
97f118013a Include update in the resources args for api/web/push_subscriptions route (#35801) 2025-09-04 10:45:54 +02:00
Claire
ea5d1f0297 Fix tootctl admin create not bypassing reserved username checks (#35779) 2025-09-04 10:45:54 +02:00
Matt Jankowski
7a862d3308 First pass coverage addition for antispam class (#35771) 2025-09-04 10:45:54 +02:00
Echo
1675eab561 Redirect on success for standalone compose (#35763) 2025-09-04 10:45:54 +02:00
Claire
5f4116a311 Fix interaction policy changes in implicit updates not being saved (#35751) 2025-09-04 10:45:54 +02:00
Claire
0741381670 Add test for Delete of inlined QuoteAuthorization (#35724) 2025-09-04 10:45:54 +02:00
Claire
e61900cadc Fix quote revocation not being streamed (#35710) 2025-09-04 10:45:54 +02:00
Emelia Smith
cbb9a4dbe3 Refactor to reuse the one status partial across moderation tools (#35644) 2025-09-04 10:45:54 +02:00
Claire
4ef0ce033e Fix export of large user archives on 4.4 by enabling Zip64 (#35850) 2025-08-23 01:59:11 +02:00
Claire
48315a719d Change glitch flavor to show approximate reply count by default (#3157) 2025-08-22 22:31:39 +02:00
394 changed files with 7603 additions and 2329 deletions

View File

@@ -20,7 +20,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=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
latest=false
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
@@ -37,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=${{ startsWith(github.ref, 'refs/tags/v4.4.') }}
latest=false
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}

View File

@@ -9,7 +9,7 @@ permissions:
jobs:
download-translations-stable:
runs-on: ubuntu-latest
if: github.repository == 'mastodon/mastodon'
if: github.repository == 'glitch-soc/mastodon'
steps:
- name: Checkout

View File

@@ -21,11 +21,11 @@ Metrics/BlockNesting:
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
Max: 25
Enabled: false
# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
Max: 27
Enabled: false
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedVars, DefaultToNil.

View File

@@ -2,6 +2,177 @@
All notable changes to this project will be documented in this file.
## [4.4.14] - 2026-02-24
### Security
- Reject unconfirmed FASPs (#37926 by @oneiros, [GHSA-qgmm-vr4c-ggjg](https://github.com/mastodon/mastodon/security/advisories/GHSA-qgmm-vr4c-ggjg))
- Re-use custom socket class for FASP requests (#37925 by @oneiros, [GHSA-46w6-g98f-wxqm](https://github.com/mastodon/mastodon/security/advisories/GHSA-46w6-g98f-wxqm))
### Added
- Add `--suspended-only` option to `tootctl emoji purge` (#37828 and #37861 by @ClearlyClaire and @mjankowski)
### Fixed
- Fix custom emojis not being purged on domain suspension (#37808 by @ClearlyClaire)
- Fix processing of object updates with duplicate hashtags (#37756 by @ClearlyClaire)
## [4.4.13] - 2026-02-03
### Security
- Fix ActivityPub collection caching logic for pinned posts and featured tags not checking blocked accounts ([GHSA-ccpr-m53r-mfwr](https://github.com/mastodon/mastodon/security/advisories/GHSA-ccpr-m53r-mfwr))
### Fixed
- Fix relationship cache not being cleared when handling account migrations (#37664 by @ClearlyClaire)
- Fix error when encountering invalid tag in updated object (#37635 by @ClearlyClaire)
- Fix recycled connections not being immediately closed (#37335 and #37674 by @ClearlyClaire and @shleeable)
## [4.4.12] - 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 `Vary` parsing in cache control enforcement (#37426 by @MegaManSec)
- Fix thread-unsafe ActivityPub activity dispatch (#37423 by @MegaManSec)
- Fix SignatureParser accepting duplicate parameters in HTTP Signature header (#37375 by @shleeable)
## [4.4.11] - 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 mentions of domain-blocked users being processed (#37257 by @ClearlyClaire)
## [4.4.10] - 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 YouTube embeds by sending referer (#37126 by @ChaosExAnima)
- Fix YouTube iframe not being able to start at a defined time (#26584 by @BrunoViveiros)
- Fix streamed quoted polls not being hydrated correctly (#37118 by @ClearlyClaire)
- Fix error handling when re-fetching already-known statuses (#37077 by @ClearlyClaire)
- Fix known expensive S3 batch delete operation failing because of short timeouts (#37004 by @ClearlyClaire)
## [4.4.9] - 2025-11-20
### Fixed
- Fix `tootctl upgrade storage-schema` failing with `ArgumentError` (#36914 by @shugo)
- Fix old previously-undiscovered posts being treated as new when receiving an `Update` (#36848 by @ClearlyClaire)
- Fix filters not being applied to quotes in detailed view (#36843 by @ClearlyClaire)
## [4.4.8] - 2025-10-21
### Security
- Fix quote control bypass ([GHSA-8h43-rcqj-wpc6](https://github.com/mastodon/mastodon/security/advisories/GHSA-8h43-rcqj-wpc6))
## [4.4.7] - 2025-10-15
### Fixed
- Fix forwarder being called with `nil` status when quote post is soft-deleted (#36463 by @ClearlyClaire)
- Fix moderation warning e-mails that include posts (#36462 by @ClearlyClaire)
- Fix allow_referrer_origin typo (#36460 by @ShadowJonathan)
## [4.4.6] - 2025-10-13
### Security
- Update dependencies `rack` and `uri`
- Fix streaming server connection not being closed on user suspension (by @ThisIsMissEm, [GHSA-r2fh-jr9c-9pxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-r2fh-jr9c-9pxh))
- Fix password change through admin CLI not invalidating existing sessions and access tokens (by @ThisIsMissEm, [GHSA-f3q3-rmf7-9655](https://github.com/mastodon/mastodon/security/advisories/GHSA-f3q3-rmf7-9655))
- Fix streaming server allowing access to public timelines even without the `read` or `read:statuses` OAuth scopes (by @ThisIsMissEm, [GHSA-7gwh-mw97-qjgp](https://github.com/mastodon/mastodon/security/advisories/GHSA-7gwh-mw97-qjgp))
### Added
- Add support for processing quotes of deleted posts signaled through a `Tombstone` (#36381 by @ClearlyClaire)
### Fixed
- Fix quote post state sometimes not being updated through streaming server (#36408 by @ClearlyClaire)
- Fix inconsistent “pending tags” count on admin dashboard (#36404 by @mjankowski)
- Fix JSON payload being potentially mutated when processing interaction policies (#36392 by @ClearlyClaire)
- Fix quotes not being displayed in email notifications (#36379 by @diondiondion)
- Fix redirect to external object when URL is missing or malformed (#36347 by @ClearlyClaire)
- Fix quotes not being displayed in the featured carousel (#36335 by @diondiondion)
## [4.4.5] - 2025-09-23
### Security
- Update dependencies
### Added
- Add support for `has:quote` in search (#36217 by @ClearlyClaire)
### Changed
- Change quoted posts from silenced accounts to use a click-through rather than being hidden (#36166 and #36167 by @ClearlyClaire)
### Fixed
- Fix processing of out-of-order `Update` as implicit updates (#36190 by @ClearlyClaire)
- Fix getting `Create` and `Update` out of order (#36176 by @ClearlyClaire)
- Fix quotes with Content Warnings but no text being shown without Content Warnings (#36150 by @ClearlyClaire)
## [4.4.4] - 2025-09-16
### Security
- Update dependencies
### Fixed
- Fix missing memoization in `Web::PushNotificationWorker` (#36085 by @ClearlyClaire)
- Fix unresponsive areas around GIFV modals in some cases (#36059 by @ClearlyClaire)
- Fix missing `beforeUnload` confirmation when a poll is being authored (#36030 by @ClearlyClaire)
- Fix processing of remote edited statuses with new media and no text (#35970 by @unfokus)
- Fix polls not being displayed in moderation interface (#35644 and #35933 by @ThisIsMissEm)
- Fix WebUI handling of deleted quoted posts (#35909 and #35918 by @ClearlyClaire and @diondiondion)
- Fix “Edit” and “Delete & Redraft” on a poll not inserting empty option (#35892 by @ClearlyClaire)
- Fix loading of some compatibility CSS on some configurations (#35876 by @shleeable)
- Fix HttpLog not being enabled with `RAILS_LOG_LEVEL=debug` (#35833 by @mjankowski)
- Fix self-destruct scheduler behavior on some Redis setups (#35823 by @ClearlyClaire)
- Fix `tootctl admin create` not bypassing reserved username checks (#35779 by @ClearlyClaire)
- Fix interaction policy changes in implicit updates not being saved (#35751 by @ClearlyClaire)
- Fix quote revocation not being streamed (#35710 by @ClearlyClaire)
- Fix export of large user archives by enabling Zip64 (#35850 by @ClearlyClaire)
### Changed
- Change labels for quote policy settings (#35893 by @ClearlyClaire)
- Change standalone “Share” page to redirect to web interface after posting (#35763 by @ChaosExAnima)
## [4.4.3] - 2025-08-05
### Security

View File

@@ -48,3 +48,22 @@ 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 |

View File

@@ -159,6 +159,9 @@ group :test do
# Stub web requests for specs
gem 'webmock', '~> 3.18'
# Websocket driver for testing integration between rails/sidekiq and streaming
gem 'websocket-driver', '~> 0.8', require: false
end
group :development do

View File

@@ -10,29 +10,29 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (8.0.2)
actionpack (= 8.0.2)
activesupport (= 8.0.2)
actioncable (8.0.2.1)
actionpack (= 8.0.2.1)
activesupport (= 8.0.2.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (8.0.2)
actionpack (= 8.0.2)
activejob (= 8.0.2)
activerecord (= 8.0.2)
activestorage (= 8.0.2)
activesupport (= 8.0.2)
actionmailbox (8.0.2.1)
actionpack (= 8.0.2.1)
activejob (= 8.0.2.1)
activerecord (= 8.0.2.1)
activestorage (= 8.0.2.1)
activesupport (= 8.0.2.1)
mail (>= 2.8.0)
actionmailer (8.0.2)
actionpack (= 8.0.2)
actionview (= 8.0.2)
activejob (= 8.0.2)
activesupport (= 8.0.2)
actionmailer (8.0.2.1)
actionpack (= 8.0.2.1)
actionview (= 8.0.2.1)
activejob (= 8.0.2.1)
activesupport (= 8.0.2.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (8.0.2)
actionview (= 8.0.2)
activesupport (= 8.0.2)
actionpack (8.0.2.1)
actionview (= 8.0.2.1)
activesupport (= 8.0.2.1)
nokogiri (>= 1.8.5)
rack (>= 2.2.4)
rack-session (>= 1.0.1)
@@ -40,15 +40,15 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (8.0.2)
actionpack (= 8.0.2)
activerecord (= 8.0.2)
activestorage (= 8.0.2)
activesupport (= 8.0.2)
actiontext (8.0.2.1)
actionpack (= 8.0.2.1)
activerecord (= 8.0.2.1)
activestorage (= 8.0.2.1)
activesupport (= 8.0.2.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (8.0.2)
activesupport (= 8.0.2)
actionview (8.0.2.1)
activesupport (= 8.0.2.1)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
@@ -58,22 +58,22 @@ GEM
activemodel (>= 4.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (8.0.2)
activesupport (= 8.0.2)
activejob (8.0.2.1)
activesupport (= 8.0.2.1)
globalid (>= 0.3.6)
activemodel (8.0.2)
activesupport (= 8.0.2)
activerecord (8.0.2)
activemodel (= 8.0.2)
activesupport (= 8.0.2)
activemodel (8.0.2.1)
activesupport (= 8.0.2.1)
activerecord (8.0.2.1)
activemodel (= 8.0.2.1)
activesupport (= 8.0.2.1)
timeout (>= 0.4.0)
activestorage (8.0.2)
actionpack (= 8.0.2)
activejob (= 8.0.2)
activerecord (= 8.0.2)
activesupport (= 8.0.2)
activestorage (8.0.2.1)
actionpack (= 8.0.2.1)
activejob (= 8.0.2.1)
activerecord (= 8.0.2.1)
activesupport (= 8.0.2.1)
marcel (~> 1.0)
activesupport (8.0.2)
activesupport (8.0.2.1)
base64
benchmark (>= 0.3)
bigdecimal
@@ -494,7 +494,7 @@ GEM
tzinfo
validate_url
webfinger (~> 2.0)
openssl (3.3.0)
openssl (3.3.1)
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
opentelemetry-api (1.5.0)
@@ -642,7 +642,7 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
rack (3.1.16)
rack (3.1.18)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-cors (3.0.0)
@@ -668,20 +668,20 @@ GEM
rack (>= 1.3)
rackup (2.2.1)
rack (>= 3)
rails (8.0.2)
actioncable (= 8.0.2)
actionmailbox (= 8.0.2)
actionmailer (= 8.0.2)
actionpack (= 8.0.2)
actiontext (= 8.0.2)
actionview (= 8.0.2)
activejob (= 8.0.2)
activemodel (= 8.0.2)
activerecord (= 8.0.2)
activestorage (= 8.0.2)
activesupport (= 8.0.2)
rails (8.0.2.1)
actioncable (= 8.0.2.1)
actionmailbox (= 8.0.2.1)
actionmailer (= 8.0.2.1)
actionpack (= 8.0.2.1)
actiontext (= 8.0.2.1)
actionview (= 8.0.2.1)
activejob (= 8.0.2.1)
activemodel (= 8.0.2.1)
activerecord (= 8.0.2.1)
activestorage (= 8.0.2.1)
activesupport (= 8.0.2.1)
bundler (>= 1.15.0)
railties (= 8.0.2)
railties (= 8.0.2.1)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
@@ -692,9 +692,9 @@ GEM
rails-i18n (8.0.1)
i18n (>= 0.7, < 2)
railties (>= 8.0.0, < 9)
railties (8.0.2)
actionpack (= 8.0.2)
activesupport (= 8.0.2)
railties (8.0.2.1)
actionpack (= 8.0.2.1)
activesupport (= 8.0.2.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
@@ -725,7 +725,7 @@ GEM
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.4.1)
rexml (3.4.4)
rotp (6.3.0)
rouge (4.5.2)
rpam2 (4.0.2)
@@ -899,7 +899,7 @@ GEM
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
uri (1.0.4)
useragent (0.16.11)
validate_url (1.0.15)
activemodel (>= 3.0.0)
@@ -932,7 +932,7 @@ GEM
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.9.1)
websocket-driver (0.7.7)
websocket-driver (0.8.0)
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
@@ -1096,6 +1096,7 @@ DEPENDENCIES
webauthn (~> 3.0)
webmock (~> 3.18)
webpush!
websocket-driver (~> 0.8)
xorcist (~> 1.1)
RUBY VERSION

View File

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

View File

@@ -4,17 +4,31 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
vary_by -> { 'Signature' if authorized_fetch_mode? }
before_action :require_account_signature!, if: :authorized_fetch_mode?
before_action :check_authorization
before_action :set_items
before_action :set_size
before_action :set_type
def show
expires_in 3.minutes, public: public_fetch_mode?
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
if @unauthorized
render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
else
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
end
end
private
def check_authorization
# Because in public fetch mode we cache the response, there would be no
# benefit from performing the check below, since a blocked account or domain
# would likely be served the cache from the reverse proxy anyway
@unauthorized = authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
end
def set_items
case params[:id]
when 'featured'
@@ -57,11 +71,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
end
def for_signed_account
# Because in public fetch mode we cache the response, there would be no
# benefit from performing the check below, since a blocked account or domain
# would likely be served the cache from the reverse proxy anyway
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
if @unauthorized
[]
else
yield

View File

@@ -3,6 +3,7 @@
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!
@@ -16,6 +17,10 @@ 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

@@ -22,7 +22,7 @@ class ActivityPub::LikesController < ActivityPub::BaseController
def set_status
@status = @account.statuses.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -25,7 +25,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
def set_status
@status = @account.statuses.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -22,7 +22,7 @@ class ActivityPub::SharesController < ActivityPub::BaseController
def set_status
@status = @account.statuses.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -9,10 +9,16 @@ module Admin
@pending_appeals_count = Appeal.pending.async_count
@pending_reports_count = Report.unresolved.async_count
@pending_tags_count = Tag.pending_review.async_count
@pending_tags_count = pending_tags.async_count
@pending_users_count = User.pending.async_count
@system_checks = Admin::SystemCheck.perform(current_user)
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
end
private
def pending_tags
::Trends::TagFilter.new(status: :pending_review).results
end
end
end

View File

@@ -47,7 +47,7 @@ class Api::Fasp::BaseController < ApplicationController
provider = nil
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
provider = Fasp::Provider.find(keyid)
provider = Fasp::Provider.confirmed.find(keyid)
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
end

View File

@@ -17,7 +17,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
def set_poll
@poll = Poll.find(params[:poll_id])
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -17,7 +17,7 @@ class Api::V1::PollsController < Api::BaseController
def set_poll
@poll = Poll.find(params[:id])
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -10,7 +10,7 @@ class Api::V1::Statuses::BaseController < Api::BaseController
def set_status
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end
end

View File

@@ -23,7 +23,7 @@ class Api::V1::Statuses::BookmarksController < Api::V1::Statuses::BaseController
bookmark&.destroy!
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false })
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end
end

View File

@@ -25,7 +25,7 @@ class Api::V1::Statuses::FavouritesController < Api::V1::Statuses::BaseControlle
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }, attributes_map: { @status.id => { favourites_count: count } })
render json: @status, serializer: REST::StatusSerializer, relationships: relationships
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end
end

View File

@@ -36,7 +36,7 @@ class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }, attributes_map: { @reblog.id => { reblogs_count: count } })
render json: @reblog, serializer: REST::StatusSerializer, relationships: relationships
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end
@@ -45,7 +45,7 @@ class Api::V1::Statuses::ReblogsController < Api::V1::Statuses::BaseController
def set_reblog
@reblog = Status.find(params[:status_id])
authorize @reblog, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -129,7 +129,7 @@ class Api::V1::StatusesController < Api::BaseController
def set_status
@status = Status.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -30,7 +30,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController
def set_status
@status = Status.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end
end

View File

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

View File

@@ -21,7 +21,7 @@ class AuthorizeInteractionsController < ApplicationController
def set_resource
@resource = located_resource
authorize(@resource, :show?) if @resource.is_a?(Status)
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

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']&.split&.map { |x| x.strip.downcase }
vary = response.headers['Vary'].to_s.split(',').map { |x| x.strip.downcase }.reject(&:empty?)
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

@@ -70,10 +70,13 @@ 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

@@ -34,7 +34,7 @@ class MediaController < ApplicationController
def verify_permitted_status!
authorize @media_attachment.status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

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

View File

@@ -59,7 +59,7 @@ class StatusesController < ApplicationController
def set_status
@status = @account.statuses.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
not_found
end

View File

@@ -102,6 +102,16 @@ module ApplicationHelper
policy(record).public_send(:"#{action}?")
end
def conditional_link_to(condition, name, options = {}, html_options = {}, &block)
if condition && !current_page?(block_given? ? name : options)
link_to(name, options, html_options, &block)
elsif block_given?
content_tag(:span, options, html_options, &block)
else
content_tag(:span, name, html_options)
end
end
def material_symbol(icon, attributes = {})
safe_join(
[

View File

@@ -101,12 +101,17 @@ export const ensureComposeIsVisible = (getState) => {
};
export function setComposeToStatus(status, text, spoiler_text, content_type) {
return{
type: COMPOSE_SET_STATUS,
status,
text,
spoiler_text,
content_type,
return (dispatch, getState) => {
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
dispatch({
type: COMPOSE_SET_STATUS,
status,
text,
spoiler_text,
content_type,
maxOptions
});
};
}
@@ -193,8 +198,9 @@ export function directCompose(account) {
/**
* @param {null | string} overridePrivacy
* @param {undefined | Function} successCallback
*/
export function submitCompose(overridePrivacy = null) {
export function submitCompose(overridePrivacy = null, successCallback = undefined) {
return function (dispatch, getState) {
let status = getState().getIn(['compose', 'text'], '');
const media = getState().getIn(['compose', 'media_attachments']);
@@ -259,6 +265,9 @@ export function submitCompose(overridePrivacy = null) {
dispatch(insertIntoTagHistory(response.data.tags, status));
dispatch(submitComposeSuccess({ ...response.data }));
if (typeof successCallback === 'function') {
successCallback(response.data);
}
// To make the app more responsive, immediately push the status
// into the columns

View File

@@ -21,6 +21,15 @@ export function normalizeFilterResult(result) {
return normalResult;
}
function stripQuoteFallback(text) {
const wrapper = document.createElement('div');
wrapper.innerHTML = text;
wrapper.querySelector('.quote-inline')?.remove();
return wrapper.innerHTML;
}
export function normalizeStatus(status, normalOldStatus, settings) {
const normalStatus = { ...status };
@@ -78,6 +87,11 @@ export function normalizeStatus(status, normalOldStatus, settings) {
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
normalStatus.hidden = (spoilerText.length > 0 || normalStatus.sensitive) && autoHideCW(settings, spoilerText);
// Remove quote fallback link from the DOM so it doesn't mess with paragraph margins
if (normalStatus.quote) {
normalStatus.contentHtml = stripQuoteFallback(normalStatus.contentHtml);
}
if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) {
normalStatus.url = null;
}
@@ -117,6 +131,11 @@ export function normalizeStatusTranslation(translation, status) {
spoiler_text: translation.spoiler_text,
};
// Remove quote fallback link from the DOM so it doesn't mess with paragraph margins
if (status.get('quote')) {
normalTranslation.contentHtml = stripQuoteFallback(normalTranslation.contentHtml);
}
return normalTranslation;
}

View File

@@ -3,7 +3,7 @@ import { browserHistory } from 'flavours/glitch/components/router';
import api from '../api';
import { ensureComposeIsVisible, setComposeToStatus } from './compose';
import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer';
import { importFetchedStatus, importFetchedAccount } from './importer';
import { fetchContext } from './statuses_typed';
import { deleteFromTimelines } from './timelines';
@@ -48,7 +48,18 @@ export function fetchStatusRequest(id, skipLoading) {
};
}
export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
/**
* @param {string} id
* @param {Object} [options]
* @param {boolean} [options.forceFetch]
* @param {boolean} [options.alsoFetchContext]
* @param {string | null | undefined} [options.parentQuotePostId]
*/
export function fetchStatus(id, {
forceFetch = false,
alsoFetchContext = true,
parentQuotePostId,
} = {}) {
return (dispatch, getState) => {
const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
@@ -66,7 +77,9 @@ export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
dispatch(importFetchedStatus(response.data));
dispatch(fetchStatusSuccess(skipLoading));
}).catch(error => {
dispatch(fetchStatusFail(id, error, skipLoading));
dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId));
if (error.status === 404)
dispatch(deleteFromTimelines(id));
});
};
}
@@ -78,22 +91,28 @@ export function fetchStatusSuccess(skipLoading) {
};
}
export function fetchStatusFail(id, error, skipLoading) {
export function fetchStatusFail(id, error, skipLoading, parentQuotePostId) {
return {
type: STATUS_FETCH_FAIL,
id,
error,
parentQuotePostId,
skipLoading,
skipAlert: true,
};
}
export function redraft(status, raw_text, content_type) {
return {
type: REDRAFT,
status,
raw_text,
content_type,
return (dispatch, getState) => {
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
dispatch({
type: REDRAFT,
status,
raw_text,
content_type,
maxOptions,
});
};
}

View File

@@ -1,10 +1,11 @@
import { apiRequestPost } from 'flavours/glitch/api';
import type { Status, StatusVisibility } from 'flavours/glitch/models/status';
import type { ApiStatusJSON } from 'flavours/glitch/api_types/statuses';
import type { StatusVisibility } from 'flavours/glitch/models/status';
export const apiReblog = (statusId: string, visibility: StatusVisibility) =>
apiRequestPost<{ reblog: Status }>(`v1/statuses/${statusId}/reblog`, {
apiRequestPost<{ reblog: ApiStatusJSON }>(`v1/statuses/${statusId}/reblog`, {
visibility,
});
export const apiUnreblog = (statusId: string) =>
apiRequestPost<Status>(`v1/statuses/${statusId}/unreblog`);
apiRequestPost<ApiStatusJSON>(`v1/statuses/${statusId}/unreblog`);

View File

@@ -20,7 +20,7 @@ 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 StatusContainer from '@/flavours/glitch/containers/status_container';
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';
@@ -218,12 +218,7 @@ const FeaturedCarouselItem: React.FC<
ref={handleRef}
{...props}
>
<StatusContainer
// @ts-expect-error inferred props are wrong
id={statusId}
contextType='account'
withCounters
/>
<StatusQuoteManager id={statusId} contextType='account' withCounters />
</animated.div>
);
};

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
@@ -12,12 +12,15 @@ import ArticleIcon from '@/material-icons/400-24px/article.svg?react';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { Icon } from 'flavours/glitch/components/icon';
import StatusContainer from 'flavours/glitch/containers/status_container';
import { domain } from 'flavours/glitch/initial_state';
import type { Status } from 'flavours/glitch/models/status';
import type { RootState } from 'flavours/glitch/store';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
import { revealAccount } from '../actions/accounts_typed';
import { fetchStatus } from '../actions/statuses';
import { makeGetStatus } from '../selectors';
import { getAccountHidden } from '../selectors/accounts';
const MAX_QUOTE_POSTS_NESTING_LEVEL = 1;
@@ -37,9 +40,7 @@ const QuoteWrapper: React.FC<{
);
};
const NestedQuoteLink: React.FC<{
status: Status;
}> = ({ status }) => {
const NestedQuoteLink: React.FC<{ status: Status }> = ({ status }) => {
const accountId = status.get('account') as string;
const account = useAppSelector((state) =>
accountId ? state.accounts.get(accountId) : undefined,
@@ -75,24 +76,74 @@ type GetStatusSelector = (
props: { id?: string | null; contextType?: string },
) => Status | null;
const LimitedAccountHint: React.FC<{ accountId: string }> = ({ accountId }) => {
const dispatch = useAppDispatch();
const reveal = useCallback(() => {
dispatch(revealAccount({ id: accountId }));
}, [dispatch, accountId]);
return (
<>
<FormattedMessage
id='status.quote_error.limited_account_hint.title'
defaultMessage='This account has been hidden by the moderators of {domain}.'
values={{ domain }}
/>
<button onClick={reveal} className='link-button'>
<FormattedMessage
id='status.quote_error.limited_account_hint.action'
defaultMessage='Show anyway'
/>
</button>
</>
);
};
export const QuotedStatus: React.FC<{
quote: QuoteMap;
contextType?: string;
parentQuotePostId?: string | null;
variant?: 'full' | 'link';
nestingLevel?: number;
}> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => {
}> = ({
quote,
contextType,
parentQuotePostId,
nestingLevel = 1,
variant = 'full',
}) => {
const dispatch = useAppDispatch();
const quoteState = useAppSelector((state) =>
parentQuotePostId
? state.statuses.getIn([parentQuotePostId, 'quote', 'state'])
: quote.get('state'),
);
const quotedStatusId = quote.get('quoted_status');
const quoteState = quote.get('state');
const status = useAppSelector((state) =>
quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
);
const shouldLoadQuote = !status?.get('isLoading') && quoteState !== 'deleted';
const accountId: string | null = status?.get('account', null) as
| string
| null;
const hiddenAccount = useAppSelector(
(state) => accountId && getAccountHidden(state, accountId),
);
useEffect(() => {
if (!status && quotedStatusId) {
dispatch(fetchStatus(quotedStatusId));
if (shouldLoadQuote && quotedStatusId) {
dispatch(
fetchStatus(quotedStatusId, {
parentQuotePostId,
alsoFetchContext: false,
}),
);
}
}, [status, quotedStatusId, dispatch]);
}, [shouldLoadQuote, quotedStatusId, parentQuotePostId, dispatch]);
// In order to find out whether the quoted post should be completely hidden
// due to a matching filter, we run it through the selector used by `status_container`.
@@ -147,6 +198,8 @@ export const QuotedStatus: React.FC<{
defaultMessage='This post cannot be displayed.'
/>
);
} else if (hiddenAccount && accountId) {
quoteError = <LimitedAccountHint accountId={accountId} />;
}
if (quoteError) {
@@ -173,6 +226,7 @@ export const QuotedStatus: React.FC<{
{canRenderChildQuote && (
<QuotedStatus
quote={childQuote}
parentQuotePostId={quotedStatusId}
contextType={contextType}
variant={
nestingLevel === MAX_QUOTE_POSTS_NESTING_LEVEL ? 'link' : 'full'
@@ -209,7 +263,11 @@ export const StatusQuoteManager = (props: StatusQuoteManagerProps) => {
return (
/* @ts-expect-error Status is not yet typed */
<StatusContainer {...props}>
<QuotedStatus quote={quote} contextType={props.contextType} />
<QuotedStatus
quote={quote}
parentQuotePostId={status?.get('id') as string}
contextType={props.contextType}
/>
</StatusContainer>
);
}

View File

@@ -81,6 +81,7 @@ class ComposeForm extends ImmutablePureComponent {
singleColumn: PropTypes.bool,
lang: PropTypes.string,
maxChars: PropTypes.number,
redirectOnSuccess: PropTypes.bool,
};
static defaultProps = {
@@ -336,7 +337,7 @@ class ComposeForm extends ImmutablePureComponent {
>
{intl.formatMessage(
this.props.isEditing ?
messages.saveChanges :
messages.saveChanges :
(this.props.isInReply ? messages.reply : messages.publish)
)}
</Button>

View File

@@ -56,7 +56,7 @@ const mapStateToProps = state => ({
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = (dispatch, props) => ({
onChange (text) {
dispatch(changeCompose(text));
@@ -69,7 +69,11 @@ const mapDispatchToProps = (dispatch) => ({
modalProps: { overridePrivacy },
}));
} else {
dispatch(submitCompose(overridePrivacy));
dispatch(submitCompose(overridePrivacy, (status) => {
if (props.redirectOnSuccess) {
window.location.assign(status.url);
}
}));
}
},

View File

@@ -5,7 +5,7 @@ import ModalContainer from 'flavours/glitch/features/ui/containers/modal_contain
const Compose = () => (
<>
<ComposeFormContainer autoFocus withoutNavigation />
<ComposeFormContainer autoFocus withoutNavigation redirectOnSuccess />
<AlertsController />
<ModalContainer />
<LoadingBarContainer className='loading-bar' />

View File

@@ -38,7 +38,7 @@ const Embed: React.FC<{ id: string }> = ({ id }) => {
const dispatchRenderSignal = useRenderSignal();
useEffect(() => {
dispatch(fetchStatus(id, false, false));
dispatch(fetchStatus(id, { alsoFetchContext: false }));
}, [dispatch, id]);
const handleToggleHidden = useCallback(() => {

View File

@@ -26,18 +26,23 @@ const getHostname = url => {
const domParser = new DOMParser();
const addAutoPlay = html => {
const handleIframeUrl = (html, url, providerName) => {
const document = domParser.parseFromString(html, 'text/html').documentElement;
const iframe = document.querySelector('iframe');
const startTime = new URL(url).searchParams.get('t')
if (iframe) {
if (iframe.src.indexOf('?') !== -1) {
iframe.src += '&';
} else {
iframe.src += '?';
const iframeUrl = new URL(iframe.src)
iframeUrl.searchParams.set('autoplay', 1)
iframeUrl.searchParams.set('auto_play', 1)
if (providerName === 'YouTube') {
iframeUrl.searchParams.set('start', startTime || '');
iframe.referrerPolicy = 'strict-origin-when-cross-origin';
}
iframe.src += 'autoplay=1&auto_play=1';
iframe.src = iframeUrl.href
// DOM parser creates html/body elements around original HTML fragment,
// so we need to get innerHTML out of the body and not the entire document
@@ -103,7 +108,7 @@ export default class Card extends PureComponent {
renderVideo () {
const { card } = this.props;
const content = { __html: addAutoPlay(card.get('html')) };
const content = { __html: handleIframeUrl(card.get('html'), card.get('url'), card.get('provider_name')) };
return (
<div

View File

@@ -418,7 +418,11 @@ export const DetailedStatus: React.FC<{
{hashtagBar}
{status.get('quote') && (
<QuotedStatus quote={status.get('quote')} />
<QuotedStatus
quote={status.get('quote')}
parentQuotePostId={status.get('id')}
contextType='thread'
/>
)}
</>
)}

View File

@@ -38,7 +38,7 @@ class FilterModal extends ImmutablePureComponent {
handleSuccess = () => {
const { dispatch, statusId } = this.props;
dispatch(fetchStatus(statusId, true));
dispatch(fetchStatus(statusId, {forceFetch: true}));
this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
};

View File

@@ -201,8 +201,6 @@ class MediaModal extends ImmutablePureComponent {
preview={image.get('preview_url')}
blurhash={image.get('blurhash')}
src={image.get('url')}
width={image.get('width')}
height={image.get('height')}
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
startTime={currentTime || 0}
startPlaying={autoPlay || false}
@@ -218,8 +216,6 @@ class MediaModal extends ImmutablePureComponent {
return (
<GIFV
src={image.get('url')}
width={width}
height={height}
key={image.get('url')}
alt={description}
lang={lang}

View File

@@ -92,8 +92,7 @@ const messages = defineMessages({
const mapStateToProps = state => ({
layout: state.getIn(['meta', 'layout']),
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
hasComposingContents: state.getIn(['compose', 'text']).trim().length !== 0 || state.getIn(['compose', 'media_attachments']).size > 0 || state.getIn(['compose', 'poll']) !== null,
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
isWide: state.getIn(['local_settings', 'stretch']),
fullWidthColumns: state.getIn(['local_settings', 'fullwidth_columns']),
@@ -284,8 +283,7 @@ class UI extends PureComponent {
fullWidthColumns: PropTypes.bool,
systemFontUi: PropTypes.bool,
isComposing: PropTypes.bool,
hasComposingText: PropTypes.bool,
hasMediaAttachments: PropTypes.bool,
hasComposingContents: PropTypes.bool,
canUploadMore: PropTypes.bool,
intl: PropTypes.object.isRequired,
unreadNotifications: PropTypes.number,
@@ -304,11 +302,11 @@ class UI extends PureComponent {
};
handleBeforeUnload = e => {
const { intl, dispatch, hasComposingText, hasMediaAttachments } = this.props;
const { intl, dispatch, hasComposingContents } = this.props;
dispatch(synchronouslySubmitMarkers());
if (hasComposingText || hasMediaAttachments) {
if (hasComposingContents) {
// Setting returnValue to any string causes confirmation dialog.
// Many browsers no longer display this text to users,
// but we set user-friendly message for other browsers, e.g. Edge.

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "عرض الملف الشخصي كاملاً",
"boost_modal.missing_description": "هذا المنشور يحتوي على وسائط بلا وصف",
"column.favourited_by": "المفضلة من قبل",
"column.heading": "متنوعة",
"column.reblogged_by": "المرقى من قبل",
"column.subheading": "خيارات متنوعة",
"column_header.profile": "الملف الشخصي",
"column_subheading.lists": "القوائم",
"column_subheading.navigation": "التنقل",
"community.column_settings.allow_local_only": "إظهار المنشورات المحلية فقط",
"compose.attach.doodle": "الرسوم و التخمين",
"compose.change_federation": "تغيير اعدادات الفيديرالية",
@@ -36,8 +32,6 @@
"keyboard_shortcuts.secondary_toot": "لإرسال التبويق باستخدام إعدادات الخصوصية الثانوية",
"moved_to_warning": "عُلِّم هذا الحساب بأنه انتقل إلى {moved_to_link}، لذا قد لا يقبل متابعات جديدة.",
"navigation_bar.app_settings": "إعدادات التطبيق",
"navigation_bar.keyboard_shortcuts": "اختصارات لوحة المفاتيح",
"navigation_bar.misc": "متنوع",
"settings.always_show_spoilers_field": "تمكين دائما حقل تحذير المحتوى",
"settings.close": "إغلاق",
"settings.content_warnings": "Content warnings",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Zobrazit celý profil",
"boost_modal.missing_description": "Příspěvek obsahuje obrázky bez popisků",
"column.favourited_by": "Oblíbeno uživatelem",
"column.heading": "Různé",
"column.reblogged_by": "Boostnuto uživatelem",
"column.subheading": "Různé",
"column_header.profile": "Profil",
"column_subheading.lists": "Seznamy",
"column_subheading.navigation": "Navigace",
"community.column_settings.allow_local_only": "Zobrazit pouze místní tooty",
"compose.attach.doodle": "Nakreslete něco",
"compose.change_federation": "Změnit nastavení federace",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "pro odeslání příspěvku s sekundárním nastavením soukromí",
"moved_to_warning": "Tento účet je označen jako přesunut na {moved_to_link}, a proto nemusí přijímat nové sledování.",
"navigation_bar.app_settings": "Nastavení aplikace",
"navigation_bar.keyboard_shortcuts": "Klávesové zkratky",
"navigation_bar.misc": "Různé",
"notifications.column_settings.filter_bar.show_bar": "Zobrazit panel filtrů",
"settings.always_show_spoilers_field": "Vždy zobrazit pole pro varování o obsahu",
"settings.close": "Zavřít",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Dangos proffil cyfan",
"boost_modal.missing_description": "Mae'r tŵt yma'n cynnwys ychydig gyfryngau heb ddisgrifiad",
"column.favourited_by": "Wedi'i hoffi gan",
"column.heading": "Misg",
"column.reblogged_by": "Wedi'i bŵstio gan",
"column.subheading": "Opsiynnau arall",
"column_header.profile": "Proffil",
"column_subheading.lists": "Rhestri",
"column_subheading.navigation": "Llywio",
"community.column_settings.allow_local_only": "Dangos tŵtiau lleol yn unig",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",

View File

@@ -2,7 +2,6 @@
"home.column_settings.advanced": "Avanceret",
"home.column_settings.show_direct": "Vis private omtaler",
"navigation_bar.app_settings": "Appindstillinger",
"navigation_bar.misc": "Diverse",
"settings.always_show_spoilers_field": "Vis altid feltet til indholdsadvarsel",
"settings.close": "Luk",
"settings.content_warnings": "Indholdsadvarsler",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Vollständiges Profil anzeigen",
"boost_modal.missing_description": "Dieser Toot enthält Medien ohne Beschreibung",
"column.favourited_by": "Favorisiert von",
"column.heading": "Sonstiges",
"column.reblogged_by": "Geteilt von",
"column.subheading": "Sonstige Optionen",
"column_header.profile": "Profil",
"column_subheading.lists": "Listen",
"column_subheading.navigation": "Navigation",
"community.column_settings.allow_local_only": "Nur-lokale Toots anzeigen",
"compose.attach.doodle": "Male etwas",
"compose.change_federation": "Föderationseinstellungen ändern",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "Toot mit sekundärer Privatsphäreeinstellung absenden",
"moved_to_warning": "Dieses Konto ist als verschoben zu {moved_to_link} markiert und akzeptiert daher keine neuen Follower.",
"navigation_bar.app_settings": "App-Einstellungen",
"navigation_bar.keyboard_shortcuts": "Tastaturkürzel",
"navigation_bar.misc": "Sonstiges",
"notifications.column_settings.filter_bar.show_bar": "Filterleiste anzeigen",
"settings.always_show_spoilers_field": "Das Inhaltswarnungs-Feld immer aktivieren",
"settings.close": "Schließen",

View File

@@ -1,4 +1,92 @@
{
"settings.content_warnings": "Content warnings",
"settings.preferences": "Preferences"
"about.fork_disclaimer": "Το Glitch-soc είναι ελεύθερο λογισμικό ανοιχτού κώδικα που είναι αντιγραφή από το Mastodon.",
"account.disclaimer_full": "Οι παρακάτω πληροφορίες μπορεί να αντικατοπτρίζουν το προφίλ του χρήστη ελλιπώς.",
"account.follows": "Ακολουθεί",
"account.suspended_disclaimer_full": "Αυτός ο χρήστης έχει ανασταλεί από έναν συντονιστή.",
"account.view_full_profile": "Προβολή πλήρους προφίλ",
"boost_modal.missing_description": "Αυτό το τουτ περιέχει κάποια μέσα χωρίς περιγραφή",
"column.favourited_by": "Αγαπήθηκε από",
"column.reblogged_by": "Ενισχύθηκε από",
"column_header.profile": "Προφίλ",
"community.column_settings.allow_local_only": "Εμφάνιση τοπικών τουτ",
"compose.attach.doodle": "Σχεδίασε κάτι",
"compose.change_federation": "Αλλαγή ρυθμίσεων ομοσπονδίας",
"compose.content-type.change": "Αλλαγή προηγμένων επιλογών μορφοποίησης",
"compose.content-type.html": "HTML",
"compose.content-type.html_meta": "Μορφοποίηση των αναρτήσεων σας με χρήση HTML",
"compose.content-type.markdown": "Markdown",
"compose.content-type.markdown_meta": "Μορφοποίηση των αναρτήσεων σας με χρήση Markdown",
"compose.content-type.plain": "Απλό κείμενο",
"compose.content-type.plain_meta": "Γράψιμο χωρίς προηγμένη μορφοποίηση",
"compose.disable_threaded_mode": "Απενεργοποίηση λειτουργίας νημάτων",
"compose.enable_threaded_mode": "Ενεργοποίηση λειτουργίας νημάτων",
"compose_form.sensitive.hide": "{count, plural, one {Επισήμανση πολυμέσου ως ευαίσθητο} other {Επισήμανση πολυμέσων ως ευαίσθητα}}",
"compose_form.sensitive.marked": "{count, plural, one {Το πολυμέσο έχει σημανθεί ως ευαίσθητο} other {Τα πολυμέσα έχουν σημανθεί ως ευαίσθητα}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Το πολυμέσο δεν έχει σημανθεί ως ευαίσθητο} other {Τα πολυμέσα δεν έχουν σημανθεί ως ευαίσθητα}}",
"confirmation_modal.do_not_ask_again": "Να μην ζητηθεί επιβεβαίωση ξανά",
"confirmations.deprecated_settings.confirm": "Χρήση προτιμήσεων του Mastodon",
"direct.group_by_conversations": "Ομαδοποίηση ανά συζήτηση",
"favourite_modal.favourite": "Αγάπησε την ανάρτηση;",
"federation.federated.long": "Επιτρέψτε σε αυτήν την ανάρτηση να φτάσει σε άλλους διακομιστές",
"federation.local_only.long": "Αποτρέψτε αυτήν την ανάρτηση να φτάσει σε άλλους διακομιστές",
"federation.local_only.short": "Τοπικά μόνο",
"firehose.column_settings.allow_local_only": "Εμφάνιση τοπικών αναρτήσεων σε \"Όλα\"",
"home.column_settings.advanced": "Για προχωρημένους",
"home.column_settings.filter_regex": "Φιλτράρισμα βάσει των κανονικών εκφράσεων",
"home.column_settings.show_direct": "Εμφάνιση ιδιωτικών επισημάνσεων",
"home.settings": "Ρυθμίσεις στήλης",
"keyboard_shortcuts.bookmark": "για να σελιδοποιήσει",
"keyboard_shortcuts.secondary_toot": "για αποστολή τουτ χρησιμοποιώντας δευτερεύουσα ρύθμιση απορρήτου",
"moved_to_warning": "Αυτός ο λογαριασμός έχει σημανθεί ως μετακινημένος στο {moved_to_link} και έτσι δεν μπορεί να δεχτεί νέους ακολούθους.",
"navigation_bar.app_settings": "Ρυθμίσεις εφαρμογής",
"notifications.column_settings.filter_bar.show_bar": "Εμφάνιση μπάρας φίλτρου",
"settings.always_show_spoilers_field": "Πάντα ενεργοποίηση του πεδίου Προειδοποίηση Περιεχομένου",
"settings.close": "Κλείσιμο",
"settings.compose_box_opts": "Πλαίσιο σύνθεσης",
"settings.content_warnings": "Προειδοποιήσεις περιεχομένου",
"settings.content_warnings.regexp": "Κανονική έκφραση (regex)",
"settings.content_warnings_filter": "Προειδοποιήσεις περιεχομένου να μην ξεδιπλώνονται αυτόματα:",
"settings.content_warnings_shared_state": "Εμφάνιση/απόκρυψη περιεχομένου όλων των αντιγράφων ταυτόχρονα",
"settings.content_warnings_unfold_opts": "Επιλογές αυτόματου ξεδιπλώματος",
"settings.deprecated_setting": "Αυτή η ρύθμιση τώρα ελέγχεται από τις {settings_page_link} του Mastodon",
"settings.enable_content_warnings_auto_unfold": "Αυτόματη ξεδίπλωμα προειδοποιήσεων περιεχομένου",
"settings.general": "Γενικά",
"settings.layout_opts": "Επιλογές διάταξης",
"settings.media": "Πολυμέσα",
"settings.notifications.tab_badge": "Σήμα μη αναγνωσμένων ειδοποιήσεων",
"settings.notifications.tab_badge.hint": "Εμφανίζει ένα σήμα για μη αναγνωσμένες ειδοποιήσεις στα εικονίδια της στήλης όταν η στήλη ειδοποιήσεων δεν είναι ανοιχτή",
"settings.notifications_opts": "Ρυθμίσεις ειδοποιήσεων",
"settings.pop_in_left": "Αριστερά",
"settings.pop_in_right": "Δεξιά",
"settings.preferences": "Προτιμήσεις χρήστη",
"settings.preselect_on_reply": "Προεπιλογή ονομάτων χρηστών στην απάντηση",
"settings.preselect_on_reply_hint": "Όταν απαντάτε σε μια συνομιλία με πολλαπλούς συμμετέχοντες, προεπιλέξτε τα ονόματα χρηστών μετά τον πρώτο",
"settings.rewrite_mentions": "Ξαναγράψε επισημάνσεις στις προβαλλόμενες καταστάσεις",
"settings.rewrite_mentions_acct": "Ξαναγράψε με όνομα χρήστη και τομέα (όταν ο λογαριασμός είναι απομακρυσμένος)",
"settings.rewrite_mentions_no": "Μην ξαναγράψεις επισημάνσεις",
"settings.rewrite_mentions_username": "Ξαναγράψε με όνομα χρήστη",
"settings.shared_settings_link": "προτιμήσεις χρήστη",
"settings.side_arm": "Δευτερεύον κουμπί τουτ:",
"settings.side_arm.none": "Κανένα",
"settings.side_arm_reply_mode": "Όταν απαντάτε σε ένα τουτ, το κουμπί δευτερεύοντος τουτ πρέπει να:",
"settings.side_arm_reply_mode.copy": "Αντιγράψει τη ρύθμιση απορρήτου του τουτ που απαντάται",
"settings.side_arm_reply_mode.keep": "Διατηρήσει το καθορισμένο απόρρητο",
"settings.side_arm_reply_mode.restrict": "Περιορίσει τη ρύθμιση απορρήτου σε εκείνη του τουτ που απαντάται",
"settings.status_icons": "Εικονίδια τουτ",
"settings.status_icons_language": "Ένδειξη γλώσσας",
"settings.status_icons_local_only": "Ένδειξη τοπικό μόνο",
"settings.status_icons_media": "Ενδείξεις μέσων και δημοσκοπήσεων",
"settings.status_icons_reply": "Ένδειξη απαντήσεων",
"settings.status_icons_visibility": "Ένδειξη απορρήτου τουτ",
"settings.tag_misleading_links": "Σήμανση παραπλανητικών συνδέσμων",
"settings.tag_misleading_links.hint": "Προσθήκη οπτικής ένδειξης με τον εξυπηρετητή στόχο του συνδέσμου σε κάθε σύνδεσμο που δεν αναφέρεται ρητά",
"status.filtered": "Φιλτραρισμένο",
"status.has_audio": "Διαθέτει συνημμένα αρχεία ήχου",
"status.has_pictures": "Διαθέτει συνημμένες εικόνες",
"status.has_preview_card": "Διαθέτει συνημμένη κάρτα προεπισκόπησης",
"status.has_video": "Διαθέτει συνημμένα βίντεο",
"status.hide": "Απόκρυψη ανάρτησης",
"status.in_reply_to": "Αυτό το τουτ είναι απάντηση",
"status.is_poll": "Αυτό το τουτ είναι δημοσκόπηση",
"status.show_filter_reason": "Εμφάνιση ούτως ή άλλως"
}

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Vidi plenan profilon",
"boost_modal.missing_description": "Ĉi tiu afiŝo enhavas plurmedion, ke ne havas priskribon",
"column.favourited_by": "Stelumita per",
"column.heading": "Diversaj aferoj",
"column.reblogged_by": "Diskonigita de",
"column.subheading": "Diversaj agordoj",
"column_header.profile": "Profilo",
"column_subheading.lists": "Listoj",
"column_subheading.navigation": "Navigado",
"community.column_settings.allow_local_only": "Montri nur-lokajn afiŝojn",
"compose.attach.doodle": "Skribu ion",
"compose.change_federation": "Ŝanĝi agordojn de federacio",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "sendi afiŝon per dua agordo de privateco",
"moved_to_warning": "Ĉi tiun konton sigelas, kiel movinta al {moved_to_link}, kaj pro ne permesos novajn sekvantojn.",
"navigation_bar.app_settings": "Agordoj de aplikaĵo",
"navigation_bar.keyboard_shortcuts": "Fulmoklavoj",
"navigation_bar.misc": "Aliaj",
"notifications.column_settings.filter_bar.show_bar": "Montri mezuron de filtrilo",
"settings.always_show_spoilers_field": "Ĉiam ŝaltiĝu la arealo de Enhava Averto",
"settings.close": "Fermi",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Ver perfil completo",
"boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
"column.favourited_by": "Marcado como favorito por",
"column.heading": "Misc",
"column.reblogged_by": "Impulsado por",
"column.subheading": "Opciones misceláneas",
"column_header.profile": "Perfil",
"column_subheading.lists": "Listas",
"column_subheading.navigation": "Navegación",
"community.column_settings.allow_local_only": "Mostrar sólo toots locales",
"compose.attach.doodle": "Dibujá algo",
"compose.change_federation": "Cambiar configuración de la federación",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
"moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
"navigation_bar.app_settings": "Ajustes de aplicación",
"navigation_bar.keyboard_shortcuts": "Atajos de teclado",
"navigation_bar.misc": "Misc",
"notifications.column_settings.filter_bar.show_bar": "Mostrar barra de filtros",
"settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
"settings.close": "Cerrar",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Ver perfil completo",
"boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
"column.favourited_by": "Marcado como favorito por",
"column.heading": "Misc",
"column.reblogged_by": "Impulsado por",
"column.subheading": "Opciones misceláneas",
"column_header.profile": "Perfil",
"column_subheading.lists": "Listas",
"column_subheading.navigation": "Navegación",
"community.column_settings.allow_local_only": "Mostrar sólo toots locales",
"compose.attach.doodle": "Dibujar algo",
"compose.change_federation": "Cambiar configuración de la federación",
@@ -41,8 +37,6 @@
"keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
"moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
"navigation_bar.app_settings": "Ajustes de aplicación",
"navigation_bar.keyboard_shortcuts": "Atajos de teclado",
"navigation_bar.misc": "Misc",
"settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
"settings.close": "Cerrar",
"settings.compose_box_opts": "Cuadro de redacción",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Ver perfil completo",
"boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
"column.favourited_by": "Marcado como favorito por",
"column.heading": "Misc",
"column.reblogged_by": "Impulsado por",
"column.subheading": "Opciones misceláneas",
"column_header.profile": "Perfil",
"column_subheading.lists": "Listas",
"column_subheading.navigation": "Navegación",
"community.column_settings.allow_local_only": "Mostrar toots solo-locales",
"compose.attach.doodle": "Dibujar algo",
"compose.change_federation": "Cambiar configuración de la federación",
@@ -41,8 +37,6 @@
"keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
"moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
"navigation_bar.app_settings": "Ajustes de la aplicación",
"navigation_bar.keyboard_shortcuts": "Atajos de teclado",
"navigation_bar.misc": "Misc",
"settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
"settings.close": "Cerrar",
"settings.compose_box_opts": "Cuadro de redacción",

View File

@@ -8,7 +8,6 @@
"compose.content-type.plain": "متن ساده",
"home.column_settings.advanced": "پیشرفته",
"navigation_bar.app_settings": "تنظیمات کاره",
"navigation_bar.keyboard_shortcuts": "میان‌برهای صفحه‌کلید",
"settings.close": "بستن",
"settings.content_warnings": "هشدارهای محتوا",
"settings.media": "رسانه",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Voir le profil complet",
"boost_modal.missing_description": "Ce post contient des médias sans description",
"column.favourited_by": "Ajouté en favori par",
"column.heading": "Divers",
"column.reblogged_by": "Partagé par",
"column.subheading": "Autres options",
"column_header.profile": "Profil",
"column_subheading.lists": "Listes",
"column_subheading.navigation": "Navigation",
"community.column_settings.allow_local_only": "Afficher seulement les posts locaux",
"compose.attach.doodle": "Dessinez quelque chose",
"compose.change_federation": "Changer les paramètres de fédération",
@@ -24,6 +20,9 @@
"compose.content-type.plain_meta": "Écrire sans formatage avancé",
"compose.disable_threaded_mode": "Désactiver le mode thread",
"compose.enable_threaded_mode": "Activer le mode thread",
"compose_form.sensitive.hide": "{count, plural, one {Marquer le média comme sensible} other {Marquer les médias comme sensibles}}",
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Le média nest pas marqué comme sensible} other {Les médias ne sont pas marqué comme sensible}}",
"confirmation_modal.do_not_ask_again": "Ne plus demander confirmation",
"confirmations.deprecated_settings.confirm": "Utiliser les préférences de Mastodon",
"confirmations.deprecated_settings.message": "Certaines {app_settings} de glitch-soc que vous utilisez ont été remplacées par les {preferences} de Mastodon et seront remplacées :",
@@ -42,8 +41,7 @@
"keyboard_shortcuts.secondary_toot": "Envoyer le post en utilisant les paramètres secondaires de confidentialité",
"moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
"navigation_bar.app_settings": "Paramètres de l'application",
"navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
"navigation_bar.misc": "Autres",
"notifications.column_settings.filter_bar.show_bar": "Afficher la barre de filtre",
"settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
"settings.close": "Fermer",
"settings.compose_box_opts": "Zone de rédaction",
@@ -57,6 +55,8 @@
"settings.content_warnings_unfold_opts": "Options de dépliement automatique",
"settings.deprecated_setting": "Cette option est maintenant définie par les {settings_page_link} de Mastodon",
"settings.enable_content_warnings_auto_unfold": "Déplier automatiquement les avertissements de contenu",
"settings.fullwidth_view": "Étirer les colonnes sur toute la largeur (mode bureau uniquement)",
"settings.fullwidth_view_hint": "Étire les colonnes pour remplir tout l'espace disponible.",
"settings.general": "Général",
"settings.hicolor_privacy_icons": "Indicateurs de confidentialité en couleurs",
"settings.hicolor_privacy_icons.hint": "Affiche les indicateurs de confidentialité dans des couleurs facilement distinguables",
@@ -105,11 +105,14 @@
"settings.tag_misleading_links.hint": "Ajouter une indication visuelle avec l'hôte cible du lien à chaque lien ne le mentionnant pas explicitement",
"settings.wide_view": "Vue élargie (mode ordinateur uniquement)",
"settings.wide_view_hint": "Étire les colonnes pour mieux remplir l'espace disponible.",
"status.filtered": "Filtré",
"status.has_audio": "Contient des fichiers audio attachés",
"status.has_pictures": "Contient des images attachées",
"status.has_preview_card": "Contient une carte de prévisualisation attachée",
"status.has_video": "Contient des vidéos attachées",
"status.hide": "Masquer la publication",
"status.in_reply_to": "Ce post est une réponse",
"status.is_poll": "Ce post est un sondage",
"status.local_only": "Visible uniquement depuis votre instance"
"status.local_only": "Visible uniquement depuis votre instance",
"status.show_filter_reason": "Afficher quand même"
}

View File

@@ -2,16 +2,12 @@
"about.fork_disclaimer": "Glitch-soc est un logiciel gratuit et open source, fork de Mastodon.",
"account.disclaimer_full": "Les informations ci-dessous peuvent être incomplètes.",
"account.follows": "Abonnements",
"account.suspended_disclaimer_full": "Cet utilisateur a été suspendu par un modérateur.",
"account.suspended_disclaimer_full": "Ce compte a été suspendu par un modérateur.",
"account.view_full_profile": "Voir le profil complet",
"boost_modal.missing_description": "Ce post contient des médias sans description",
"column.favourited_by": "Ajouté en favori par",
"column.heading": "Divers",
"column.reblogged_by": "Partagé par",
"column.subheading": "Autres options",
"column_header.profile": "Profil",
"column_subheading.lists": "Listes",
"column_subheading.navigation": "Navigation",
"community.column_settings.allow_local_only": "Afficher seulement les posts locaux",
"compose.attach.doodle": "Dessinez quelque chose",
"compose.change_federation": "Changer les paramètres de fédération",
@@ -20,10 +16,13 @@
"compose.content-type.html_meta": "Formatez vos messages en HTML",
"compose.content-type.markdown": "Markdown",
"compose.content-type.markdown_meta": "Formatez vos messages en Markdown",
"compose.content-type.plain": "Text brut",
"compose.content-type.plain": "Texte brut",
"compose.content-type.plain_meta": "Écrire sans formatage avancé",
"compose.disable_threaded_mode": "Désactiver le mode thread",
"compose.enable_threaded_mode": "Activer le mode thread",
"compose_form.sensitive.hide": "{count, plural, one {Marquer le média comme sensible} other {Marquer les médias comme sensibles}}",
"compose_form.sensitive.marked": "{count, plural, one {Le média est marqué comme sensible} other {Les médias sont marqués comme sensibles}}",
"compose_form.sensitive.unmarked": "{count, plural, one {Le média nest pas marqué comme sensible} other {Les médias ne sont pas marqué comme sensible}}",
"confirmation_modal.do_not_ask_again": "Ne plus demander confirmation",
"confirmations.deprecated_settings.confirm": "Utiliser les préférences de Mastodon",
"confirmations.deprecated_settings.message": "Certaines {app_settings} de glitch-soc que vous utilisez ont été remplacées par les {preferences} de Mastodon et seront remplacées :",
@@ -42,8 +41,7 @@
"keyboard_shortcuts.secondary_toot": "Envoyer le post en utilisant les paramètres secondaires de confidentialité",
"moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
"navigation_bar.app_settings": "Paramètres de l'application",
"navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
"navigation_bar.misc": "Autres",
"notifications.column_settings.filter_bar.show_bar": "Afficher la barre de filtre",
"settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
"settings.close": "Fermer",
"settings.compose_box_opts": "Zone de rédaction",
@@ -57,6 +55,8 @@
"settings.content_warnings_unfold_opts": "Options de dépliement automatique",
"settings.deprecated_setting": "Cette option est maintenant définie par les {settings_page_link} de Mastodon",
"settings.enable_content_warnings_auto_unfold": "Déplier automatiquement les avertissements de contenu",
"settings.fullwidth_view": "Étirer les colonnes sur toute la largeur (mode bureau uniquement)",
"settings.fullwidth_view_hint": "Étire les colonnes pour remplir tout l'espace disponible.",
"settings.general": "Général",
"settings.hicolor_privacy_icons": "Indicateurs de confidentialité en couleurs",
"settings.hicolor_privacy_icons.hint": "Affiche les indicateurs de confidentialité dans des couleurs facilement distinguables",
@@ -76,7 +76,7 @@
"settings.pop_in_player": "Activer le lecteur pop-in",
"settings.pop_in_position": "Position du lecteur pop-in :",
"settings.pop_in_right": "Droite",
"settings.preferences": "Preferences",
"settings.preferences": "Préferences",
"settings.prepend_cw_re": "Préfixer les avertissements avec \"re: \" lors d'une réponse",
"settings.preselect_on_reply": "Présélectionner les noms dutilisateur·rices lors de la réponse",
"settings.preselect_on_reply_hint": "Présélectionner les noms d'utilisateurs après le premier lors d'une réponse à une conversation à plusieurs participants",
@@ -105,11 +105,14 @@
"settings.tag_misleading_links.hint": "Ajouter une indication visuelle avec l'hôte cible du lien à chaque lien ne le mentionnant pas explicitement",
"settings.wide_view": "Vue élargie (mode ordinateur uniquement)",
"settings.wide_view_hint": "Étire les colonnes pour mieux remplir l'espace disponible.",
"status.filtered": "Filtré",
"status.has_audio": "Contient des fichiers audio attachés",
"status.has_pictures": "Contient des images attachées",
"status.has_preview_card": "Contient une carte de prévisualisation attachée",
"status.has_video": "Contient des vidéos attachées",
"status.hide": "Masquer la publication",
"status.in_reply_to": "Ce post est une réponse",
"status.is_poll": "Ce post est un sondage",
"status.local_only": "Visible uniquement depuis votre instance"
"status.local_only": "Visible uniquement depuis votre instance",
"status.show_filter_reason": "Afficher quand même"
}

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Ver perfil completo",
"boost_modal.missing_description": "Esta publicación contén multimedia sen descrición",
"column.favourited_by": "Favorecida por",
"column.heading": "Varios",
"column.reblogged_by": "Promovida por",
"column.subheading": "Opcións diversas",
"column_header.profile": "Perfil",
"column_subheading.lists": "Listas",
"column_subheading.navigation": "Navegación",
"community.column_settings.allow_local_only": "Ver só mensaxes de ámbito local",
"compose.attach.doodle": "Debuxa algo",
"compose.change_federation": "Cambiar axustes de federación",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "para enviar publicación usando axuste secundario de privacidade",
"moved_to_warning": "Esta conta moveuse a {moved_to_link}, e non acepta novos seguimentos.",
"navigation_bar.app_settings": "Axustes da app",
"navigation_bar.keyboard_shortcuts": "Atallos de teclado",
"navigation_bar.misc": "Varios",
"notifications.column_settings.filter_bar.show_bar": "Amosar barra de filtros",
"settings.always_show_spoilers_field": "Activar sempre o campo de Aviso sobre o contido CW",
"settings.close": "Pechar",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Tampilkan profil lengkap",
"boost_modal.missing_description": "Toot ini berisi beberapa media tanpa deskripsi",
"column.favourited_by": "Disukai oleh",
"column.heading": "Lainnya",
"column.reblogged_by": "Dibagikan oleh",
"column.subheading": "Opsi lain-lain",
"column_header.profile": "Profil",
"column_subheading.lists": "Daftar",
"column_subheading.navigation": "Penelusuran",
"community.column_settings.allow_local_only": "Tampilkan toot lokal saja",
"compose.change_federation": "Ubah pengaturan federasi",
"compose.content-type.change": "Ubah opsi pemformatan lanjutan",
@@ -38,8 +34,6 @@
"keyboard_shortcuts.secondary_toot": "untuk mengirim toot menggunakan pengaturan privasi sekunder",
"moved_to_warning": "Akun ini ditandai sebagai dipindahkan ke {moved_to_link}, dan karenanya tidak menerima pengikut baru.",
"navigation_bar.app_settings": "Pengaturan aplikasi",
"navigation_bar.keyboard_shortcuts": "Pintasan keyboard",
"navigation_bar.misc": "Lainnya",
"settings.always_show_spoilers_field": "Selalu aktifkan bidang Peringatan Konten",
"settings.close": "Tutup",
"settings.compose_box_opts": "Kotak tulis",
@@ -49,6 +43,7 @@
"settings.content_warnings.regexp": "Ekspresi reguler",
"settings.content_warnings_filter": "Peringatan konten yang tidak akan dibuka secara otomatis:",
"settings.content_warnings_shared_state": "Tampilkan/sembunyikan konten semua salinan sekaligus",
"settings.pop_in_right": "Kanan",
"settings.preferences": "Preferences",
"settings.status_icons_reply": "Indikator balasan",
"settings.status_icons_visibility": "Indikator privasi toot",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "正確な情報を見る",
"boost_modal.missing_description": "このトゥートには少なくとも1つの画像に説明が付与されていません",
"column.favourited_by": "お気に入りしたユーザー",
"column.heading": "その他",
"column.reblogged_by": "ブーストしたユーザー",
"column.subheading": "その他のオプション",
"column_header.profile": "プロフィール",
"column_subheading.lists": "リスト",
"column_subheading.navigation": "ナビゲーション",
"community.column_settings.allow_local_only": "ローカル限定投稿を表示する",
"compose.content-type.html": "HTML",
"compose.content-type.html_meta": "投稿に HTML を使用する",
@@ -33,8 +29,6 @@
"keyboard_shortcuts.secondary_toot": "セカンダリートゥートの公開範囲でトゥートする",
"moved_to_warning": "このアカウント{moved_to_link}に引っ越したため、新しいフォロワーを受け入れていません。",
"navigation_bar.app_settings": "アプリ設定",
"navigation_bar.keyboard_shortcuts": "キーボードショートカット",
"navigation_bar.misc": "その他",
"settings.always_show_spoilers_field": "常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)",
"settings.close": "閉じる",
"settings.compose_box_opts": "コンポーズボックス設定",

View File

@@ -1,6 +1,5 @@
{
"compose.attach.doodle": "Ssuneɣ-d kra",
"navigation_bar.keyboard_shortcuts": "Inezgumen n unasiw",
"settings.close": "Mdel",
"settings.content_warnings": "Content warnings",
"settings.preferences": "Preferences"

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "전체 프로필 보기",
"boost_modal.missing_description": "이 게시물은 설명이 없는 미디어를 포함하고 있습니다",
"column.favourited_by": "즐겨찾기 한 사람",
"column.heading": "기타",
"column.reblogged_by": "부스트 한 사람",
"column.subheading": "다양한 옵션",
"column_header.profile": "프로필",
"column_subheading.lists": "리스트",
"column_subheading.navigation": "탐색",
"community.column_settings.allow_local_only": "로컬 전용 글 보기",
"compose.attach.doodle": "뭔가 그려보세요",
"compose.change_federation": "연합 설정 변경",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "보조 프라이버시 설정으로 글 보내기",
"moved_to_warning": "이 계정은 {moved_to_link}로 이동한 것으로 표시되었고, 새 팔로우를 받지 않는 것 같습니다.",
"navigation_bar.app_settings": "앱 설정",
"navigation_bar.keyboard_shortcuts": "키보드 단축기",
"navigation_bar.misc": "다양한 옵션들",
"notifications.column_settings.filter_bar.show_bar": "필터 막대 표시",
"settings.always_show_spoilers_field": "열람주의 항목을 언제나 활성화",
"settings.close": "닫기",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Peržiūrėti visą profilį",
"boost_modal.missing_description": "Šis įrašas turi šiek tiek medijų be aprašų.",
"column.favourited_by": "Pamėgo",
"column.heading": "Įvairūs",
"column.reblogged_by": "Pasidalino",
"column.subheading": "Įvairios parinktys",
"column_header.profile": "Profilis",
"column_subheading.lists": "Sąrašai",
"column_subheading.navigation": "Naršymas",
"community.column_settings.allow_local_only": "Rodyti tik vietinius įrašus",
"compose.attach.doodle": "Nupiešti kažką",
"compose.change_federation": "Keisti federacijos nustatymus",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "siųsti įrašą naudojant antrinį privatumo nustatymą",
"moved_to_warning": "Ši paskyra pažymėta kaip perkelta į {moved_to_link}, todėl negali priimti naujų sekimų.",
"navigation_bar.app_settings": "Programėlės nustatymai",
"navigation_bar.keyboard_shortcuts": "Spartieji klavišai",
"navigation_bar.misc": "Įvairūs",
"notifications.column_settings.filter_bar.show_bar": "Rodyti filtro juostą",
"settings.always_show_spoilers_field": "Visada įjungti turinio įspėjimo lauką",
"settings.close": "Užverti",

View File

@@ -3,12 +3,8 @@
"account.view_full_profile": "Volledig profiel weergeven",
"boost_modal.missing_description": "Deze toot bevat media zonder beschrijving",
"column.favourited_by": "Favoriet door",
"column.heading": "Overige",
"column.reblogged_by": "Geboost door",
"column.subheading": "Diverse opties",
"column_header.profile": "Profiel",
"column_subheading.lists": "Lijsten",
"column_subheading.navigation": "Navigatie",
"community.column_settings.allow_local_only": "Toon alleen lokale toots",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Pokaż pełny profil",
"boost_modal.missing_description": "Ten wpis zawiera multimedialne załączniki bez opisu",
"column.favourited_by": "Polubiony przez",
"column.heading": "Różne",
"column.reblogged_by": "Podbity przez",
"column.subheading": "Różne opcje",
"column_header.profile": "Profil",
"column_subheading.lists": "Listy",
"column_subheading.navigation": "Nawigacja",
"community.column_settings.allow_local_only": "Pokazuj wyłącznie wpisy lokalne",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",
@@ -28,8 +24,6 @@
"keyboard_shortcuts.secondary_toot": "aby opublikować wpis używając dodatkowych ustawień prywatności",
"moved_to_warning": "To konto oznaczone jest jako przeniesione do {moved_to_link} i może z tego powodu nie akceptować nowych obserwujących.",
"navigation_bar.app_settings": "Ustawienia aplikacji",
"navigation_bar.keyboard_shortcuts": "Skróty klawiszowe",
"navigation_bar.misc": "Różne",
"settings.always_show_spoilers_field": "Zawsze pokazuj pole ostrzeżenia o zawartości",
"settings.close": "Zamknij",
"settings.compose_box_opts": "Pole edycji",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Ver o perfil completo",
"boost_modal.missing_description": "Este toot contém algumas mídias sem descrição",
"column.favourited_by": "Favoritado por",
"column.heading": "Diversos",
"column.reblogged_by": "Inpulsionado por",
"column.subheading": "Opções diversas",
"column_header.profile": "Perfil",
"column_subheading.lists": "Listas",
"column_subheading.navigation": "Navegação",
"community.column_settings.allow_local_only": "Mostrar os toots apenas locais",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",
@@ -28,8 +24,6 @@
"keyboard_shortcuts.secondary_toot": "para enviar toot usando a configuração de privacidade secundária",
"moved_to_warning": "Esta conta foi como movida para {moved_to_link} e, portanto, pode não aceitar novos seguidores.",
"navigation_bar.app_settings": "Configurações do aplicativo",
"navigation_bar.keyboard_shortcuts": "Atalhos de teclado",
"navigation_bar.misc": "Diversos",
"settings.always_show_spoilers_field": "Sempre ativar o campo Aviso de Conteúdo",
"settings.close": "Fechar",
"settings.compose_box_opts": "Caixa de composição",

View File

@@ -6,7 +6,6 @@
"account.view_full_profile": "Ver o perfil completo",
"boost_modal.missing_description": "Este post contém alguns media sem descrição",
"column.favourited_by": "Adicionado aos favoritos de",
"column.heading": "Diversos",
"moved_to_warning": "Esta conta mudou-se para {moved_to_link} e, portanto, pode não aceitar novos seguidores.",
"notifications.column_settings.filter_bar.show_bar": "Mostrar barra de filtros",
"settings.always_show_spoilers_field": "Mostrar sempre o campo Aviso de Conteúdo",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Открыть полный профиль",
"boost_modal.missing_description": "Этот пост содержит медиафайлы без описания",
"column.favourited_by": "Добавили в избранное",
"column.heading": "Прочее",
"column.reblogged_by": "Продвинули",
"column.subheading": "Прочие разделы",
"column_header.profile": "Профиль",
"column_subheading.lists": "Списки",
"column_subheading.navigation": "Навигация",
"community.column_settings.allow_local_only": "Показывать нефедерируемые посты",
"compose.attach.doodle": "Нарисовать что-нибудь",
"compose.change_federation": "Изменить настройки федерации",
@@ -39,8 +35,6 @@
"keyboard_shortcuts.bookmark": "добавить закладку",
"moved_to_warning": "Этот аккаунт переехал на {moved_to_link}, и скорее всего не принимает новых подписчиков.",
"navigation_bar.app_settings": "Настройки приложения",
"navigation_bar.keyboard_shortcuts": "Сочетания клавиш",
"navigation_bar.misc": "Прочее",
"notifications.column_settings.filter_bar.show_bar": "Показать панель фильтров",
"settings.always_show_spoilers_field": "Всегда ставить предупреждение о содержании",
"settings.close": "Закрыть",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "View full profile",
"boost_modal.missing_description": "This post contains some media withoot description",
"column.favourited_by": "Favouritit by",
"column.heading": "Misc",
"column.reblogged_by": "Boosted by",
"column.subheading": "Miscellaneous options",
"column_header.profile": "Profile",
"column_subheading.lists": "Lists",
"column_subheading.navigation": "Navigation",
"community.column_settings.allow_local_only": "Show local-only posts",
"compose.attach.doodle": "Draw something",
"compose.change_federation": "Change federation settins",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "tae send post using secondary privacy settin",
"moved_to_warning": "This account is marked as moved tae {moved_to_link}, an' may not accept new follows.",
"navigation_bar.app_settings": "App settins",
"navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
"navigation_bar.misc": "Misc",
"notifications.column_settings.filter_bar.show_bar": "Show filter bar",
"settings.always_show_spoilers_field": "Always enable the Content Warning field",
"settings.close": "Close",

View File

@@ -4,12 +4,8 @@
"account.view_full_profile": "Visa full profil",
"boost_modal.missing_description": "Denna toot innehåller viss media utan beskrivning",
"column.favourited_by": "Favoritmarkerad av",
"column.heading": "Övrigt",
"column.reblogged_by": "Boostad av",
"column.subheading": "Övriga val",
"column_header.profile": "Profil",
"column_subheading.lists": "Listor",
"column_subheading.navigation": "Navigering",
"community.column_settings.allow_local_only": "Visa endast lokala toots",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",

View File

@@ -1,8 +1,5 @@
{
"about.fork_disclaimer": ".",
"account.disclaimer_full": ".",
"account.follows": "",
"community.column_settings.allow_local_only": "",
"account.follows": "ติดตาม",
"settings.content_warnings": "Content warnings",
"settings.preferences": "Preferences"
}

View File

@@ -5,12 +5,8 @@
"account.view_full_profile": "Tam görünüm",
"boost_modal.missing_description": "Bu gönderi açıklaması olmayan medya içerir",
"column.favourited_by": "Tarafından favorilere eklendi",
"column.heading": "Diğer",
"column.reblogged_by": "Tarafından yükseltildi",
"column.subheading": "Diğer seçenekler",
"column_header.profile": "Profil",
"column_subheading.lists": "Listeler",
"column_subheading.navigation": "Gezinme",
"community.column_settings.allow_local_only": "Sadece yerel gönderileri göster",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown modu",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "Переглянути повний профіль",
"boost_modal.missing_description": "Цей дмух містить деякі медіа без опису",
"column.favourited_by": "Уподобані",
"column.heading": "Різне",
"column.reblogged_by": "Поширено",
"column.subheading": "Інші параметри",
"column_header.profile": "Профіль",
"column_subheading.lists": "Списки",
"column_subheading.navigation": "Навігація",
"community.column_settings.allow_local_only": "Показувати тільки локальні дмухи",
"compose.content-type.html": "HTML",
"compose.content-type.markdown": "Markdown",
@@ -29,8 +25,6 @@
"keyboard_shortcuts.secondary_toot": "надсилати повідомлення з використанням вторинних налаштувань конфіденційності",
"moved_to_warning": "Цей обліковий запис позначено як переміщений до {moved_to_link} і тому не може прийняти нових підписок.",
"navigation_bar.app_settings": "Налаштування програми",
"navigation_bar.keyboard_shortcuts": "Комбінації клавіш",
"navigation_bar.misc": "Різне",
"settings.always_show_spoilers_field": "Завжди вмикати попередження про вміст",
"settings.close": "Закрити",
"settings.compose_box_opts": "Compose box",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "查看完整资料",
"boost_modal.missing_description": "这条嘟文未包含媒体描述",
"column.favourited_by": "点赞",
"column.heading": "杂项",
"column.reblogged_by": "转嘟",
"column.subheading": "其他选项",
"column_header.profile": "资料卡",
"column_subheading.lists": "列表",
"column_subheading.navigation": "导航",
"community.column_settings.allow_local_only": "只显示仅本站可见的嘟文",
"compose.attach.doodle": "涂鸦",
"compose.change_federation": "更改联动设置",
@@ -45,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "使用另一隐私设置发送嘟文",
"moved_to_warning": "此账户已被标记为移至 {moved_to_link},并且似乎没有收到新粉丝。",
"navigation_bar.app_settings": "应用设置",
"navigation_bar.keyboard_shortcuts": "键盘快捷键",
"navigation_bar.misc": "杂项",
"notifications.column_settings.filter_bar.show_bar": "显示筛选栏",
"settings.always_show_spoilers_field": "始终显示内容警告框",
"settings.close": "关闭",

View File

@@ -6,12 +6,8 @@
"account.view_full_profile": "查看完整個人資料",
"boost_modal.missing_description": "此貼文包含未加說明的媒體檔案",
"column.favourited_by": "誰按了最愛",
"column.heading": "雜項",
"column.reblogged_by": "被誰轉貼",
"column.subheading": "其他選項",
"column_header.profile": "個人檔案",
"column_subheading.lists": "列表",
"column_subheading.navigation": "導覽",
"community.column_settings.allow_local_only": "顯示僅限本地的貼文",
"compose.attach.doodle": "塗鴉",
"compose.change_federation": "變更聯邦設定",
@@ -24,6 +20,9 @@
"compose.content-type.plain_meta": "不使用進階格式撰寫",
"compose.disable_threaded_mode": "停用貼文串模式",
"compose.enable_threaded_mode": "啟用貼文串模式",
"compose_form.sensitive.hide": "{count, plural, other {將媒體標記為敏感內容}}",
"compose_form.sensitive.marked": "{count, plural, other {媒體已被標記為敏感內容}}",
"compose_form.sensitive.unmarked": "{count, plural, other {媒體未被標記為敏感內容}}",
"confirmation_modal.do_not_ask_again": "不要再顯示確認訊息",
"confirmations.deprecated_settings.confirm": "使用 Mastodon 偏好",
"confirmations.deprecated_settings.message": "您正在使用的某些特定於 glitch-soc 設備的 {app_settings} 已被 Mastodon {preferences} 所取代,並將被覆蓋:",
@@ -42,8 +41,6 @@
"keyboard_shortcuts.secondary_toot": "使用次要隱私設定來發布貼文",
"moved_to_warning": "此帳戶已標記為移至 {moved_to_link},因此可能不接受新的追隨者。",
"navigation_bar.app_settings": "應用程式設定",
"navigation_bar.keyboard_shortcuts": "鍵盤快速鍵",
"navigation_bar.misc": "雜項",
"notifications.column_settings.filter_bar.show_bar": "顯示過濾器",
"settings.always_show_spoilers_field": "永遠啟用內容警告欄位",
"settings.close": "關閉",
@@ -58,6 +55,8 @@
"settings.content_warnings_unfold_opts": "自動展開選項",
"settings.deprecated_setting": "此設定現在已由 Mastodon 的 {settings_page_link} 控制。",
"settings.enable_content_warnings_auto_unfold": "自動展開內容警告",
"settings.fullwidth_view": "將欄位延展至全寬(僅限桌面模式)",
"settings.fullwidth_view_hint": "將欄位延展以填滿所有可用空間。",
"settings.general": "一般設定",
"settings.hicolor_privacy_icons": "隱私圖示使用對比色",
"settings.hicolor_privacy_icons.hint": "用明亮且易於區分的顏色顯示隱私圖示",

View File

@@ -619,8 +619,13 @@ export const composeReducer = (state = initialState, action) => {
}
if (action.status.get('poll')) {
let options = ImmutableList(action.status.get('poll').options.map(x => x.title));
if (options.size < action.maxOptions) {
options = options.push('');
}
map.set('poll', ImmutableMap({
options: ImmutableList(action.status.get('poll').options.map(x => x.title)),
options: options,
multiple: action.status.get('poll').multiple,
expires_in: expiresInFromExpiresAt(action.status.get('poll').expires_at),
}));
@@ -650,8 +655,13 @@ export const composeReducer = (state = initialState, action) => {
}
if (action.status.get('poll')) {
let options = ImmutableList(action.status.get('poll').options.map(x => x.title));
if (options.size < action.maxOptions) {
options = options.push('');
}
map.set('poll', ImmutableMap({
options: ImmutableList(action.status.get('poll').options.map(x => x.title)),
options: options,
multiple: action.status.get('poll').multiple,
expires_in: expiresInFromExpiresAt(action.status.get('poll').expires_at),
}));

View File

@@ -10,7 +10,7 @@ const initialState = ImmutableMap({
stretch : true,
side_arm : 'none',
side_arm_reply_mode : 'keep',
show_reply_count : false,
show_reply_count : true,
always_show_spoilers_field: false,
confirm_boost_missing_media_description: false,
confirm_before_clearing_draft: true,

View File

@@ -64,6 +64,10 @@ const statusTranslateUndo = (state, id) => {
});
};
const removeStatusStub = (state, id) => {
return state.getIn([id, 'id']) ? state.deleteIn([id, 'isLoading']) : state.delete(id);
}
/** @type {ImmutableMap<string, import('flavours/glitch/models/status').Status>} */
const initialState = ImmutableMap();
@@ -73,8 +77,14 @@ export default function statuses(state = initialState, action) {
switch(action.type) {
case STATUS_FETCH_REQUEST:
return state.setIn([action.id, 'isLoading'], true);
case STATUS_FETCH_FAIL:
return state.delete(action.id);
case STATUS_FETCH_FAIL: {
if (action.parentQuotePostId && action.error.status === 404) {
return removeStatusStub(state, action.id)
.setIn([action.parentQuotePostId, 'quote', 'state'], 'deleted')
} else {
return removeStatusStub(state, action.id);
}
}
case STATUS_IMPORT:
return importStatus(state, action.status);
case STATUSES_IMPORT:

View File

@@ -1893,7 +1893,7 @@ a.sparkline {
font-size: 15px;
line-height: 22px;
li {
> li {
counter-increment: step 1;
padding-inline-start: 2.5rem;
padding-bottom: 8px;

View File

@@ -2398,6 +2398,7 @@ a .account__avatar {
.detailed-status__display-name,
.detailed-status__datetime,
.detailed-status__application,
.detailed-status__link,
.account__display-name {
text-decoration: none;
}
@@ -2430,7 +2431,8 @@ a.account__display-name {
}
.detailed-status__application,
.detailed-status__datetime {
.detailed-status__datetime,
.detailed-status__link {
color: inherit;
}
@@ -2616,8 +2618,9 @@ a.account__display-name {
}
.status__relative-time,
.detailed-status__datetime {
&:hover {
.detailed-status__datetime,
.detailed-status__link {
&:is(a):hover {
text-decoration: underline;
}
}

View File

@@ -589,11 +589,14 @@ code {
}
.stacked-actions {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 30px;
margin-bottom: 15px;
}
button:not(.button, .link-button, .help-button) {
.btn {
display: block;
width: 100%;
border: 0;
@@ -610,8 +613,6 @@ code {
cursor: pointer;
font-weight: 500;
outline: 0;
margin-bottom: 10px;
margin-inline-end: 10px;
&:last-child {
margin-inline-end: 0;

View File

@@ -107,7 +107,8 @@
cursor: pointer;
}
&.editable {
&.editable,
&.disabled {
align-items: center;
overflow: visible;
}
@@ -165,7 +166,8 @@
}
}
&__option.editable &__input {
&__option.editable &__input,
&__option.disabled &__input {
&:active,
&:focus,
&:hover {

View File

@@ -96,12 +96,17 @@ export const ensureComposeIsVisible = (getState) => {
};
export function setComposeToStatus(status, text, spoiler_text) {
return{
type: COMPOSE_SET_STATUS,
status,
text,
spoiler_text,
};
return (dispatch, getState) => {
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
dispatch({
type: COMPOSE_SET_STATUS,
status,
text,
spoiler_text,
maxOptions,
});
}
}
export function changeCompose(text) {
@@ -183,7 +188,7 @@ export function directCompose(account) {
};
}
export function submitCompose() {
export function submitCompose(successCallback) {
return function (dispatch, getState) {
const status = getState().getIn(['compose', 'text'], '');
const media = getState().getIn(['compose', 'media_attachments']);
@@ -239,6 +244,9 @@ export function submitCompose() {
dispatch(insertIntoTagHistory(response.data.tags, status));
dispatch(submitComposeSuccess({ ...response.data }));
if (typeof successCallback === 'function') {
successCallback(response.data);
}
// To make the app more responsive, immediately push the status
// into the columns

View File

@@ -21,6 +21,15 @@ export function normalizeFilterResult(result) {
return normalResult;
}
function stripQuoteFallback(text) {
const wrapper = document.createElement('div');
wrapper.innerHTML = text;
wrapper.querySelector('.quote-inline')?.remove();
return wrapper.innerHTML;
}
export function normalizeStatus(status, normalOldStatus) {
const normalStatus = { ...status };
@@ -72,7 +81,7 @@ export function normalizeStatus(status, normalOldStatus) {
} else {
// If the status has a CW but no contents, treat the CW as if it were the
// status' contents, to avoid having a CW toggle with seemingly no effect.
if (normalStatus.spoiler_text && !normalStatus.content) {
if (normalStatus.spoiler_text && !normalStatus.content && !normalStatus.quote) {
normalStatus.content = normalStatus.spoiler_text;
normalStatus.spoiler_text = '';
}
@@ -86,6 +95,11 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
// Remove quote fallback link from the DOM so it doesn't mess with paragraph margins
if (normalStatus.quote) {
normalStatus.contentHtml = stripQuoteFallback(normalStatus.contentHtml);
}
if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) {
normalStatus.url = null;
}
@@ -125,6 +139,11 @@ export function normalizeStatusTranslation(translation, status) {
spoiler_text: translation.spoiler_text,
};
// Remove quote fallback link from the DOM so it doesn't mess with paragraph margins
if (status.get('quote')) {
normalTranslation.contentHtml = stripQuoteFallback(normalTranslation.contentHtml);
}
return normalTranslation;
}

View File

@@ -3,7 +3,7 @@ import { browserHistory } from 'mastodon/components/router';
import api from '../api';
import { ensureComposeIsVisible, setComposeToStatus } from './compose';
import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer';
import { importFetchedStatus, importFetchedAccount } from './importer';
import { fetchContext } from './statuses_typed';
import { deleteFromTimelines } from './timelines';
@@ -48,7 +48,18 @@ export function fetchStatusRequest(id, skipLoading) {
};
}
export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
/**
* @param {string} id
* @param {Object} [options]
* @param {boolean} [options.forceFetch]
* @param {boolean} [options.alsoFetchContext]
* @param {string | null | undefined} [options.parentQuotePostId]
*/
export function fetchStatus(id, {
forceFetch = false,
alsoFetchContext = true,
parentQuotePostId,
} = {}) {
return (dispatch, getState) => {
const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
@@ -66,7 +77,9 @@ export function fetchStatus(id, forceFetch = false, alsoFetchContext = true) {
dispatch(importFetchedStatus(response.data));
dispatch(fetchStatusSuccess(skipLoading));
}).catch(error => {
dispatch(fetchStatusFail(id, error, skipLoading));
dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId));
if (error.status === 404)
dispatch(deleteFromTimelines(id));
});
};
}
@@ -78,21 +91,27 @@ export function fetchStatusSuccess(skipLoading) {
};
}
export function fetchStatusFail(id, error, skipLoading) {
export function fetchStatusFail(id, error, skipLoading, parentQuotePostId) {
return {
type: STATUS_FETCH_FAIL,
id,
error,
parentQuotePostId,
skipLoading,
skipAlert: true,
};
}
export function redraft(status, raw_text) {
return {
type: REDRAFT,
status,
raw_text,
return (dispatch, getState) => {
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
dispatch({
type: REDRAFT,
status,
raw_text,
maxOptions,
});
};
}

View File

@@ -1,10 +1,11 @@
import { apiRequestPost } from 'mastodon/api';
import type { Status, StatusVisibility } from 'mastodon/models/status';
import type { ApiStatusJSON } from 'mastodon/api_types/statuses';
import type { StatusVisibility } from 'mastodon/models/status';
export const apiReblog = (statusId: string, visibility: StatusVisibility) =>
apiRequestPost<{ reblog: Status }>(`v1/statuses/${statusId}/reblog`, {
apiRequestPost<{ reblog: ApiStatusJSON }>(`v1/statuses/${statusId}/reblog`, {
visibility,
});
export const apiUnreblog = (statusId: string) =>
apiRequestPost<Status>(`v1/statuses/${statusId}/unreblog`);
apiRequestPost<ApiStatusJSON>(`v1/statuses/${statusId}/unreblog`);

View File

@@ -20,7 +20,7 @@ import { useDrag } from '@use-gesture/react';
import { expandAccountFeaturedTimeline } from '@/mastodon/actions/timelines';
import { Icon } from '@/mastodon/components/icon';
import { IconButton } from '@/mastodon/components/icon_button';
import StatusContainer from '@/mastodon/containers/status_container';
import { StatusQuoteManager } from '@/mastodon/components/status_quoted';
import { usePrevious } from '@/mastodon/hooks/usePrevious';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
@@ -218,12 +218,7 @@ const FeaturedCarouselItem: React.FC<
ref={handleRef}
{...props}
>
<StatusContainer
// @ts-expect-error inferred props are wrong
id={statusId}
contextType='account'
withCounters
/>
<StatusQuoteManager id={statusId} contextType='account' withCounters />
</animated.div>
);
};

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
@@ -11,13 +11,16 @@ import ArticleIcon from '@/material-icons/400-24px/article.svg?react';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { Icon } from 'mastodon/components/icon';
import StatusContainer from 'mastodon/containers/status_container';
import { domain } from 'mastodon/initial_state';
import type { Status } from 'mastodon/models/status';
import type { RootState } from 'mastodon/store';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import QuoteIcon from '../../images/quote.svg?react';
import { revealAccount } from '../actions/accounts_typed';
import { fetchStatus } from '../actions/statuses';
import { makeGetStatus } from '../selectors';
import { getAccountHidden } from '../selectors/accounts';
const MAX_QUOTE_POSTS_NESTING_LEVEL = 1;
@@ -37,9 +40,7 @@ const QuoteWrapper: React.FC<{
);
};
const NestedQuoteLink: React.FC<{
status: Status;
}> = ({ status }) => {
const NestedQuoteLink: React.FC<{ status: Status }> = ({ status }) => {
const accountId = status.get('account') as string;
const account = useAppSelector((state) =>
accountId ? state.accounts.get(accountId) : undefined,
@@ -75,24 +76,74 @@ type GetStatusSelector = (
props: { id?: string | null; contextType?: string },
) => Status | null;
const LimitedAccountHint: React.FC<{ accountId: string }> = ({ accountId }) => {
const dispatch = useAppDispatch();
const reveal = useCallback(() => {
dispatch(revealAccount({ id: accountId }));
}, [dispatch, accountId]);
return (
<>
<FormattedMessage
id='status.quote_error.limited_account_hint.title'
defaultMessage='This account has been hidden by the moderators of {domain}.'
values={{ domain }}
/>
<button onClick={reveal} className='link-button'>
<FormattedMessage
id='status.quote_error.limited_account_hint.action'
defaultMessage='Show anyway'
/>
</button>
</>
);
};
export const QuotedStatus: React.FC<{
quote: QuoteMap;
contextType?: string;
parentQuotePostId?: string | null;
variant?: 'full' | 'link';
nestingLevel?: number;
}> = ({ quote, contextType, nestingLevel = 1, variant = 'full' }) => {
}> = ({
quote,
contextType,
parentQuotePostId,
nestingLevel = 1,
variant = 'full',
}) => {
const dispatch = useAppDispatch();
const quoteState = useAppSelector((state) =>
parentQuotePostId
? state.statuses.getIn([parentQuotePostId, 'quote', 'state'])
: quote.get('state'),
);
const quotedStatusId = quote.get('quoted_status');
const quoteState = quote.get('state');
const status = useAppSelector((state) =>
quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
);
const shouldLoadQuote = !status?.get('isLoading') && quoteState !== 'deleted';
const accountId: string | null = status?.get('account', null) as
| string
| null;
const hiddenAccount = useAppSelector(
(state) => accountId && getAccountHidden(state, accountId),
);
useEffect(() => {
if (!status && quotedStatusId) {
dispatch(fetchStatus(quotedStatusId));
if (shouldLoadQuote && quotedStatusId) {
dispatch(
fetchStatus(quotedStatusId, {
parentQuotePostId,
alsoFetchContext: false,
}),
);
}
}, [status, quotedStatusId, dispatch]);
}, [shouldLoadQuote, quotedStatusId, parentQuotePostId, dispatch]);
// In order to find out whether the quoted post should be completely hidden
// due to a matching filter, we run it through the selector used by `status_container`.
@@ -147,6 +198,8 @@ export const QuotedStatus: React.FC<{
defaultMessage='This post cannot be displayed.'
/>
);
} else if (hiddenAccount && accountId) {
quoteError = <LimitedAccountHint accountId={accountId} />;
}
if (quoteError) {
@@ -173,6 +226,7 @@ export const QuotedStatus: React.FC<{
{canRenderChildQuote && (
<QuotedStatus
quote={childQuote}
parentQuotePostId={quotedStatusId}
contextType={contextType}
variant={
nestingLevel === MAX_QUOTE_POSTS_NESTING_LEVEL ? 'link' : 'full'
@@ -208,7 +262,11 @@ export const StatusQuoteManager = (props: StatusQuoteManagerProps) => {
if (quote) {
return (
<StatusContainer {...props}>
<QuotedStatus quote={quote} contextType={props.contextType} />
<QuotedStatus
quote={quote}
parentQuotePostId={status?.get('id') as string}
contextType={props.contextType}
/>
</StatusContainer>
);
}

View File

@@ -73,6 +73,7 @@ class ComposeForm extends ImmutablePureComponent {
singleColumn: PropTypes.bool,
lang: PropTypes.string,
maxChars: PropTypes.number,
redirectOnSuccess: PropTypes.bool,
};
static defaultProps = {
@@ -310,7 +311,7 @@ class ComposeForm extends ImmutablePureComponent {
>
{intl.formatMessage(
this.props.isEditing ?
messages.saveChanges :
messages.saveChanges :
(this.props.isInReply ? messages.reply : messages.publish)
)}
</Button>

View File

@@ -34,7 +34,7 @@ const mapStateToProps = state => ({
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = (dispatch, props) => ({
onChange (text) {
dispatch(changeCompose(text));
@@ -47,7 +47,11 @@ const mapDispatchToProps = (dispatch) => ({
modalProps: {},
}));
} else {
dispatch(submitCompose());
dispatch(submitCompose((status) => {
if (props.redirectOnSuccess) {
window.location.assign(status.url);
}
}));
}
},

View File

@@ -5,7 +5,7 @@ import ModalContainer from 'mastodon/features/ui/containers/modal_container';
const Compose = () => (
<>
<ComposeFormContainer autoFocus withoutNavigation />
<ComposeFormContainer autoFocus withoutNavigation redirectOnSuccess />
<AlertsController />
<ModalContainer />
<LoadingBarContainer className='loading-bar' />

View File

@@ -32,7 +32,7 @@ const Embed: React.FC<{ id: string }> = ({ id }) => {
const dispatchRenderSignal = useRenderSignal();
useEffect(() => {
dispatch(fetchStatus(id, false, false));
dispatch(fetchStatus(id, { alsoFetchContext: false }));
}, [dispatch, id]);
const handleToggleHidden = useCallback(() => {

View File

@@ -37,18 +37,23 @@ const getHostname = url => {
const domParser = new DOMParser();
const addAutoPlay = html => {
const handleIframeUrl = (html, url, providerName) => {
const document = domParser.parseFromString(html, 'text/html').documentElement;
const iframe = document.querySelector('iframe');
const startTime = new URL(url).searchParams.get('t')
if (iframe) {
if (iframe.src.indexOf('?') !== -1) {
iframe.src += '&';
} else {
iframe.src += '?';
const iframeUrl = new URL(iframe.src)
iframeUrl.searchParams.set('autoplay', 1)
iframeUrl.searchParams.set('auto_play', 1)
if (providerName === 'YouTube') {
iframeUrl.searchParams.set('start', startTime || '');
iframe.referrerPolicy = 'strict-origin-when-cross-origin';
}
iframe.src += 'autoplay=1&auto_play=1';
iframe.src = iframeUrl.href
// DOM parser creates html/body elements around original HTML fragment,
// so we need to get innerHTML out of the body and not the entire document
@@ -114,7 +119,7 @@ export default class Card extends PureComponent {
renderVideo () {
const { card } = this.props;
const content = { __html: addAutoPlay(card.get('html')) };
const content = { __html: handleIframeUrl(card.get('html'), card.get('url'), card.get('provider_name')) };
return (
<div

View File

@@ -381,7 +381,11 @@ export const DetailedStatus: React.FC<{
{hashtagBar}
{status.get('quote') && (
<QuotedStatus quote={status.get('quote')} />
<QuotedStatus
quote={status.get('quote')}
parentQuotePostId={status.get('id')}
contextType='thread'
/>
)}
</>
)}

View File

@@ -38,7 +38,7 @@ class FilterModal extends ImmutablePureComponent {
handleSuccess = () => {
const { dispatch, statusId } = this.props;
dispatch(fetchStatus(statusId, true));
dispatch(fetchStatus(statusId, {forceFetch: true}));
this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
};

View File

@@ -201,8 +201,6 @@ class MediaModal extends ImmutablePureComponent {
preview={image.get('preview_url')}
blurhash={image.get('blurhash')}
src={image.get('url')}
width={image.get('width')}
height={image.get('height')}
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${image.getIn(['meta', 'original', 'width'])} / ${image.getIn(['meta', 'original', 'height'])}`}
startTime={currentTime || 0}
@@ -219,8 +217,6 @@ class MediaModal extends ImmutablePureComponent {
return (
<GIFV
src={image.get('url')}
width={width}
height={height}
key={image.get('url')}
alt={description}
lang={lang}

View File

@@ -90,8 +90,7 @@ const messages = defineMessages({
const mapStateToProps = state => ({
layout: state.getIn(['meta', 'layout']),
isComposing: state.getIn(['compose', 'is_composing']),
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
hasComposingContents: state.getIn(['compose', 'text']).trim().length !== 0 || state.getIn(['compose', 'media_attachments']).size > 0 || state.getIn(['compose', 'poll']) !== null,
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']),
firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0,
@@ -272,8 +271,7 @@ class UI extends PureComponent {
dispatch: PropTypes.func.isRequired,
children: PropTypes.node,
isComposing: PropTypes.bool,
hasComposingText: PropTypes.bool,
hasMediaAttachments: PropTypes.bool,
hasComposingContents: PropTypes.bool,
canUploadMore: PropTypes.bool,
intl: PropTypes.object.isRequired,
layout: PropTypes.string.isRequired,
@@ -288,11 +286,11 @@ class UI extends PureComponent {
};
handleBeforeUnload = e => {
const { intl, dispatch, isComposing, hasComposingText, hasMediaAttachments } = this.props;
const { intl, dispatch, isComposing, hasComposingContents } = this.props;
dispatch(synchronouslySubmitMarkers());
if (isComposing && (hasComposingText || hasMediaAttachments)) {
if (isComposing && hasComposingContents) {
e.preventDefault();
// Setting returnValue to any string causes confirmation dialog.
// Many browsers no longer display this text to users,

View File

@@ -1,6 +1,7 @@
{
"about.blocks": "Gemodereerde bedieners",
"about.contact": "Kontak:",
"about.default_locale": "Verstek",
"about.disclaimer": "Mastodon is gratis oopbronsagteware en n handelsmerk van Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Rede nie beskikbaar nie",
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",

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