diff --git a/app/javascript/mastodon/components/hover_card_account.stories.tsx b/app/javascript/mastodon/components/hover_card_account.stories.tsx new file mode 100644 index 00000000000..cc3f23b2179 --- /dev/null +++ b/app/javascript/mastodon/components/hover_card_account.stories.tsx @@ -0,0 +1,142 @@ +import { Map as ImmutableMap } from 'immutable'; +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { accountFactory, accountFactoryState } from '@/testing/factories'; + +import { HoverCardAccount } from './hover_card_account'; + +const meta = { + title: 'Components/HoverCardAccount', + component: HoverCardAccount, + argTypes: { + accountId: { + type: 'string', + description: 'ID of the account to display in the hover card', + }, + }, + args: { + accountId: '1', + }, + decorators: [ + (Story) => ( +
+

+ Hover card examples - demonstrating Issue #35623 fix for moved accounts +

+ +
+ ), + ], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +// Mock data for different account states +const regularAccount = accountFactoryState({ + id: '1', + username: 'alice', + acct: 'alice@mastodon.social', + display_name: 'Alice Johnson', + note: 'Frontend developer who loves building amazing user interfaces. Coffee enthusiast ☕', + followers_count: 1250, + following_count: 342, + statuses_count: 1840, + avatar: 'https://picsum.photos/200/200?random=1', + header: 'https://picsum.photos/600/200?random=1', + fields: [ + { name: 'Website', value: 'https://alice.dev', verified_at: null }, + { name: 'Location', value: 'San Francisco, CA', verified_at: null }, + ], +}); + +const movedAccount = accountFactoryState({ + id: '2', + username: 'bob_old', + acct: 'bob_old@mastodon.social', + display_name: 'Bob Smith (Moved)', + note: 'I have moved to a new account. Please follow me there!', + followers_count: 890, + following_count: 156, + statuses_count: 420, + avatar: 'https://picsum.photos/200/200?random=2', + header: 'https://picsum.photos/600/200?random=2', + moved: accountFactory({ + id: '3', + username: 'bob_new', + acct: 'bob_new@social.example', + display_name: 'Bob Smith', + followers_count: 950, + following_count: 180, + statuses_count: 45, + avatar: 'https://picsum.photos/200/200?random=3', + header: 'https://picsum.photos/600/200?random=3', + }), +}); + +const newAccount = accountFactoryState({ + id: '3', + username: 'bob_new', + acct: 'bob_new@social.example', + display_name: 'Bob Smith', + note: 'This is my new account! Thanks for following me here.', + followers_count: 950, + following_count: 180, + statuses_count: 45, + avatar: 'https://picsum.photos/200/200?random=3', + header: 'https://picsum.photos/600/200?random=3', +}); + +export const RegularAccount: Story = { + args: { + accountId: '1', + }, + parameters: { + state: { + accounts: ImmutableMap({ + '1': regularAccount, + }), + relationships: ImmutableMap(), + meta: ImmutableMap({ + locale: 'en', + emoji_style: 'unicode', + }), + }, + }, +}; + +export const MovedAccount: Story = { + args: { + accountId: '2', + }, + parameters: { + state: { + accounts: ImmutableMap({ + '2': movedAccount, + '3': newAccount, + }), + relationships: ImmutableMap(), + meta: ImmutableMap({ + locale: 'en', + emoji_style: 'unicode', + }), + }, + }, +}; + +export const LoadingState: Story = { + args: { + accountId: '999', + }, + parameters: { + state: { + accounts: ImmutableMap(), + relationships: ImmutableMap(), + meta: ImmutableMap({ + locale: 'en', + emoji_style: 'unicode', + }), + }, + }, +}; diff --git a/app/javascript/mastodon/components/hover_card_account.tsx b/app/javascript/mastodon/components/hover_card_account.tsx index 67004b59c0d..a01907ec8af 100644 --- a/app/javascript/mastodon/components/hover_card_account.tsx +++ b/app/javascript/mastodon/components/hover_card_account.tsx @@ -32,6 +32,11 @@ export const HoverCardAccount = forwardRef< const account = useAppSelector((state) => accountId ? state.accounts.get(accountId) : undefined, ); + // Get the moved account data if this account has moved + // This allows us to show proper follow button for the new account + const movedAccount = useAppSelector((state) => + account?.moved ? state.accounts.get(account.moved) : undefined, + ); const suspended = account?.suspended; const hidden = useAppSelector((state) => accountId ? getAccountHidden(state, accountId) : undefined, @@ -99,18 +104,26 @@ export const HoverCardAccount = forwardRef< )} ) : account.moved ? ( + // Issue #35623: Show moved account notice instead of regular content + // This prevents following the old account and provides a link + follow button for the new account
- @{account.moved.acct} + acct: movedAccount ? ( + + @{movedAccount.acct} + ) : ( + @{account.moved} ), }} /> + {/* Follow button that follows the NEW account, not the old one */} + {movedAccount && ( + + )}
) : ( <> diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 2b66da007e1..f1a137bb5ba 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -11031,6 +11031,9 @@ noscript { text-align: center; font-weight: 500; color: $secondary-text-color; + display: flex; + flex-direction: column; + gap: 12px; a { color: inherit;