Profile redesign: Follow button and menu reorg (#37707)
This commit is contained in:
parent
218ca36653
commit
346ca87ee8
@ -153,7 +153,8 @@ export function fetchAccountFail(id, error) {
|
|||||||
*/
|
*/
|
||||||
export function followAccount(id, options = { reblogs: true }) {
|
export function followAccount(id, options = { reblogs: true }) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
|
const relationship = getState().getIn(['relationships', id]);
|
||||||
|
const alreadyFollowing = relationship?.following || relationship?.requested;
|
||||||
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
||||||
|
|
||||||
dispatch(followAccountRequest({ id, locked }));
|
dispatch(followAccountRequest({ id, locked }));
|
||||||
|
|||||||
@ -71,10 +71,15 @@ export const DropdownMenuItemContent: React.FC<{ item: MenuItem }> = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text, description, icon } = item;
|
const { text, description, icon, iconId } = item;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{icon && <Icon icon={icon} id={`${text}-icon`} />}
|
{icon && (
|
||||||
|
<Icon
|
||||||
|
icon={icon}
|
||||||
|
id={iconId ?? text.toLowerCase().replaceAll(/[^a-z]+/g, '-')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className='dropdown-menu__item-content'>
|
<span className='dropdown-menu__item-content'>
|
||||||
{text}
|
{text}
|
||||||
{Boolean(description) && (
|
{Boolean(description) && (
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { useIntl, defineMessages } from 'react-intl';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { useIdentity } from '@/mastodon/identity_context';
|
import { useIdentity } from '@/mastodon/identity_context';
|
||||||
|
import { isClientFeatureEnabled } from '@/mastodon/utils/environment';
|
||||||
import {
|
import {
|
||||||
fetchRelationships,
|
fetchRelationships,
|
||||||
followAccount,
|
followAccount,
|
||||||
@ -94,7 +95,17 @@ export const FollowButton: React.FC<{
|
|||||||
|
|
||||||
if (accountId === me) {
|
if (accountId === me) {
|
||||||
return;
|
return;
|
||||||
} else if (relationship.muting) {
|
} else if (relationship.blocking) {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'CONFIRM_UNBLOCK',
|
||||||
|
modalProps: { account },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
relationship.muting &&
|
||||||
|
!isClientFeatureEnabled('profile_redesign')
|
||||||
|
) {
|
||||||
dispatch(unmuteAccount(accountId));
|
dispatch(unmuteAccount(accountId));
|
||||||
} else if (account && relationship.following) {
|
} else if (account && relationship.following) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -107,13 +118,6 @@ export const FollowButton: React.FC<{
|
|||||||
modalProps: { account },
|
modalProps: { account },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if (relationship.blocking) {
|
|
||||||
dispatch(
|
|
||||||
openModal({
|
|
||||||
modalType: 'CONFIRM_UNBLOCK',
|
|
||||||
modalProps: { account },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(followAccount(accountId));
|
dispatch(followAccount(accountId));
|
||||||
}
|
}
|
||||||
@ -136,7 +140,10 @@ export const FollowButton: React.FC<{
|
|||||||
label = intl.formatMessage(messages.editProfile);
|
label = intl.formatMessage(messages.editProfile);
|
||||||
} else if (!relationship) {
|
} else if (!relationship) {
|
||||||
label = <LoadingIndicator />;
|
label = <LoadingIndicator />;
|
||||||
} else if (relationship.muting) {
|
} else if (
|
||||||
|
relationship.muting &&
|
||||||
|
!isClientFeatureEnabled('profile_redesign')
|
||||||
|
) {
|
||||||
label = intl.formatMessage(messages.unmute);
|
label = intl.formatMessage(messages.unmute);
|
||||||
} else if (relationship.following) {
|
} else if (relationship.following) {
|
||||||
label = intl.formatMessage(messages.unfollow);
|
label = intl.formatMessage(messages.unfollow);
|
||||||
@ -173,7 +180,7 @@ export const FollowButton: React.FC<{
|
|||||||
(!(relationship?.following || relationship?.requested) &&
|
(!(relationship?.following || relationship?.requested) &&
|
||||||
(account?.suspended || !!account?.moved))
|
(account?.suspended || !!account?.moved))
|
||||||
}
|
}
|
||||||
secondary={following}
|
secondary={following || relationship?.blocking}
|
||||||
compact={compact}
|
compact={compact}
|
||||||
className={classNames(className, { 'button--destructive': following })}
|
className={classNames(className, { 'button--destructive': following })}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import type { RefCallback } from 'react';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@ -77,6 +78,40 @@ export const AccountHeader: React.FC<{
|
|||||||
[dispatch, account],
|
[dispatch, account],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [isFooterIntersecting, setIsIntersecting] = useState(false);
|
||||||
|
const handleIntersect: IntersectionObserverCallback = useCallback(
|
||||||
|
(entries) => {
|
||||||
|
const entry = entries.at(0);
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsIntersecting(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [observer] = useState(
|
||||||
|
() =>
|
||||||
|
new IntersectionObserver(handleIntersect, {
|
||||||
|
rootMargin: '0px 0px -55px 0px', // Height of bottom nav bar.
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleObserverRef: RefCallback<HTMLDivElement> = useCallback(
|
||||||
|
(node) => {
|
||||||
|
if (node) {
|
||||||
|
observer.observe(node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[observer],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, [observer]);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -114,7 +149,12 @@ export const AccountHeader: React.FC<{
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__header__bar'>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'account__header__bar',
|
||||||
|
isRedesignEnabled() && redesignClasses.barWrapper,
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className='account__header__tabs'>
|
<div className='account__header__tabs'>
|
||||||
<a
|
<a
|
||||||
className='avatar'
|
className='avatar'
|
||||||
@ -144,7 +184,13 @@ export const AccountHeader: React.FC<{
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<AccountName accountId={accountId} />
|
<AccountName accountId={accountId} />
|
||||||
{isRedesignEnabled() && <AccountButtons accountId={accountId} />}
|
{isRedesignEnabled() && (
|
||||||
|
<AccountButtons
|
||||||
|
accountId={accountId}
|
||||||
|
className={redesignClasses.buttonsDesktop}
|
||||||
|
noShare
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AccountBadges accountId={accountId} />
|
<AccountBadges accountId={accountId} />
|
||||||
@ -153,11 +199,13 @@ export const AccountHeader: React.FC<{
|
|||||||
<FamiliarFollowers accountId={accountId} />
|
<FamiliarFollowers accountId={accountId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AccountButtons
|
{!isRedesignEnabled() && (
|
||||||
className='account__header__buttons--mobile'
|
<AccountButtons
|
||||||
accountId={accountId}
|
className='account__header__buttons--mobile'
|
||||||
noShare
|
accountId={accountId}
|
||||||
/>
|
noShare
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{!suspendedOrHidden && (
|
{!suspendedOrHidden && (
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
@ -181,10 +229,22 @@ export const AccountHeader: React.FC<{
|
|||||||
<AccountNumberFields accountId={accountId} />
|
<AccountNumberFields accountId={accountId} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isRedesignEnabled() && (
|
||||||
|
<AccountButtons
|
||||||
|
className={classNames(
|
||||||
|
redesignClasses.buttonsMobile,
|
||||||
|
!isFooterIntersecting && redesignClasses.buttonsMobileIsStuck,
|
||||||
|
)}
|
||||||
|
accountId={accountId}
|
||||||
|
noShare
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</AnimateEmojiProvider>
|
</AnimateEmojiProvider>
|
||||||
|
|
||||||
{!hideTabs && !hidden && <AccountTabs acct={account.acct} />}
|
{!hideTabs && !hidden && <AccountTabs acct={account.acct} />}
|
||||||
|
<div ref={handleObserverRef} />
|
||||||
|
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{titleFromAccount(account)}</title>
|
<title>{titleFromAccount(account)}</title>
|
||||||
|
|||||||
@ -108,48 +108,6 @@ export const AccountBadges: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
className={classNames(className, classes.badgeMuted)}
|
className={classNames(className, classes.badgeMuted)}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
} else if (
|
|
||||||
relationship.followed_by &&
|
|
||||||
(relationship.following || relationship.requested)
|
|
||||||
) {
|
|
||||||
badges.push(
|
|
||||||
<Badge
|
|
||||||
key='mutuals-badge'
|
|
||||||
label={
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.badges.mutuals'
|
|
||||||
defaultMessage='You follow each other'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
className={className}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
} else if (relationship.followed_by) {
|
|
||||||
badges.push(
|
|
||||||
<Badge
|
|
||||||
key='follows-you-badge'
|
|
||||||
label={
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.badges.follows_you'
|
|
||||||
defaultMessage='Follows you'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
className={className}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
} else if (relationship.requested_by) {
|
|
||||||
badges.push(
|
|
||||||
<Badge
|
|
||||||
key='requested-to-follow-badge'
|
|
||||||
label={
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.badges.requested_to_follow'
|
|
||||||
defaultMessage='Requested to follow you'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
className={className}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
unpinAccount,
|
unpinAccount,
|
||||||
} from '@/mastodon/actions/accounts';
|
} from '@/mastodon/actions/accounts';
|
||||||
import { removeAccountFromFollowers } from '@/mastodon/actions/accounts_typed';
|
import { removeAccountFromFollowers } from '@/mastodon/actions/accounts_typed';
|
||||||
|
import { showAlert } from '@/mastodon/actions/alerts';
|
||||||
import { directCompose, mentionCompose } from '@/mastodon/actions/compose';
|
import { directCompose, mentionCompose } from '@/mastodon/actions/compose';
|
||||||
import {
|
import {
|
||||||
initDomainBlockModal,
|
initDomainBlockModal,
|
||||||
@ -23,16 +24,78 @@ import { initReport } from '@/mastodon/actions/reports';
|
|||||||
import { Dropdown } from '@/mastodon/components/dropdown_menu';
|
import { Dropdown } from '@/mastodon/components/dropdown_menu';
|
||||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||||
import { useIdentity } from '@/mastodon/identity_context';
|
import { useIdentity } from '@/mastodon/identity_context';
|
||||||
|
import type { Account } from '@/mastodon/models/account';
|
||||||
import type { MenuItem } from '@/mastodon/models/dropdown_menu';
|
import type { MenuItem } from '@/mastodon/models/dropdown_menu';
|
||||||
|
import type { Relationship } from '@/mastodon/models/relationship';
|
||||||
import {
|
import {
|
||||||
PERMISSION_MANAGE_FEDERATION,
|
PERMISSION_MANAGE_FEDERATION,
|
||||||
PERMISSION_MANAGE_USERS,
|
PERMISSION_MANAGE_USERS,
|
||||||
} from '@/mastodon/permissions';
|
} from '@/mastodon/permissions';
|
||||||
|
import type { AppDispatch } from '@/mastodon/store';
|
||||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||||
|
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
|
||||||
|
import EditIcon from '@/material-icons/400-24px/edit_square.svg?react';
|
||||||
|
import LinkIcon from '@/material-icons/400-24px/link_2.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
|
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
|
||||||
|
import ReportIcon from '@/material-icons/400-24px/report.svg?react';
|
||||||
|
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
||||||
|
|
||||||
import { isRedesignEnabled } from '../common';
|
import { isRedesignEnabled } from '../common';
|
||||||
|
|
||||||
|
export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { signedIn, permissions } = useIdentity();
|
||||||
|
|
||||||
|
const account = useAccount(accountId);
|
||||||
|
const relationship = useAppSelector((state) =>
|
||||||
|
state.relationships.get(accountId),
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const menuItems = useMemo(() => {
|
||||||
|
if (!account) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRedesignEnabled()) {
|
||||||
|
return redesignMenuItems({
|
||||||
|
account,
|
||||||
|
signedIn,
|
||||||
|
permissions,
|
||||||
|
intl,
|
||||||
|
relationship,
|
||||||
|
dispatch,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return currentMenuItems({
|
||||||
|
account,
|
||||||
|
signedIn,
|
||||||
|
permissions,
|
||||||
|
intl,
|
||||||
|
relationship,
|
||||||
|
dispatch,
|
||||||
|
});
|
||||||
|
}, [account, signedIn, permissions, intl, relationship, dispatch]);
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
disabled={menuItems.length === 0}
|
||||||
|
items={menuItems}
|
||||||
|
icon='ellipsis-v'
|
||||||
|
iconComponent={MoreHorizIcon}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MenuItemsParams {
|
||||||
|
account: Account;
|
||||||
|
signedIn: boolean;
|
||||||
|
permissions: number;
|
||||||
|
intl: ReturnType<typeof useIntl>;
|
||||||
|
relationship?: Relationship;
|
||||||
|
dispatch: AppDispatch;
|
||||||
|
}
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
||||||
@ -109,80 +172,78 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
function currentMenuItems({
|
||||||
const intl = useIntl();
|
account,
|
||||||
const { signedIn, permissions } = useIdentity();
|
signedIn,
|
||||||
|
permissions,
|
||||||
|
intl,
|
||||||
|
relationship,
|
||||||
|
dispatch,
|
||||||
|
}: MenuItemsParams): MenuItem[] {
|
||||||
|
const items: MenuItem[] = [];
|
||||||
|
const isRemote = account.acct !== account.username;
|
||||||
|
|
||||||
const account = useAccount(accountId);
|
if (signedIn && !account.suspended) {
|
||||||
const relationship = useAppSelector((state) =>
|
items.push(
|
||||||
state.relationships.get(accountId),
|
{
|
||||||
);
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const menuItems = useMemo(() => {
|
|
||||||
const arr: MenuItem[] = [];
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isRemote = account.acct !== account.username;
|
|
||||||
|
|
||||||
if (signedIn && !account.suspended) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.mention, {
|
text: intl.formatMessage(messages.mention, {
|
||||||
name: account.username,
|
name: account.username,
|
||||||
}),
|
}),
|
||||||
action: () => {
|
action: () => {
|
||||||
dispatch(mentionCompose(account));
|
dispatch(mentionCompose(account));
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
arr.push({
|
{
|
||||||
text: intl.formatMessage(messages.direct, {
|
text: intl.formatMessage(messages.direct, {
|
||||||
name: account.username,
|
name: account.username,
|
||||||
}),
|
}),
|
||||||
action: () => {
|
action: () => {
|
||||||
dispatch(directCompose(account));
|
dispatch(directCompose(account));
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
arr.push(null);
|
null,
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
arr.push({
|
items.push(
|
||||||
|
{
|
||||||
text: intl.formatMessage(messages.openOriginalPage),
|
text: intl.formatMessage(messages.openOriginalPage),
|
||||||
href: account.url,
|
href: account.url,
|
||||||
});
|
},
|
||||||
arr.push(null);
|
null,
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!signedIn) {
|
if (!signedIn) {
|
||||||
return arr;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship?.following) {
|
if (relationship?.following) {
|
||||||
if (!relationship.muting) {
|
// Timeline options
|
||||||
if (relationship.showing_reblogs) {
|
if (!relationship.muting) {
|
||||||
arr.push({
|
if (relationship.showing_reblogs) {
|
||||||
text: intl.formatMessage(messages.hideReblogs, {
|
items.push({
|
||||||
name: account.username,
|
text: intl.formatMessage(messages.hideReblogs, {
|
||||||
}),
|
name: account.username,
|
||||||
action: () => {
|
}),
|
||||||
dispatch(followAccount(account.id, { reblogs: false }));
|
action: () => {
|
||||||
},
|
dispatch(followAccount(account.id, { reblogs: false }));
|
||||||
});
|
},
|
||||||
} else {
|
});
|
||||||
arr.push({
|
} else {
|
||||||
text: intl.formatMessage(messages.showReblogs, {
|
items.push({
|
||||||
name: account.username,
|
text: intl.formatMessage(messages.showReblogs, {
|
||||||
}),
|
name: account.username,
|
||||||
action: () => {
|
}),
|
||||||
dispatch(followAccount(account.id, { reblogs: true }));
|
action: () => {
|
||||||
},
|
dispatch(followAccount(account.id, { reblogs: true }));
|
||||||
});
|
},
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
arr.push({
|
items.push(
|
||||||
|
{
|
||||||
text: intl.formatMessage(messages.languages),
|
text: intl.formatMessage(messages.languages),
|
||||||
action: () => {
|
action: () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -194,13 +255,295 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
arr.push(null);
|
null,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRedesignEnabled()) {
|
items.push(
|
||||||
arr.push({
|
{
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship.endorsed ? messages.unendorse : messages.endorse,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
if (relationship.endorsed) {
|
||||||
|
dispatch(unpinAccount(account.id));
|
||||||
|
} else {
|
||||||
|
dispatch(pinAccount(account.id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.add_or_remove_from_list),
|
||||||
|
action: () => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'LIST_ADDER',
|
||||||
|
modalProps: {
|
||||||
|
accountId: account.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationship?.followed_by) {
|
||||||
|
const handleRemoveFromFollowers = () => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'CONFIRM',
|
||||||
|
modalProps: {
|
||||||
|
title: intl.formatMessage(messages.confirmRemoveFromFollowersTitle),
|
||||||
|
message: intl.formatMessage(
|
||||||
|
messages.confirmRemoveFromFollowersMessage,
|
||||||
|
{ name: <strong>{account.acct}</strong> },
|
||||||
|
),
|
||||||
|
confirm: intl.formatMessage(
|
||||||
|
messages.confirmRemoveFromFollowersButton,
|
||||||
|
),
|
||||||
|
onConfirm: () => {
|
||||||
|
void dispatch(
|
||||||
|
removeAccountFromFollowers({ accountId: account.id }),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.removeFromFollowers, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: handleRemoveFromFollowers,
|
||||||
|
dangerous: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationship?.muting) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.unmute, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(unmuteAccount(account.id));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.mute, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(initMuteModal(account));
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationship?.blocking) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.unblock, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(unblockAccount(account.id));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.block, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(blockAccount(account.id));
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.suspended) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.report, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(initReport(account));
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteDomain = isRemote ? account.acct.split('@')[1] : null;
|
||||||
|
if (remoteDomain) {
|
||||||
|
items.push(null);
|
||||||
|
|
||||||
|
if (relationship?.domain_blocking) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.unblockDomain, {
|
||||||
|
domain: remoteDomain,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(unblockDomain(remoteDomain));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.blockDomain, {
|
||||||
|
domain: remoteDomain,
|
||||||
|
}),
|
||||||
|
action: () => {
|
||||||
|
dispatch(initDomainBlockModal(account));
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS ||
|
||||||
|
(isRemote &&
|
||||||
|
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
||||||
|
PERMISSION_MANAGE_FEDERATION)
|
||||||
|
) {
|
||||||
|
items.push(null);
|
||||||
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.admin_account, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
href: `/admin/accounts/${account.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isRemote &&
|
||||||
|
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
||||||
|
PERMISSION_MANAGE_FEDERATION
|
||||||
|
) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.admin_domain, {
|
||||||
|
domain: remoteDomain,
|
||||||
|
}),
|
||||||
|
href: `/admin/instances/${remoteDomain}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
const redesignMessages = defineMessages({
|
||||||
|
share: { id: 'account.menu.share', defaultMessage: 'Share…' },
|
||||||
|
copy: { id: 'account.menu.copy', defaultMessage: 'Copy link' },
|
||||||
|
copied: {
|
||||||
|
id: 'account.menu.copied',
|
||||||
|
defaultMessage: 'Copied account link to clipboard',
|
||||||
|
},
|
||||||
|
mention: { id: 'account.menu.mention', defaultMessage: 'Mention' },
|
||||||
|
direct: {
|
||||||
|
id: 'account.menu.direct',
|
||||||
|
defaultMessage: 'Privately mention',
|
||||||
|
},
|
||||||
|
mute: { id: 'account.menu.mute', defaultMessage: 'Mute account' },
|
||||||
|
unmute: {
|
||||||
|
id: 'account.menu.unmute',
|
||||||
|
defaultMessage: 'Unmute account',
|
||||||
|
},
|
||||||
|
block: { id: 'account.menu.block', defaultMessage: 'Block account' },
|
||||||
|
unblock: {
|
||||||
|
id: 'account.menu.unblock',
|
||||||
|
defaultMessage: 'Unblock account',
|
||||||
|
},
|
||||||
|
domainBlock: {
|
||||||
|
id: 'account.menu.block_domain',
|
||||||
|
defaultMessage: 'Block {domain}',
|
||||||
|
},
|
||||||
|
domainUnblock: {
|
||||||
|
id: 'account.menu.unblock_domain',
|
||||||
|
defaultMessage: 'Unblock {domain}',
|
||||||
|
},
|
||||||
|
report: { id: 'account.menu.report', defaultMessage: 'Report account' },
|
||||||
|
hideReblogs: {
|
||||||
|
id: 'account.menu.hide_reblogs',
|
||||||
|
defaultMessage: 'Hide boosts in timeline',
|
||||||
|
},
|
||||||
|
showReblogs: {
|
||||||
|
id: 'account.menu.show_reblogs',
|
||||||
|
defaultMessage: 'Show boosts in timeline',
|
||||||
|
},
|
||||||
|
addToList: {
|
||||||
|
id: 'account.menu.add_to_list',
|
||||||
|
defaultMessage: 'Add to list…',
|
||||||
|
},
|
||||||
|
openOriginalPage: {
|
||||||
|
id: 'account.menu.open_original_page',
|
||||||
|
defaultMessage: 'View on {domain}',
|
||||||
|
},
|
||||||
|
removeFollower: {
|
||||||
|
id: 'account.menu.remove_follower',
|
||||||
|
defaultMessage: 'Remove follower',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function redesignMenuItems({
|
||||||
|
account,
|
||||||
|
signedIn,
|
||||||
|
permissions,
|
||||||
|
intl,
|
||||||
|
relationship,
|
||||||
|
dispatch,
|
||||||
|
}: MenuItemsParams): MenuItem[] {
|
||||||
|
const items: MenuItem[] = [];
|
||||||
|
const isRemote = account.acct !== account.username;
|
||||||
|
const remoteDomain = isRemote ? account.acct.split('@')[1] : null;
|
||||||
|
|
||||||
|
// Share and copy link options
|
||||||
|
if (account.url) {
|
||||||
|
if ('share' in navigator) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(redesignMessages.share),
|
||||||
|
action: () => {
|
||||||
|
void navigator.share({
|
||||||
|
url: account.url,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: ShareIcon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(redesignMessages.copy),
|
||||||
|
action: () => {
|
||||||
|
void navigator.clipboard.writeText(account.url);
|
||||||
|
dispatch(showAlert({ message: redesignMessages.copied }));
|
||||||
|
},
|
||||||
|
icon: LinkIcon,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mention and direct message options
|
||||||
|
if (signedIn && !account.suspended) {
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(redesignMessages.mention),
|
||||||
|
action: () => {
|
||||||
|
dispatch(mentionCompose(account));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(redesignMessages.direct),
|
||||||
|
action: () => {
|
||||||
|
dispatch(directCompose(account));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
{
|
||||||
text: intl.formatMessage(
|
text: intl.formatMessage(
|
||||||
relationship?.note ? messages.editNote : messages.addNote,
|
relationship?.note ? messages.editNote : messages.addNote,
|
||||||
),
|
),
|
||||||
@ -214,27 +557,34 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
icon: EditIcon,
|
||||||
if (!relationship?.following) {
|
},
|
||||||
arr.push(null);
|
null,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship?.following) {
|
// Open on remote page.
|
||||||
arr.push({
|
if (isRemote) {
|
||||||
text: intl.formatMessage(
|
items.push(
|
||||||
relationship.endorsed ? messages.unendorse : messages.endorse,
|
{
|
||||||
),
|
text: intl.formatMessage(redesignMessages.openOriginalPage, {
|
||||||
action: () => {
|
domain: remoteDomain,
|
||||||
if (relationship.endorsed) {
|
}),
|
||||||
dispatch(unpinAccount(account.id));
|
href: account.url,
|
||||||
} else {
|
},
|
||||||
dispatch(pinAccount(account.id));
|
null,
|
||||||
}
|
);
|
||||||
},
|
}
|
||||||
});
|
|
||||||
arr.push({
|
if (!signedIn) {
|
||||||
text: intl.formatMessage(messages.add_or_remove_from_list),
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List and featuring options
|
||||||
|
if (relationship?.following) {
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(redesignMessages.addToList),
|
||||||
action: () => {
|
action: () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
@ -245,12 +595,76 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
arr.push(null);
|
{
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship.endorsed ? messages.unendorse : messages.endorse,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
if (relationship.endorsed) {
|
||||||
|
dispatch(unpinAccount(account.id));
|
||||||
|
} else {
|
||||||
|
dispatch(pinAccount(account.id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Timeline options
|
||||||
|
if (!relationship.muting) {
|
||||||
|
items.push(
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship.showing_reblogs
|
||||||
|
? redesignMessages.hideReblogs
|
||||||
|
: redesignMessages.showReblogs,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
dispatch(
|
||||||
|
followAccount(account.id, {
|
||||||
|
reblogs: !relationship.showing_reblogs,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.languages),
|
||||||
|
action: () => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'SUBSCRIBED_LANGUAGES',
|
||||||
|
modalProps: {
|
||||||
|
accountId: account.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship?.followed_by) {
|
items.push(
|
||||||
const handleRemoveFromFollowers = () => {
|
{
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship.muting ? redesignMessages.unmute : redesignMessages.mute,
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
if (relationship.muting) {
|
||||||
|
dispatch(unmuteAccount(account.id));
|
||||||
|
} else {
|
||||||
|
dispatch(initMuteModal(account));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationship?.followed_by) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(redesignMessages.removeFollower),
|
||||||
|
action: () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'CONFIRM',
|
modalType: 'CONFIRM',
|
||||||
@ -273,134 +687,91 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
dangerous: true,
|
||||||
|
icon: PersonRemoveIcon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
arr.push({
|
items.push({
|
||||||
text: intl.formatMessage(messages.removeFromFollowers, {
|
text: intl.formatMessage(
|
||||||
name: account.username,
|
relationship?.blocking
|
||||||
}),
|
? redesignMessages.unblock
|
||||||
action: handleRemoveFromFollowers,
|
: redesignMessages.block,
|
||||||
dangerous: true,
|
),
|
||||||
});
|
action: () => {
|
||||||
}
|
if (relationship?.blocking) {
|
||||||
|
dispatch(unblockAccount(account.id));
|
||||||
if (relationship?.muting) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.unmute, {
|
|
||||||
name: account.username,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(unmuteAccount(account.id));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.mute, {
|
|
||||||
name: account.username,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(initMuteModal(account));
|
|
||||||
},
|
|
||||||
dangerous: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (relationship?.blocking) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.unblock, {
|
|
||||||
name: account.username,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(unblockAccount(account.id));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.block, {
|
|
||||||
name: account.username,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(blockAccount(account.id));
|
|
||||||
},
|
|
||||||
dangerous: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!account.suspended) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.report, {
|
|
||||||
name: account.username,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(initReport(account));
|
|
||||||
},
|
|
||||||
dangerous: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const remoteDomain = isRemote ? account.acct.split('@')[1] : null;
|
|
||||||
if (remoteDomain) {
|
|
||||||
arr.push(null);
|
|
||||||
|
|
||||||
if (relationship?.domain_blocking) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.unblockDomain, {
|
|
||||||
domain: remoteDomain,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(unblockDomain(remoteDomain));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
arr.push({
|
dispatch(blockAccount(account.id));
|
||||||
text: intl.formatMessage(messages.blockDomain, {
|
|
||||||
domain: remoteDomain,
|
|
||||||
}),
|
|
||||||
action: () => {
|
|
||||||
dispatch(initDomainBlockModal(account));
|
|
||||||
},
|
|
||||||
dangerous: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
dangerous: true,
|
||||||
|
icon: BlockIcon,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!account.suspended) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(redesignMessages.report),
|
||||||
|
action: () => {
|
||||||
|
dispatch(initReport(account));
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
icon: ReportIcon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteDomain) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(
|
||||||
|
relationship?.domain_blocking
|
||||||
|
? redesignMessages.domainUnblock
|
||||||
|
: redesignMessages.domainBlock,
|
||||||
|
{
|
||||||
|
domain: remoteDomain,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
if (relationship?.domain_blocking) {
|
||||||
|
dispatch(unblockDomain(remoteDomain));
|
||||||
|
} else {
|
||||||
|
dispatch(initDomainBlockModal(account));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dangerous: true,
|
||||||
|
icon: BlockIcon,
|
||||||
|
iconId: 'domain-block',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS ||
|
||||||
|
(isRemote &&
|
||||||
|
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
||||||
|
PERMISSION_MANAGE_FEDERATION)
|
||||||
|
) {
|
||||||
|
items.push(null);
|
||||||
|
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||||
|
items.push({
|
||||||
|
text: intl.formatMessage(messages.admin_account, {
|
||||||
|
name: account.username,
|
||||||
|
}),
|
||||||
|
href: `/admin/accounts/${account.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS ||
|
remoteDomain &&
|
||||||
(isRemote &&
|
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
||||||
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
PERMISSION_MANAGE_FEDERATION
|
||||||
PERMISSION_MANAGE_FEDERATION)
|
|
||||||
) {
|
) {
|
||||||
arr.push(null);
|
items.push({
|
||||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
text: intl.formatMessage(messages.admin_domain, {
|
||||||
arr.push({
|
domain: remoteDomain,
|
||||||
text: intl.formatMessage(messages.admin_account, {
|
}),
|
||||||
name: account.username,
|
href: `/admin/instances/${remoteDomain}`,
|
||||||
}),
|
});
|
||||||
href: `/admin/accounts/${account.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
isRemote &&
|
|
||||||
(permissions & PERMISSION_MANAGE_FEDERATION) ===
|
|
||||||
PERMISSION_MANAGE_FEDERATION
|
|
||||||
) {
|
|
||||||
arr.push({
|
|
||||||
text: intl.formatMessage(messages.admin_domain, {
|
|
||||||
domain: remoteDomain,
|
|
||||||
}),
|
|
||||||
href: `/admin/instances/${remoteDomain}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return arr;
|
return items;
|
||||||
}, [account, signedIn, permissions, intl, relationship, dispatch]);
|
}
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
disabled={menuItems.length === 0}
|
|
||||||
items={menuItems}
|
|
||||||
icon='ellipsis-v'
|
|
||||||
iconComponent={MoreHorizIcon}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
.barWrapper {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.nameWrapper {
|
.nameWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@ -45,6 +49,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$button-breakpoint: 420px;
|
||||||
|
$button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
||||||
|
|
||||||
|
.buttonsDesktop {
|
||||||
|
@container (width < #{$button-breakpoint}) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (not (container-type: inline-size)) {
|
||||||
|
@media (max-width: #{$button-fallback-breakpoint}) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonsMobile {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 55px; // Height of bottom nav bar.
|
||||||
|
|
||||||
|
@container (width >= #{$button-breakpoint}) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (not (container-type: inline-size)) {
|
||||||
|
@media (min-width: (#{$button-fallback-breakpoint} + 1px)) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonsMobileIsStuck {
|
||||||
|
padding: 12px 16px;
|
||||||
|
background-color: var(--color-bg-primary);
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
margin: 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
background-color: var(--color-bg-secondary);
|
background-color: var(--color-bg-secondary);
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@ -21,11 +21,8 @@
|
|||||||
"account.badges.blocked": "Blocked",
|
"account.badges.blocked": "Blocked",
|
||||||
"account.badges.bot": "Automated",
|
"account.badges.bot": "Automated",
|
||||||
"account.badges.domain_blocked": "Blocked domain",
|
"account.badges.domain_blocked": "Blocked domain",
|
||||||
"account.badges.follows_you": "Follows you",
|
|
||||||
"account.badges.group": "Group",
|
"account.badges.group": "Group",
|
||||||
"account.badges.muted": "Muted",
|
"account.badges.muted": "Muted",
|
||||||
"account.badges.mutuals": "You follow each other",
|
|
||||||
"account.badges.requested_to_follow": "Requested to follow you",
|
|
||||||
"account.block": "Block @{name}",
|
"account.block": "Block @{name}",
|
||||||
"account.block_domain": "Block domain {domain}",
|
"account.block_domain": "Block domain {domain}",
|
||||||
"account.block_short": "Block",
|
"account.block_short": "Block",
|
||||||
@ -80,6 +77,23 @@
|
|||||||
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
|
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
|
||||||
"account.media": "Media",
|
"account.media": "Media",
|
||||||
"account.mention": "Mention @{name}",
|
"account.mention": "Mention @{name}",
|
||||||
|
"account.menu.add_to_list": "Add to list…",
|
||||||
|
"account.menu.block": "Block account",
|
||||||
|
"account.menu.block_domain": "Block {domain}",
|
||||||
|
"account.menu.copied": "Copied account link to clipboard",
|
||||||
|
"account.menu.copy": "Copy link",
|
||||||
|
"account.menu.direct": "Privately mention",
|
||||||
|
"account.menu.hide_reblogs": "Hide boosts in timeline",
|
||||||
|
"account.menu.mention": "Mention",
|
||||||
|
"account.menu.mute": "Mute account",
|
||||||
|
"account.menu.open_original_page": "View on {domain}",
|
||||||
|
"account.menu.remove_follower": "Remove follower",
|
||||||
|
"account.menu.report": "Report account",
|
||||||
|
"account.menu.share": "Share…",
|
||||||
|
"account.menu.show_reblogs": "Show boosts in timeline",
|
||||||
|
"account.menu.unblock": "Unblock account",
|
||||||
|
"account.menu.unblock_domain": "Unblock {domain}",
|
||||||
|
"account.menu.unmute": "Unmute account",
|
||||||
"account.moved_to": "{name} has indicated that their new account is now:",
|
"account.moved_to": "{name} has indicated that their new account is now:",
|
||||||
"account.mute": "Mute @{name}",
|
"account.mute": "Mute @{name}",
|
||||||
"account.mute_notifications_short": "Mute notifications",
|
"account.mute_notifications_short": "Mute notifications",
|
||||||
|
|||||||
@ -6,6 +6,7 @@ interface BaseMenuItem {
|
|||||||
text: string;
|
text: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
icon?: IconProp;
|
icon?: IconProp;
|
||||||
|
iconId?: string;
|
||||||
highlighted?: boolean;
|
highlighted?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
dangerous?: boolean;
|
dangerous?: boolean;
|
||||||
|
|||||||
1
app/javascript/material-icons/400-24px/link_2-fill.svg
Normal file
1
app/javascript/material-icons/400-24px/link_2-fill.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M318-120q-82 0-140-58t-58-140q0-40 15-76t43-64l134-133 56 56-134 134q-17 17-25.5 38.5T200-318q0 49 34.5 83.5T318-200q23 0 45-8.5t39-25.5l133-134 57 57-134 133q-28 28-64 43t-76 15Zm79-220-57-57 223-223 57 57-223 223Zm251-28-56-57 134-133q17-17 25-38t8-44q0-50-34-85t-84-35q-23 0-44.5 8.5T558-726L425-592l-57-56 134-134q28-28 64-43t76-15q82 0 139.5 58T839-641q0 39-14.5 75T782-502L648-368Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 493 B |
1
app/javascript/material-icons/400-24px/link_2.svg
Normal file
1
app/javascript/material-icons/400-24px/link_2.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M318-120q-82 0-140-58t-58-140q0-40 15-76t43-64l134-133 56 56-134 134q-17 17-25.5 38.5T200-318q0 49 34.5 83.5T318-200q23 0 45-8.5t39-25.5l133-134 57 57-134 133q-28 28-64 43t-76 15Zm79-220-57-57 223-223 57 57-223 223Zm251-28-56-57 134-133q17-17 25-38t8-44q0-50-34-85t-84-35q-23 0-44.5 8.5T558-726L425-592l-57-56 134-134q28-28 64-43t76-15q82 0 139.5 58T839-641q0 39-14.5 75T782-502L648-368Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 493 B |
@ -8248,7 +8248,6 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.account__header {
|
.account__header {
|
||||||
overflow: hidden;
|
|
||||||
container: account-header / inline-size;
|
container: account-header / inline-size;
|
||||||
|
|
||||||
&.inactive {
|
&.inactive {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user