From 1add29cf40f8a9ede5d2720adea278ce5d53806d Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 4 Mar 2026 18:44:27 +0100 Subject: [PATCH 01/15] Redirect to short account URLs when requesting HTML for one of the AP endpoints (#38056) --- app/controllers/accounts_controller.rb | 2 ++ app/controllers/statuses_controller.rb | 2 ++ spec/requests/accounts_spec.rb | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index efd0c92cef..0deeb76e3e 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -18,6 +18,8 @@ class AccountsController < ApplicationController respond_to do |format| format.html do expires_in(15.seconds, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.hour) unless user_signed_in? + + redirect_to short_account_path(@account) if account_id_param.present? && username_param.blank? end format.rss do diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index be3641589f..7b1f63da6c 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -26,6 +26,8 @@ class StatusesController < ApplicationController respond_to do |format| format.html do expires_in 10.seconds, public: true if current_account.nil? + + redirect_to short_account_status_path(@account, @status) if account_id_param.present? && username_param.blank? end format.json do diff --git a/spec/requests/accounts_spec.rb b/spec/requests/accounts_spec.rb index cc2a5be7c5..cd67e89d45 100644 --- a/spec/requests/accounts_spec.rb +++ b/spec/requests/accounts_spec.rb @@ -6,10 +6,20 @@ RSpec.describe 'Accounts show response' do let(:account) { Fabricate(:account) } context 'with numeric-based identifiers' do - it 'returns http success' do - get "/ap/users/#{account.id}" + context 'with JSON format' do + it 'returns http success' do + get "/ap/users/#{account.id}", headers: { 'ACCEPT' => 'application/json' } - expect(response).to have_http_status(200) + expect(response).to have_http_status(200) + end + end + + context 'with HTML format' do + it 'redirects to success' do + get "/ap/users/#{account.id}", as: 'html' + + expect(response).to redirect_to("/@#{account.username}") + end end end From 922fbb8c1789c90d7864b66ee8b9f05863815b31 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 5 Mar 2026 09:56:18 +0100 Subject: [PATCH 02/15] Add for searching already-known private GtS posts (#38057) --- app/services/resolve_url_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 19a94e77ad..899f586b81 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -4,7 +4,7 @@ class ResolveURLService < BaseService include JsonLdHelper include Authorization - USERNAME_STATUS_RE = %r{/@(?#{Account::USERNAME_RE})/(?[0-9]+)\Z} + USERNAME_STATUS_RE = %r{/@(?#{Account::USERNAME_RE})/(statuses/)?(?[0-9a-zA-Z]+)\Z} def call(url, on_behalf_of: nil) @url = url From eefdf7ecdf7145a77b6dca65bc8928bf2e50856c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 09:56:32 +0100 Subject: [PATCH 03/15] Update dependency linzer to v0.7.8 (#38050) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ee036b208b..56be5db8f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -414,12 +414,12 @@ GEM rexml link_header (0.0.8) lint_roller (1.1.0) - linzer (0.7.7) - cgi (~> 0.4.2) + linzer (0.7.8) + cgi (>= 0.4.2, < 0.6.0) forwardable (~> 1.3, >= 1.3.3) logger (~> 1.7, >= 1.7.0) - net-http (~> 0.6.0) - openssl (~> 3.0, >= 3.0.0) + net-http (>= 0.6, < 0.10) + openssl (>= 3, < 5) rack (>= 2.2, < 4.0) starry (~> 0.2) stringio (~> 3.1, >= 3.1.2) From 748dbf3217b8f4151185b5071bb48ebfec33f17e Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 5 Mar 2026 10:01:08 +0100 Subject: [PATCH 04/15] Add "Collections" link to main navigation (#38065) --- .../features/navigation_panel/index.tsx | 17 +++++++++++++++++ app/javascript/mastodon/locales/en.json | 1 + .../material-icons/400-24px/category-fill.svg | 1 + .../material-icons/400-24px/category.svg | 1 + 4 files changed, 20 insertions(+) create mode 100644 app/javascript/material-icons/400-24px/category-fill.svg create mode 100644 app/javascript/material-icons/400-24px/category.svg diff --git a/app/javascript/mastodon/features/navigation_panel/index.tsx b/app/javascript/mastodon/features/navigation_panel/index.tsx index 4f2c490d5a..1c1e911a7b 100644 --- a/app/javascript/mastodon/features/navigation_panel/index.tsx +++ b/app/javascript/mastodon/features/navigation_panel/index.tsx @@ -14,6 +14,8 @@ import AddIcon from '@/material-icons/400-24px/add.svg?react'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react'; +import CollectionsActiveIcon from '@/material-icons/400-24px/category-fill.svg?react'; +import CollectionsIcon from '@/material-icons/400-24px/category.svg?react'; import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react'; import HomeIcon from '@/material-icons/400-24px/home.svg?react'; import InfoIcon from '@/material-icons/400-24px/info.svg?react'; @@ -48,6 +50,7 @@ import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifica import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { AnnualReportNavItem } from '../annual_report/nav_item'; +import { areCollectionsEnabled } from '../collections/utils'; import { DisabledAccountBanner } from './components/disabled_account_banner'; import { FollowedTagsPanel } from './components/followed_tags_panel'; @@ -71,6 +74,10 @@ const messages = defineMessages({ direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, + collections: { + id: 'navigation_bar.collections', + defaultMessage: 'Collections', + }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences', @@ -325,6 +332,16 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({ activeIconComponent={BookmarksActiveIcon} text={intl.formatMessage(messages.bookmarks)} /> + {areCollectionsEnabled() && ( + + )} \ No newline at end of file diff --git a/app/javascript/material-icons/400-24px/category.svg b/app/javascript/material-icons/400-24px/category.svg new file mode 100644 index 0000000000..59dd6a1b1e --- /dev/null +++ b/app/javascript/material-icons/400-24px/category.svg @@ -0,0 +1 @@ + \ No newline at end of file From dd27c7b9179a75df45d213cb6305343aabb7884e Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 5 Mar 2026 10:10:49 +0100 Subject: [PATCH 05/15] =?UTF-8?q?Fix=20=E2=80=9CUnblock=E2=80=9D=20and=20?= =?UTF-8?q?=E2=80=9CUnmute=E2=80=9D=20actions=20being=20disabled=20when=20?= =?UTF-8?q?blocked=20(#38075)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/mastodon/components/follow_button.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/components/follow_button.tsx b/app/javascript/mastodon/components/follow_button.tsx index 719294f190..e715de51c8 100644 --- a/app/javascript/mastodon/components/follow_button.tsx +++ b/app/javascript/mastodon/components/follow_button.tsx @@ -138,6 +138,8 @@ export const FollowButton: React.FC<{ : messages.follow; let label; + let disabled = + relationship?.blocked_by || account?.suspended || !!account?.moved; if (!signedIn) { label = intl.formatMessage(followMessage); @@ -147,12 +149,16 @@ export const FollowButton: React.FC<{ label = ; } else if (relationship.muting && withUnmute) { label = intl.formatMessage(messages.unmute); + disabled = false; } else if (relationship.following) { label = intl.formatMessage(messages.unfollow); + disabled = false; } else if (relationship.blocking) { label = intl.formatMessage(messages.unblock); + disabled = false; } else if (relationship.requested) { label = intl.formatMessage(messages.followRequestCancel); + disabled = false; } else if (relationship.followed_by && !account?.locked) { label = intl.formatMessage(messages.followBack); } else { @@ -187,11 +193,7 @@ export const FollowButton: React.FC<{ return ( + {!hasFields && ( + + + + )} + = ({ onClose }) => { const intl = useIntl(); const titleId = useId(); - const counterId = useId(); - const textAreaRef = useRef(null); const { profile: { bio } = {}, isPending } = useAppSelector( (state) => state.profileEdit, ); const [newBio, setNewBio] = useState(bio ?? ''); - const handleChange: ChangeEventHandler = useCallback( - (event) => { - setNewBio(event.currentTarget.value); - }, - [], + const maxLength = useAppSelector( + (state) => + state.server.getIn([ + 'server', + 'configuration', + 'accounts', + 'max_note_length', + ]) as number | undefined, ); - const handlePickEmoji = useCallback((emoji: string) => { - setNewBio((prev) => { - const position = textAreaRef.current?.selectionStart ?? prev.length; - return insertEmojiAtPosition(prev, emoji, position); - }); - }, []); const dispatch = useAppDispatch(); const handleSave = useCallback(() => { @@ -70,27 +57,18 @@ export const BioModal: FC = ({ onClose }) => { onConfirm={handleSave} onClose={onClose} updating={isPending} - disabled={newBio.length > MAX_BIO_LENGTH} + disabled={!!maxLength && newBio.length > maxLength} noFocusButton > -
-