sforkowany z mirror/soapbox
Move RSS button to account menu
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>toast-story
rodzic
8be2ee918d
commit
fe50acf0c3
|
@ -21,12 +21,12 @@ import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, Men
|
||||||
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
||||||
import MovedNote from 'soapbox/features/account-timeline/components/moved-note';
|
import MovedNote from 'soapbox/features/account-timeline/components/moved-note';
|
||||||
import ActionButton from 'soapbox/features/ui/components/action-button';
|
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 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 { normalizeAttachment } from 'soapbox/normalizers';
|
||||||
import { Account } from 'soapbox/types/entities';
|
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';
|
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' },
|
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}' },
|
userUnendorsed: { id: 'account.unendorse.success', defaultMessage: 'You are no longer featuring @{acct}' },
|
||||||
profileExternal: { id: 'account.profile_external', defaultMessage: 'View profile on {domain}' },
|
profileExternal: { id: 'account.profile_external', defaultMessage: 'View profile on {domain}' },
|
||||||
|
subscribeFeed: { id: 'account.rss_feed', defaultMessage: 'Subscribe to RSS feed' },
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IHeader {
|
interface IHeader {
|
||||||
|
@ -82,6 +83,8 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const ownAccount = useOwnAccount();
|
const ownAccount = useOwnAccount();
|
||||||
|
|
||||||
|
const { software } = useAppSelector((state) => parseVersion(state.instance.version));
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return (
|
return (
|
||||||
<div className='-mt-4 -mx-4'>
|
<div className='-mt-4 -mx-4'>
|
||||||
|
@ -243,6 +246,10 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRssFeedClick = () => {
|
||||||
|
window.open(software === MASTODON ? `${account.url}.rss` : `${account.url}/feed.rss`, '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
const handleShare = () => {
|
const handleShare = () => {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
text: `@${account.acct}`,
|
text: `@${account.acct}`,
|
||||||
|
@ -255,20 +262,43 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
const makeMenu = () => {
|
const makeMenu = () => {
|
||||||
const menu: MenuType = [];
|
const menu: MenuType = [];
|
||||||
|
|
||||||
if (!account || !ownAccount) {
|
if (!account) {
|
||||||
return [];
|
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) {
|
if ('share' in navigator) {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.share, { name: account.username }),
|
text: intl.formatMessage(messages.share, { name: account.username }),
|
||||||
action: handleShare,
|
action: handleShare,
|
||||||
icon: require('@tabler/icons/upload.svg'),
|
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);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.id === ownAccount?.id) {
|
if (account.id === ownAccount.id) {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.edit_profile),
|
text: intl.formatMessage(messages.edit_profile),
|
||||||
to: '/settings/profile',
|
to: '/settings/profile',
|
||||||
|
@ -427,17 +457,9 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
icon: require('@tabler/icons/ban.svg'),
|
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(null);
|
||||||
|
|
||||||
menu.push({
|
menu.push({
|
||||||
|
@ -455,7 +477,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
|
|
||||||
if (!account || !ownAccount) return info;
|
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(
|
info.push(
|
||||||
<Badge
|
<Badge
|
||||||
key='followed_by'
|
key='followed_by'
|
||||||
|
@ -463,7 +485,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
title={<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />}
|
title={<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else if (ownAccount?.id !== account.id && account.relationship?.blocking) {
|
} else if (ownAccount.id !== account.id && account.relationship?.blocking) {
|
||||||
info.push(
|
info.push(
|
||||||
<Badge
|
<Badge
|
||||||
key='blocked'
|
key='blocked'
|
||||||
|
@ -473,7 +495,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ownAccount?.id !== account.id && account.relationship?.muting) {
|
if (ownAccount.id !== account.id && account.relationship?.muting) {
|
||||||
info.push(
|
info.push(
|
||||||
<Badge
|
<Badge
|
||||||
key='muted'
|
key='muted'
|
||||||
|
@ -481,7 +503,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
title={<FormattedMessage id='account.muted' defaultMessage='Muted' />}
|
title={<FormattedMessage id='account.muted' defaultMessage='Muted' />}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else if (ownAccount?.id !== account.id && account.relationship?.domain_blocking) {
|
} else if (ownAccount.id !== account.id && account.relationship?.domain_blocking) {
|
||||||
info.push(
|
info.push(
|
||||||
<Badge
|
<Badge
|
||||||
key='domain_blocked'
|
key='domain_blocked'
|
||||||
|
@ -495,7 +517,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// const renderMessageButton = () => {
|
// const renderMessageButton = () => {
|
||||||
// if (!ownAccount || !account || account.id === ownAccount?.id) {
|
// if (!ownAccount || !account || account.id === ownAccount.id) {
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -587,7 +609,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
<div className='mt-10 flex flex-row space-y-0 space-x-2'>
|
<div className='mt-10 flex flex-row space-y-0 space-x-2'>
|
||||||
<SubscriptionButton account={account} />
|
<SubscriptionButton account={account} />
|
||||||
|
|
||||||
{ownAccount ? (
|
{menu.length > 0 && (
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
|
@ -621,8 +643,6 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
||||||
})}
|
})}
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
) : (
|
|
||||||
<FeedButton account={account} />
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{renderShareButton()}
|
{renderShareButton()}
|
||||||
|
|
|
@ -157,6 +157,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
||||||
onClick={handleRemoteFollow}
|
onClick={handleRemoteFollow}
|
||||||
icon={require('@tabler/icons/plus.svg')}
|
icon={require('@tabler/icons/plus.svg')}
|
||||||
text={intl.formatMessage(messages.follow)}
|
text={intl.formatMessage(messages.follow)}
|
||||||
|
size='sm'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
// Pleroma's classic remote follow form.
|
// Pleroma's classic remote follow form.
|
||||||
|
@ -165,7 +166,11 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
||||||
<form method='POST' action='/main/ostatus'>
|
<form method='POST' action='/main/ostatus'>
|
||||||
<input type='hidden' name='nickname' value={account.acct} />
|
<input type='hidden' name='nickname' value={account.acct} />
|
||||||
<input type='hidden' name='profile' value='' />
|
<input type='hidden' name='profile' value='' />
|
||||||
<Button text={intl.formatMessage(messages.remote_follow)} type='submit' />
|
<Button
|
||||||
|
text={intl.formatMessage(messages.remote_follow)}
|
||||||
|
type='submit'
|
||||||
|
size='sm'
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { IconButton } from 'soapbox/components/ui';
|
|
||||||
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
|
|
||||||
import { isLocal } from 'soapbox/utils/accounts';
|
|
||||||
import { parseVersion, MASTODON, PLEROMA } from 'soapbox/utils/features';
|
|
||||||
|
|
||||||
import type { Account as AccountEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
subscribeFeed: { id: 'account.rss_feed', defaultMessage: 'Subscribe to RSS feed' },
|
|
||||||
});
|
|
||||||
|
|
||||||
interface IFeedButton {
|
|
||||||
account: AccountEntity
|
|
||||||
}
|
|
||||||
|
|
||||||
const FeedButton = ({ account }: IFeedButton) => {
|
|
||||||
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 (
|
|
||||||
<IconButton
|
|
||||||
src={require('@tabler/icons/rss.svg')}
|
|
||||||
onClick={() => window.open(feedUrl, '_blank')}
|
|
||||||
title={intl.formatMessage(messages.subscribeFeed)}
|
|
||||||
theme='outlined'
|
|
||||||
className='px-[10px]'
|
|
||||||
iconClassName='w-4 h-4'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FeedButton;
|
|
|
@ -543,6 +543,14 @@ const getInstanceFeatures = (instance: Instance) => {
|
||||||
v.software === PLEROMA,
|
v.software === PLEROMA,
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability to follow account feeds using RSS.
|
||||||
|
*/
|
||||||
|
rssFeeds: any([
|
||||||
|
v.software === MASTODON,
|
||||||
|
v.software === PLEROMA,
|
||||||
|
]),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can schedule statuses to be posted at a later time.
|
* Can schedule statuses to be posted at a later time.
|
||||||
* @see POST /api/v1/statuses
|
* @see POST /api/v1/statuses
|
||||||
|
|
Ładowanie…
Reference in New Issue