diff --git a/.storybook/main.ts b/.storybook/main.ts index 00f1703c0..bb4c1d232 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -11,6 +11,7 @@ const config: StorybookConfig = { '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', + 'storybook-react-intl', { name: '@storybook/addon-postcss', options: { diff --git a/.storybook/preview.ts b/.storybook/preview.ts deleted file mode 100644 index c876c5abf..000000000 --- a/.storybook/preview.ts +++ /dev/null @@ -1,12 +0,0 @@ -import '../app/styles/tailwind.css'; -import '../stories/theme.css'; - -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -} \ No newline at end of file diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 000000000..df2195f0c --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,22 @@ +import '../app/styles/tailwind.css'; +import '../stories/theme.css'; + +import { addDecorator, Story } from '@storybook/react'; +import { IntlProvider } from 'react-intl'; +import React from 'react'; + +const withProvider = (Story: Story) => ( + +); + +addDecorator(withProvider); + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d0c5977..48fb6ddc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Compatibility: improved browser support for older browsers. - Events: allow to repost events in event menu. - Groups: Initial support for groups. +- Profile: Add RSS link to user profiles. ### Changed - Chats: improved display of media attachments. diff --git a/app/soapbox/components/ui/tabs/tabs.tsx b/app/soapbox/components/ui/tabs/tabs.tsx index 0d0d4d9a8..5b8c47cbf 100644 --- a/app/soapbox/components/ui/tabs/tabs.tsx +++ b/app/soapbox/components/ui/tabs/tabs.tsx @@ -46,7 +46,7 @@ const AnimatedTabs: React.FC = ({ children, ...rest }) => { ref={ref} >
= ({ account }) => { const features = useFeatures(); const ownAccount = useOwnAccount(); + const { software } = useAppSelector((state) => parseVersion(state.instance.version)); + const { getOrCreateChatByAccountId } = useChats(); const createAndNavigateToChat = useMutation((accountId: string) => { @@ -257,6 +261,10 @@ const Header: React.FC = ({ account }) => { } }; + const handleRssFeedClick = () => { + window.open(software === MASTODON ? `${account.url}.rss` : `${account.url}/feed.rss`, '_blank'); + }; + const handleShare = () => { navigator.share({ text: `@${account.acct}`, @@ -269,20 +277,43 @@ const Header: React.FC = ({ account }) => { const makeMenu = () => { const menu: MenuType = []; - if (!account || !ownAccount) { + if (!account) { return []; } + if (features.rssFeeds && isLocal(account)) { + menu.push({ + text: intl.formatMessage(messages.subscribeFeed), + action: handleRssFeedClick, + icon: require('@tabler/icons/rss.svg'), + }); + } + if ('share' in navigator) { menu.push({ text: intl.formatMessage(messages.share, { name: account.username }), action: handleShare, icon: require('@tabler/icons/upload.svg'), }); + } + + if (features.federating && isRemote(account)) { + const domain = account.fqn.split('@')[1]; + + menu.push({ + text: intl.formatMessage(messages.profileExternal, { domain }), + action: () => onProfileExternal(account.url), + icon: require('@tabler/icons/external-link.svg'), + }); + } + + if (!ownAccount) return menu; + + if (menu.length) { menu.push(null); } - if (account.id === ownAccount?.id) { + if (account.id === ownAccount.id) { menu.push({ text: intl.formatMessage(messages.edit_profile), to: '/settings/profile', @@ -435,17 +466,9 @@ const Header: React.FC = ({ account }) => { icon: require('@tabler/icons/ban.svg'), }); } - - if (features.federating) { - menu.push({ - text: intl.formatMessage(messages.profileExternal, { domain }), - action: () => onProfileExternal(account.url), - icon: require('@tabler/icons/external-link.svg'), - }); - } } - if (ownAccount?.staff) { + if (ownAccount.staff) { menu.push(null); menu.push({ @@ -463,7 +486,7 @@ const Header: React.FC = ({ account }) => { if (!account || !ownAccount) return info; - if (ownAccount?.id !== account.id && account.relationship?.followed_by) { + if (ownAccount.id !== account.id && account.relationship?.followed_by) { info.push( = ({ account }) => { title={} />, ); - } else if (ownAccount?.id !== account.id && account.relationship?.blocking) { + } else if (ownAccount.id !== account.id && account.relationship?.blocking) { info.push( = ({ account }) => { ); } - if (ownAccount?.id !== account.id && account.relationship?.muting) { + if (ownAccount.id !== account.id && account.relationship?.muting) { info.push( = ({ account }) => { title={} />, ); - } else if (ownAccount?.id !== account.id && account.relationship?.domain_blocking) { + } else if (ownAccount.id !== account.id && account.relationship?.domain_blocking) { info.push( = ({ account }) => { {renderMessageButton()} {renderShareButton()} - {ownAccount && ( + {menu.length > 0 && ( = ({ account, actionType, small }) = onClick={handleRemoteFollow} icon={require('@tabler/icons/plus.svg')} text={intl.formatMessage(messages.follow)} + size='sm' /> ); // Pleroma's classic remote follow form. @@ -164,7 +165,11 @@ const ActionButton: React.FC = ({ account, actionType, small }) =
-