From 5d10324127f9168fc214f61680c0daa325540117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 11 Nov 2022 00:11:19 +0100 Subject: [PATCH 01/40] Allow displaying RSS button to unauthenticated users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../features/account/components/header.tsx | 5 +- app/soapbox/features/soapbox_config/index.tsx | 8 +++ .../features/ui/components/feed-button.tsx | 50 +++++++++++++++++++ .../ui/components/subscription-button.tsx | 12 ++--- app/soapbox/locales/pl.json | 1 + .../normalizers/soapbox/soapbox_config.ts | 1 + webpack/production.ts | 2 +- 7 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 app/soapbox/features/ui/components/feed-button.tsx diff --git a/app/soapbox/features/account/components/header.tsx b/app/soapbox/features/account/components/header.tsx index 21e44f33b..a8f8f2901 100644 --- a/app/soapbox/features/account/components/header.tsx +++ b/app/soapbox/features/account/components/header.tsx @@ -21,6 +21,7 @@ import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, Men import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import MovedNote from 'soapbox/features/account_timeline/components/moved_note'; import ActionButton from 'soapbox/features/ui/components/action-button'; +import FeedButton from 'soapbox/features/ui/components/feed-button'; import SubscriptionButton from 'soapbox/features/ui/components/subscription-button'; import { useAppDispatch, useFeatures, useOwnAccount } from 'soapbox/hooks'; import { normalizeAttachment } from 'soapbox/normalizers'; @@ -573,7 +574,7 @@ const Header: React.FC = ({ account }) => {
- {ownAccount && ( + {ownAccount ? ( = ({ account }) => { })} + ) : ( + )} {renderShareButton()} diff --git a/app/soapbox/features/soapbox_config/index.tsx b/app/soapbox/features/soapbox_config/index.tsx index 222c44434..0f71bbf61 100644 --- a/app/soapbox/features/soapbox_config/index.tsx +++ b/app/soapbox/features/soapbox_config/index.tsx @@ -49,6 +49,7 @@ const messages = defineMessages({ authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' }, authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' }, displayCtaLabel: { id: 'soapbox_config.cta_label', defaultMessage: 'Display call to action panels if not authenticated' }, + featureFeedsLabel: { id: 'soapbox_config.feature_feeds_label', defaultMessage: 'Feature RSS feeds to unauthenticated users' }, singleUserModeLabel: { id: 'soapbox_config.single_user_mode_label', defaultMessage: 'Single user mode' }, singleUserModeHint: { id: 'soapbox_config.single_user_mode_hint', defaultMessage: 'Front page will redirect to a given user profile.' }, singleUserModeProfileLabel: { id: 'soapbox_config.single_user_mode_profile_label', defaultMessage: 'Main user handle' }, @@ -269,6 +270,13 @@ const SoapboxConfig: React.FC = () => { /> + + e.target.checked)} + /> + + { + const intl = useIntl(); + const { featureFeeds } = useSoapboxConfig(); + + const { software } = useAppSelector((state) => parseVersion(state.instance.version)); + + let feedUrl: string | undefined; + + switch (software) { + case MASTODON: + feedUrl = `${account.url}.rss`; + break; + case PLEROMA: + feedUrl = `${account.url}/feed.rss`; + break; + } + + if (!featureFeeds || !feedUrl || !isLocal(account)) return null; + + return ( + window.open(feedUrl, '_blank')} + title={intl.formatMessage(messages.subscribeFeed)} + theme='outlined' + className='px-[10px]' + iconClassName='w-4 h-4' + /> + ); +}; + +export default FeedButton; diff --git a/app/soapbox/features/ui/components/subscription-button.tsx b/app/soapbox/features/ui/components/subscription-button.tsx index 8080a7c52..242fc0cfc 100644 --- a/app/soapbox/features/ui/components/subscription-button.tsx +++ b/app/soapbox/features/ui/components/subscription-button.tsx @@ -32,12 +32,12 @@ const SubscriptionButton = ({ account }: ISubscriptionButton) => { const isFollowing = account.relationship?.following; const isRequested = account.relationship?.requested; - const isSubscribed = features.accountNotifies ? - account.relationship?.notifying : - account.relationship?.subscribing; - const title = isSubscribed ? - intl.formatMessage(messages.unsubscribe, { name: account.get('username') }) : - intl.formatMessage(messages.subscribe, { name: account.get('username') }); + const isSubscribed = features.accountNotifies + ? account.relationship?.notifying + : account.relationship?.subscribing; + const title = isSubscribed + ? intl.formatMessage(messages.unsubscribe, { name: account.get('username') }) + : intl.formatMessage(messages.subscribe, { name: account.get('username') }); const onSubscribeSuccess = () => dispatch(snackbar.success(intl.formatMessage(messages.subscribeSuccess))); diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index 23a13c2ba..9d4471af2 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -48,6 +48,7 @@ "account.report": "Zgłoś @{name}", "account.requested": "Oczekująca prośba, kliknij aby anulować", "account.requested_small": "Oczekująca prośba", + "account.rss_feed": "Subskrybuj kanał RSS", "account.search": "Szukaj wpisów @{name}", "account.search_self": "Szukaj własnych wpisów", "account.share": "Udostępnij profil @{name}", diff --git a/app/soapbox/normalizers/soapbox/soapbox_config.ts b/app/soapbox/normalizers/soapbox/soapbox_config.ts index 0447b3fc5..ac2ccdcf6 100644 --- a/app/soapbox/normalizers/soapbox/soapbox_config.ts +++ b/app/soapbox/normalizers/soapbox/soapbox_config.ts @@ -112,6 +112,7 @@ export const SoapboxConfigRecord = ImmutableRecord({ linkFooterMessage: '', links: ImmutableMap(), displayCta: true, + featureFeeds: false, }, 'SoapboxConfig'); type SoapboxConfigMap = ImmutableMap; diff --git a/webpack/production.ts b/webpack/production.ts index 6115b6aa4..08bab9732 100644 --- a/webpack/production.ts +++ b/webpack/production.ts @@ -133,7 +133,7 @@ const configuration: Configuration = { '/unsubscribe', ]; - if (backendRoutes.some(path => pathname.startsWith(path)) || pathname.endsWith('/embed')) { + if (backendRoutes.some(path => pathname.startsWith(path)) || pathname.endsWith('/embed') || pathname.endsWith('.rss')) { return url; } }, From fe50acf0c3f743caacc6bae86a9a7816477dd9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 21 Nov 2022 21:03:35 +0100 Subject: [PATCH 02/40] Move RSS button to account menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../features/account/components/header.tsx | 64 ++++++++++++------- .../features/ui/components/action-button.tsx | 7 +- .../features/ui/components/feed-button.tsx | 50 --------------- app/soapbox/utils/features.ts | 8 +++ 4 files changed, 56 insertions(+), 73 deletions(-) delete mode 100644 app/soapbox/features/ui/components/feed-button.tsx diff --git a/app/soapbox/features/account/components/header.tsx b/app/soapbox/features/account/components/header.tsx index 016f0ef74..3bc3bfb50 100644 --- a/app/soapbox/features/account/components/header.tsx +++ b/app/soapbox/features/account/components/header.tsx @@ -21,12 +21,12 @@ import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, Men import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import MovedNote from 'soapbox/features/account-timeline/components/moved-note'; import ActionButton from 'soapbox/features/ui/components/action-button'; -import FeedButton from 'soapbox/features/ui/components/feed-button'; import SubscriptionButton from 'soapbox/features/ui/components/subscription-button'; -import { useAppDispatch, useFeatures, useOwnAccount } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks'; import { normalizeAttachment } from 'soapbox/normalizers'; import { Account } from 'soapbox/types/entities'; -import { isRemote } from 'soapbox/utils/accounts'; +import { isLocal, isRemote } from 'soapbox/utils/accounts'; +import { MASTODON, parseVersion } from 'soapbox/utils/features'; import type { Menu as MenuType } from 'soapbox/components/dropdown-menu'; @@ -68,6 +68,7 @@ const messages = defineMessages({ userEndorsed: { id: 'account.endorse.success', defaultMessage: 'You are now featuring @{acct} on your profile' }, userUnendorsed: { id: 'account.unendorse.success', defaultMessage: 'You are no longer featuring @{acct}' }, profileExternal: { id: 'account.profile_external', defaultMessage: 'View profile on {domain}' }, + subscribeFeed: { id: 'account.rss_feed', defaultMessage: 'Subscribe to RSS feed' }, }); interface IHeader { @@ -82,6 +83,8 @@ const Header: React.FC = ({ account }) => { const features = useFeatures(); const ownAccount = useOwnAccount(); + const { software } = useAppSelector((state) => parseVersion(state.instance.version)); + if (!account) { return (
@@ -243,6 +246,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}`, @@ -255,20 +262,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', @@ -427,17 +457,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({ @@ -455,7 +477,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 }) => { }; // const renderMessageButton = () => { - // if (!ownAccount || !account || account.id === ownAccount?.id) { + // if (!ownAccount || !account || account.id === ownAccount.id) { // return null; // } @@ -587,7 +609,7 @@ const Header: React.FC = ({ account }) => {
- {ownAccount ? ( + {menu.length > 0 && ( = ({ account }) => { })} - ) : ( - )} {renderShareButton()} diff --git a/app/soapbox/features/ui/components/action-button.tsx b/app/soapbox/features/ui/components/action-button.tsx index 299702d94..bd031d450 100644 --- a/app/soapbox/features/ui/components/action-button.tsx +++ b/app/soapbox/features/ui/components/action-button.tsx @@ -157,6 +157,7 @@ const ActionButton: React.FC = ({ account, actionType, small }) = onClick={handleRemoteFollow} icon={require('@tabler/icons/plus.svg')} text={intl.formatMessage(messages.follow)} + size='sm' /> ); // Pleroma's classic remote follow form. @@ -165,7 +166,11 @@ const ActionButton: React.FC = ({ account, actionType, small }) =
-