From 33f739da44d81628d24a0c532a2f90953e935d18 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 13 Oct 2025 17:18:01 +0200 Subject: [PATCH 1/2] Fix permalink redirects continuing to work for suspended accounts (#36453) --- app/lib/permalink_redirector.rb | 6 +- spec/lib/permalink_redirector_spec.rb | 98 +++++++++++++++++++-------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/app/lib/permalink_redirector.rb b/app/lib/permalink_redirector.rb index 301a588686..19fb3f401c 100644 --- a/app/lib/permalink_redirector.rb +++ b/app/lib/permalink_redirector.rb @@ -12,15 +12,15 @@ class PermalinkRedirector @object ||= begin if at_username_status_request? || statuses_status_request? status = Status.find_by(id: second_segment) - status if status&.distributable? && !status&.local? + status if status&.distributable? && !status&.local? && !status&.account&.suspended? elsif at_username_request? username, domain = first_segment.delete_prefix('@').split('@') domain = nil if TagManager.instance.local_domain?(domain) account = Account.find_remote(username, domain) - account unless account&.local? + account if !account&.local? && !account&.suspended? elsif accounts_request? && record_integer_id_request? account = Account.find_by(id: second_segment) - account unless account&.local? + account if !account&.local? && !account&.suspended? end end end diff --git a/spec/lib/permalink_redirector_spec.rb b/spec/lib/permalink_redirector_spec.rb index 5a544c3d38..81fa05449e 100644 --- a/spec/lib/permalink_redirector_spec.rb +++ b/spec/lib/permalink_redirector_spec.rb @@ -10,39 +10,77 @@ RSpec.describe PermalinkRedirector do Fabricate(:status, account: remote_account, id: 123, url: 'https://example.com/status-123') end - it 'returns path for legacy account links' do - redirector = described_class.new('accounts/2') - expect(redirector.redirect_path).to eq 'https://example.com/@alice' - end - - it 'returns path for legacy status links' do - redirector = described_class.new('statuses/123') - expect(redirector.redirect_path).to eq 'https://example.com/status-123' - end - - it 'returns path for pretty account links' do - redirector = described_class.new('@alice@example.com') - expect(redirector.redirect_path).to eq 'https://example.com/@alice' - end - - it 'returns path for pretty status links' do - redirector = described_class.new('@alice/123') - expect(redirector.redirect_path).to eq 'https://example.com/status-123' - end - - it 'returns path for legacy status links with a query param' do - redirector = described_class.new('statuses/123?foo=bar') - expect(redirector.redirect_path).to eq 'https://example.com/status-123' - end - - it 'returns path for pretty status links with a query param' do - redirector = described_class.new('@alice/123?foo=bar') - expect(redirector.redirect_path).to eq 'https://example.com/status-123' - end - it 'returns path for deck URLs with query params' do redirector = described_class.new('/deck/directory?local=true') expect(redirector.redirect_path).to eq '/directory?local=true' end + + context 'when account is not suspended' do + it 'returns path for legacy account links' do + redirector = described_class.new('accounts/2') + expect(redirector.redirect_path).to eq 'https://example.com/@alice' + end + + it 'returns path for legacy status links' do + redirector = described_class.new('statuses/123') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + + it 'returns path for pretty account links' do + redirector = described_class.new('@alice@example.com') + expect(redirector.redirect_path).to eq 'https://example.com/@alice' + end + + it 'returns path for pretty status links' do + redirector = described_class.new('@alice/123') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + + it 'returns path for legacy status links with a query param' do + redirector = described_class.new('statuses/123?foo=bar') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + + it 'returns path for pretty status links with a query param' do + redirector = described_class.new('@alice/123?foo=bar') + expect(redirector.redirect_path).to eq 'https://example.com/status-123' + end + end + + context 'when account is suspended' do + before do + remote_account.suspend! + end + + it 'returns nil for legacy account links' do + redirector = described_class.new('accounts/2') + expect(redirector.redirect_path).to be_nil + end + + it 'returns nil for legacy status links' do + redirector = described_class.new('statuses/123') + expect(redirector.redirect_path).to be_nil + end + + it 'returns nil for pretty account links' do + redirector = described_class.new('@alice@example.com') + expect(redirector.redirect_path).to be_nil + end + + it 'returns nil for pretty status links' do + redirector = described_class.new('@alice/123') + expect(redirector.redirect_path).to be_nil + end + + it 'returns nil for legacy status links with a query param' do + redirector = described_class.new('statuses/123?foo=bar') + expect(redirector.redirect_path).to be_nil + end + + it 'returns nil for pretty status links with a query param' do + redirector = described_class.new('@alice/123?foo=bar') + expect(redirector.redirect_path).to be_nil + end + end end end From edd7fd98722a0d49ea486aa790a186735e491f35 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 13 Oct 2025 17:29:39 +0200 Subject: [PATCH 2/2] Emoji: Picker native rendering (#36454) --- .../mastodon/features/emoji/emoji_picker.tsx | 7 +++++++ app/javascript/mastodon/features/emoji/hooks.ts | 11 +++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/features/emoji/emoji_picker.tsx b/app/javascript/mastodon/features/emoji/emoji_picker.tsx index f5300c9fec..6dcfe37ac8 100644 --- a/app/javascript/mastodon/features/emoji/emoji_picker.tsx +++ b/app/javascript/mastodon/features/emoji/emoji_picker.tsx @@ -2,9 +2,12 @@ import type { EmojiProps, PickerProps } from 'emoji-mart'; import EmojiRaw from 'emoji-mart/dist-es/components/emoji/nimble-emoji'; import PickerRaw from 'emoji-mart/dist-es/components/picker/nimble-picker'; +import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; import { assetHost } from 'mastodon/utils/config'; +import { EMOJI_MODE_NATIVE } from './constants'; import EmojiData from './emoji_data.json'; +import { useEmojiAppState } from './hooks'; const backgroundImageFnDefault = () => `${assetHost}/emoji/sheet_15_1.png`; @@ -16,6 +19,7 @@ const Emoji = ({ backgroundImageFn = backgroundImageFnDefault, ...props }: EmojiProps) => { + const { mode } = useEmojiAppState(); return ( @@ -37,6 +42,7 @@ const Picker = ({ backgroundImageFn = backgroundImageFnDefault, ...props }: PickerProps) => { + const { mode } = useEmojiAppState(); return ( ); diff --git a/app/javascript/mastodon/features/emoji/hooks.ts b/app/javascript/mastodon/features/emoji/hooks.ts index b3b27d274a..dbd20d3044 100644 --- a/app/javascript/mastodon/features/emoji/hooks.ts +++ b/app/javascript/mastodon/features/emoji/hooks.ts @@ -1,6 +1,6 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; -import { useAppSelector } from '@/mastodon/store'; +import { createAppSelector, useAppSelector } from '@/mastodon/store'; import { isModernEmojiEnabled } from '@/mastodon/utils/environment'; import { toSupportedLocale } from './locale'; @@ -58,13 +58,16 @@ export function useEmojify({ return emojifiedText; } +const modeSelector = createAppSelector( + [(state) => state.meta.get('emoji_style') as string], + (emoji_style) => determineEmojiMode(emoji_style), +); + export function useEmojiAppState(): EmojiAppState { const locale = useAppSelector((state) => toSupportedLocale(state.meta.get('locale') as string), ); - const mode = useAppSelector((state) => - determineEmojiMode(state.meta.get('emoji_style') as string), - ); + const mode = useAppSelector(modeSelector); return { currentLocale: locale,