Status redesign: Replace icons for status actions (#40655)

This commit is contained in:
Echo
2026-09-23 09:03:40 +00:00
committed by GitHub
parent 0d41325719
commit 224e68dd39
23 changed files with 361 additions and 284 deletions

View File

@@ -1,18 +1,10 @@
import { useCallback } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import {
ArrowsClockwiseIcon,
BookmarkSimpleIcon,
ChatCircleIcon,
DotsThreeIcon,
HeartIcon,
QuotesIcon,
ShareFatIcon,
} from '@phosphor-icons/react';
import { DotsThreeIcon, ShareFatIcon } from '@phosphor-icons/react';
import { statusInteraction } from '@/mastodon/actions/interactions';
import { fetchStatus } from '@/mastodon/actions/statuses';
@@ -20,7 +12,6 @@ import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId';
import { useAccountStatus } from '@/mastodon/hooks/useStatus';
import { quickBoosting } from '@/mastodon/initial_state';
import type { AccountStatusShape } from '@/mastodon/models/status';
import { selectStatusConditions } from '@/mastodon/selectors/statuses';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import {
@@ -29,7 +20,6 @@ import {
ToggleButton,
ToggleIconButton,
} from '../button/redesign';
import { iconWeight, useIconWeight } from '../icon';
import {
Menu,
MenuItem,
@@ -38,8 +28,11 @@ import {
LegacyDropdownMenuItems,
} from '../menu';
import { boostItemState, quoteItemState } from './boost_button_utils';
import { useStatusContext, useStatusMenuActions } from './hooks';
import {
useStatusContext,
useStatusIcons,
useStatusMenuActions,
} from './hooks';
import { RemoveQuoteHint } from './legacy/action_bar/remove_quote_hint';
import classes from './styles.module.scss';
@@ -51,15 +44,6 @@ interface StatusActionBarProps {
onlyResponses?: boolean;
}
const messages = defineMessages({
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
favourite: { id: 'status.like', defaultMessage: 'Like' },
removeFavourite: {
id: 'status.unlike',
defaultMessage: 'Unlike',
},
});
export const StatusActionBar: React.FC<StatusActionBarProps> = ({
statusId,
withDismiss,
@@ -77,12 +61,6 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
// Actions
const dispatch = useAppDispatch();
const handleReplyClick = useCallback(() => {
dispatch(statusInteraction({ statusId, intent: 'reply', contextType }));
}, [contextType, dispatch, statusId]);
const handleFavouriteClick = useCallback(() => {
dispatch(statusInteraction({ statusId, intent: 'favourite', contextType }));
}, [contextType, dispatch, statusId]);
const handleShareClick = useCallback(() => {
if (!statusUrl) {
return;
@@ -99,17 +77,8 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
dispatch(statusInteraction({ statusId, intent: 'copy', contextType }));
}
}, [contextType, dispatch, statusId, statusUrl]);
const handleBookmarkClick = useCallback(() => {
dispatch(statusInteraction({ statusId, intent: 'bookmark', contextType }));
}, [contextType, dispatch, statusId]);
const intl = useIntl();
const favouriteIcon = useIconWeight(HeartIcon, status?.favourited && 'fill');
const bookmarkIcon = useIconWeight(
BookmarkSimpleIcon,
status?.bookmarked && 'fill',
);
const { reply, like, bookmark } = useStatusIcons(statusId);
if (!status) {
return null;
@@ -118,10 +87,6 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
const isPublic =
status.visibility === 'public' || status.visibility === 'unlisted';
const favouriteTitle = intl.formatMessage(
status.favourited ? messages.removeFavourite : messages.favourite,
);
const isQuotingMe = quotedAccountId === currentAccountId;
const shouldShowQuoteRemovalHint =
isQuotingMe && contextType === 'notifications';
@@ -132,11 +97,11 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
size='sm'
clipPadding
variant='ghost'
title={intl.formatMessage(messages.replyAll)}
leadingIcon={ChatCircleIcon}
onClick={handleReplyClick}
title={reply.title}
leadingIcon={reply.icon}
onClick={reply.action}
>
{withCounters && status.replies_count}
{withCounters && reply.counter}
</Button>
<StatusReblogButton statusId={statusId}>
@@ -146,13 +111,13 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
<ToggleButton
size='sm'
variant='ghost'
active={status.favourited}
title={favouriteTitle}
leadingIcon={favouriteIcon}
onClick={handleFavouriteClick}
active={like.active}
title={like.title}
leadingIcon={like.icon}
onClick={like.action}
className={classNames(!onlyResponses && classes.actionsButtonGap)}
>
{withCounters && status.favourites_count}
{withCounters && like.counter}
</ToggleButton>
</>
);
@@ -179,18 +144,12 @@ export const StatusActionBar: React.FC<StatusActionBarProps> = ({
<ToggleIconButton
size='sm'
variant='ghost'
active={status.bookmarked}
icon={bookmarkIcon}
onClick={handleBookmarkClick}
active={bookmark.active}
title={bookmark.title}
icon={bookmark.icon}
onClick={bookmark.action}
>
{!status.bookmarked ? (
<FormattedMessage id='status.save' defaultMessage='Save' />
) : (
<FormattedMessage
id='status.remove_from_saved'
defaultMessage='Remove from Saved'
/>
)}
{bookmark.title}
</ToggleIconButton>
<RemoveQuoteHint
@@ -213,32 +172,18 @@ const StatusReblogButton: React.FC<{
statusId: string;
children: React.ReactNode;
}> = ({ statusId, children }) => {
const conditions = useAppSelector((state) =>
selectStatusConditions(state, statusId),
);
const { isBoosted } = conditions;
const boostState = boostItemState(conditions);
const quoteState = quoteItemState(conditions);
const intl = useIntl();
const dispatch = useAppDispatch();
const onReblog = useCallback(() => {
dispatch(statusInteraction({ statusId, intent: 'reblog' }));
}, [dispatch, statusId]);
const onQuote = useCallback(() => {
dispatch(statusInteraction({ statusId, intent: 'quote' }));
}, [dispatch, statusId]);
const { boost, quote } = useStatusIcons(statusId);
if (quickBoosting) {
return (
<ToggleButton
size='sm'
variant='ghost'
active={isBoosted}
leadingIcon={ArrowsClockwiseIcon}
onClick={onReblog}
disabled={boostState.disabled}
active={boost.active}
title={boost.title}
leadingIcon={boost.icon}
disabled={boost.disabled}
onClick={boost.action}
>
{children}
</ToggleButton>
@@ -251,36 +196,35 @@ const StatusReblogButton: React.FC<{
as={ToggleButton}
size='sm'
variant='ghost'
active={isBoosted}
leadingIcon={ArrowsClockwiseIcon}
active={boost.active}
title={boost.title}
leadingIcon={boost.icon}
>
{children}
</MenuTrigger>
<MenuList placement='bottom' maxWidth={180}>
<MenuItem
onClick={onReblog}
icon={ArrowsClockwiseIcon}
disabled={boostState.disabled}
description={boostState.meta && intl.formatMessage(boostState.meta)}
onClick={boost.action}
icon={boost.icon}
disabled={boost.disabled}
description={boost.meta}
>
{intl.formatMessage(boostState.title)}
{boost.title}
</MenuItem>
<MenuItem
onClick={onQuote}
icon={QuotesFilledIcon}
disabled={quoteState.disabled}
description={quoteState.meta && intl.formatMessage(quoteState.meta)}
onClick={quote.action}
icon={quote.icon}
disabled={quote.disabled}
description={quote.meta}
>
{intl.formatMessage(quoteState.title)}
{quote.title}
</MenuItem>
</MenuList>
</Menu>
);
};
const QuotesFilledIcon = iconWeight(QuotesIcon, 'fill');
const StatusActionMenu: React.FC<{
dismissQuoteHint: () => void;
status: AccountStatusShape;

View File

@@ -2,16 +2,20 @@ import { defineMessages } from 'react-intl';
import type { MessageDescriptor } from 'react-intl';
import type { StatusConditions } from '@/mastodon/selectors/statuses';
import FormatQuote from '@/material-icons/400-24px/format_quote-fill.svg?react';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import FormatQuoteOff from '@/material-icons/400-24px/format_quote_off-fill.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import type { IconProp } from '../icon';
import {
StatusBoostActiveIcon,
StatusBoostIcon,
StatusQuoteIcon,
} from './icons';
export const messages = defineMessages({
all_disabled: {
id: 'status.all_disabled',
@@ -76,20 +80,27 @@ export function boostItemState({
if (isBoosted) {
return {
title: messages.reblog_cancel,
iconComponent: isPublic ? RepeatActiveIcon : RepeatPrivateActiveIcon,
iconComponent:
isPublic || isRedesignEnabled()
? StatusBoostActiveIcon
: RepeatPrivateActiveIcon,
};
}
const iconText: MenuItemState = {
title: messages.reblog,
iconComponent: RepeatIcon,
iconComponent: StatusBoostIcon,
};
if (isPrivateReblog) {
iconText.meta = messages.reblog_private;
iconText.iconComponent = RepeatPrivateIcon;
if (!isRedesignEnabled()) {
iconText.iconComponent = RepeatPrivateIcon;
}
} else if (!isPublic) {
iconText.meta = messages.reblog_cannot;
iconText.iconComponent = RepeatDisabledIcon;
if (!isRedesignEnabled()) {
iconText.iconComponent = RepeatDisabledIcon;
}
iconText.disabled = true;
}
return iconText;
@@ -105,12 +116,14 @@ export function quoteItemState({
}: StatusConditions): MenuItemState {
const iconText: MenuItemState = {
title: messages.quote,
iconComponent: FormatQuote,
iconComponent: StatusQuoteIcon,
};
if (!isPublic && !isMine) {
iconText.disabled = true;
iconText.iconComponent = FormatQuoteOff;
if (!isRedesignEnabled()) {
iconText.iconComponent = FormatQuoteOff;
}
iconText.meta = messages.quote_private;
} else if (isQuoteAutomaticallyAccepted) {
iconText.title = messages.quote;
@@ -120,7 +133,9 @@ export function quoteItemState({
// We don't show the disabled state when logged out
} else if (isLoggedIn) {
iconText.disabled = true;
iconText.iconComponent = FormatQuoteOff;
if (!isRedesignEnabled()) {
iconText.iconComponent = FormatQuoteOff;
}
iconText.meta = isQuoteFollowersOnly
? messages.quote_followers_only
: messages.quote_cannot;

View File

@@ -25,7 +25,7 @@ import { statusInteraction } from '@/mastodon/actions/interactions_typed';
import { openModal } from '@/mastodon/actions/modal';
import { toggleStatusSpoilers } from '@/mastodon/actions/statuses';
import { useRelationship } from '@/mastodon/hooks/useRelationship';
import { useExpandedStatus } from '@/mastodon/hooks/useStatus';
import { useExpandedStatus, useStatus } from '@/mastodon/hooks/useStatus';
import { useToggle } from '@/mastodon/hooks/useToggle';
import { useIdentity } from '@/mastodon/identity_context';
import { quickBoosting } from '@/mastodon/initial_state';
@@ -56,8 +56,18 @@ import type { OnElementHandler } from '@/mastodon/utils/html';
import { FOCUS_TARGET } from '../navigation_focus_target';
import { quoteItemState } from './boost_button_utils';
import { boostItemState, quoteItemState } from './boost_button_utils';
import { useElementHandledLink } from './handled_link';
import {
StatusBookmarkActiveIcon,
StatusBookmarkIcon,
StatusBoostActiveIcon,
StatusBoostIcon,
StatusLikeActiveIcon,
StatusLikeIcon,
StatusReplyAllIcon,
StatusReplyIcon,
} from './icons';
import type { StatusContextType } from './types';
export const StatusContext = createContext<{
@@ -69,6 +79,23 @@ export function useStatusContext() {
return use(StatusContext);
}
export function useStatusInteractionFactory(
statusId?: string,
contextTypeArg?: StatusContextType,
) {
const statusContext = useStatusContext();
const contextType = contextTypeArg ?? statusContext.contextType;
const dispatch = useAppDispatch();
return useCallback(
(intent: StatusInteractionIntent) => {
return () => {
dispatch(statusInteraction({ statusId, intent, contextType }));
};
},
[contextType, dispatch, statusId],
);
}
export function useStatusHandlers({
status,
contextType,
@@ -101,14 +128,7 @@ export function useStatusHandlers({
}, [dispatch, filterAction, onFilterToggle, showDespiteFilter, status]);
// Interaction handlers
const handlerFactory = useCallback(
(intent: StatusInteractionIntent) => {
return () => {
dispatch(statusInteraction({ statusId, intent, contextType }));
};
},
[contextType, dispatch, statusId],
);
const handlerFactory = useStatusInteractionFactory(statusId, contextType);
const accountId = status?.account.id;
const onMention = useCallback(() => {
@@ -238,6 +258,120 @@ export function useStatusHandlers({
}
export type StatusHandlers = ReturnType<typeof useStatusHandlers>;
interface StatusIcon {
icon: React.FC<React.SVGProps<SVGSVGElement>>;
title: string;
meta?: string;
counter?: number;
active?: boolean;
action: () => void;
disabled: boolean;
}
const iconMessages = defineMessages({
reply: { id: 'status.reply', defaultMessage: 'Reply' },
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
removeFavourite: {
id: 'status.remove_favourite',
defaultMessage: 'Remove from favorites',
},
like: { id: 'status.like', defaultMessage: 'Like' },
removeLike: {
id: 'status.unlike',
defaultMessage: 'Unlike',
},
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
removeBookmark: {
id: 'status.remove_bookmark',
defaultMessage: 'Remove bookmark',
},
});
export function useStatusIcons(statusId: string) {
const intl = useIntl();
const conditions = useAppSelector((state) =>
selectStatusConditions(state, statusId),
);
const status = useStatus(statusId);
const interactions = useAppSelector((state) =>
selectStatusInteractionsAllowed(state, statusId),
);
const interactionFactory = useStatusInteractionFactory(statusId);
const isRedesign = isRedesignEnabled();
const isReplyAll = !!status?.in_reply_to_id;
const reply: StatusIcon = {
icon: isReplyAll ? StatusReplyAllIcon : StatusReplyIcon,
title: intl.formatMessage(
isReplyAll ? iconMessages.replyAll : iconMessages.reply,
),
counter: status?.replies_count,
action: interactionFactory('reply'),
disabled: interactions.reply,
};
const boostState = boostItemState(conditions);
const boost: StatusIcon = {
icon: boostState.iconComponent,
title: intl.formatMessage(boostState.title),
meta: boostState.meta ? intl.formatMessage(boostState.meta) : undefined,
counter: status?.reblogs_count,
active: status?.reblogged ?? false,
action: interactionFactory('reblog'),
disabled: boostState.disabled ?? false,
};
if (isRedesign) {
boost.icon = status?.reblogged ? StatusBoostActiveIcon : StatusBoostIcon;
}
const quoteState = quoteItemState(conditions);
const quote: StatusIcon = {
icon: quoteState.iconComponent,
title: intl.formatMessage(quoteState.title),
meta: quoteState.meta ? intl.formatMessage(quoteState.meta) : undefined,
counter: status?.quotes_count,
action: interactionFactory('quote'),
disabled: quoteState.disabled ?? false,
};
const isLiked = !!status?.favourited;
const like: StatusIcon = {
icon: isLiked ? StatusLikeActiveIcon : StatusLikeIcon,
title: intl.formatMessage(
isLiked ? iconMessages.removeFavourite : iconMessages.favourite,
),
counter: status?.favourites_count ?? 0,
active: isLiked,
action: interactionFactory('favourite'),
disabled: interactions.favourite,
};
if (isRedesign) {
like.title = intl.formatMessage(
isLiked ? iconMessages.removeLike : iconMessages.like,
);
}
const isBookmarked = !!status?.bookmarked;
const bookmark: StatusIcon = {
icon: isBookmarked ? StatusBookmarkActiveIcon : StatusBookmarkIcon,
title: intl.formatMessage(
isBookmarked ? iconMessages.removeBookmark : iconMessages.bookmark,
),
active: isBookmarked,
action: interactionFactory('bookmark'),
disabled: interactions.bookmark,
};
return {
reply,
boost,
quote,
like,
bookmark,
} as const;
}
const screenReaderMessages = defineMessages({
quote_noun: {
id: 'status.quote_noun',
@@ -436,16 +570,7 @@ export function useStatusMenuActions({
const interactions = useAppSelector((state) =>
selectStatusInteractionsAllowed(state, status.id),
);
const statusInteractionFactory = useCallback(
(intent: StatusInteractionIntent) => {
return () => {
dispatch(
statusInteraction({ statusId: status.id, contextType, intent }),
);
};
},
[contextType, dispatch, status.id],
);
const statusInteractionFactory = useStatusInteractionFactory(status.id);
return useMemo(
() =>

View File

@@ -0,0 +1,49 @@
import {
ArrowsClockwiseIcon,
BookmarkSimpleIcon,
ChatCircleIcon,
HeartIcon,
QuotesIcon,
} from '@phosphor-icons/react';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import BookmarkLegacyIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
import BookmarkBorderLegacyIcon from '@/material-icons/400-24px/bookmark.svg?react';
import QuoteLegacyIcon from '@/material-icons/400-24px/format_quote-fill.svg?react';
import BoostLegacyIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyLegacyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllLegacyIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarLegacyIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderLegacyIcon from '@/material-icons/400-24px/star.svg?react';
import BoostActiveIcon from '@/svg-icons/boost_active.svg?react';
import BoostActiveLegacyIcon from '@/svg-icons/repeat_active.svg?react';
import { iconWeight } from '../icon';
export const StatusReplyIcon = isRedesignEnabled()
? ChatCircleIcon
: ReplyLegacyIcon;
export const StatusReplyAllIcon = isRedesignEnabled()
? StatusReplyIcon
: ReplyAllLegacyIcon;
export const StatusQuoteIcon = isRedesignEnabled()
? QuoteLegacyIcon
: QuotesIcon;
export const StatusBoostIcon = isRedesignEnabled()
? ArrowsClockwiseIcon
: BoostLegacyIcon;
export const StatusBoostActiveIcon = isRedesignEnabled()
? BoostActiveIcon
: BoostActiveLegacyIcon;
export const StatusLikeIcon = isRedesignEnabled()
? HeartIcon
: StarBorderLegacyIcon;
export const StatusLikeActiveIcon = isRedesignEnabled()
? iconWeight(HeartIcon, 'fill')
: StarLegacyIcon;
export const StatusBookmarkIcon = isRedesignEnabled()
? BookmarkSimpleIcon
: BookmarkBorderLegacyIcon;
export const StatusBookmarkActiveIcon = isRedesignEnabled()
? iconWeight(BookmarkSimpleIcon, 'fill')
: BookmarkLegacyIcon;

View File

@@ -8,13 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { ImmutablePureComponent } from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@@ -29,6 +23,7 @@ import { RemoveQuoteHint } from './remove_quote_hint';
import { quoteItemState } from '../../boost_button_utils';
import { selectStatusConditions } from '@/mastodon/selectors/statuses';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import { StatusBookmarkActiveIcon, StatusBookmarkIcon, StatusLikeActiveIcon, StatusLikeIcon, StatusReplyAllIcon, StatusReplyIcon } from '../../icons';
const baseMessages = defineMessages({
@@ -386,11 +381,11 @@ class StatusActionBar extends ImmutablePureComponent {
if (status.get('in_reply_to_id', null) === null) {
replyIcon = 'reply';
replyIconComponent = ReplyIcon;
replyIconComponent = StatusReplyIcon;
replyTitle = intl.formatMessage(messages.reply);
} else {
replyIcon = 'reply-all';
replyIconComponent = ReplyAllIcon;
replyIconComponent = StatusReplyAllIcon;
replyTitle = intl.formatMessage(messages.replyAll);
}
@@ -403,16 +398,16 @@ class StatusActionBar extends ImmutablePureComponent {
return (
<div className='status__action-bar'>
<div className='status__action-bar__button-wrapper'>
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? StatusReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
</div>
<div className='status__action-bar__button-wrapper'>
<BoostButton statusId={status.get('id')} counters={withCounters} />
</div>
<div className='status__action-bar__button-wrapper'>
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StatusLikeActiveIcon : StatusLikeIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
</div>
<div className='status__action-bar__button-wrapper'>
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={bookmarkTitle} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} />
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={bookmarkTitle} icon='bookmark' iconComponent={status.get('bookmarked') ? StatusBookmarkActiveIcon : StatusBookmarkIcon} onClick={this.handleBookmarkClick} />
</div>
<RemoveQuoteHint className='status__action-bar__button-wrapper' canShowHint={shouldShowQuoteRemovalHint}>
{(dismissQuoteHint) => (

View File

@@ -28,6 +28,7 @@ import {
messages,
quoteItemState,
} from '../boost_button_utils';
import { useStatusIcons } from '../hooks';
const StandaloneBoostButton: FC<ReblogButtonProps> = ({
statusId,
@@ -135,6 +136,8 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ statusId, counters }) => {
);
}, [dispatch, status]);
const { boost, quote } = useStatusIcons(statusId);
const items = useMemo(() => {
const boostItem = boostItemState(statusState);
const quoteItem = quoteItemState(statusState);
@@ -144,7 +147,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ statusId, counters }) => {
description: boostItem.meta
? intl.formatMessage(boostItem.meta)
: undefined,
icon: boostItem.iconComponent,
icon: boost.icon,
highlighted: wasBoosted,
disabled: boostItem.disabled,
action: (event) => {
@@ -156,14 +159,22 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ statusId, counters }) => {
description: quoteItem.meta
? intl.formatMessage(quoteItem.meta)
: undefined,
icon: quoteItem.iconComponent,
icon: quote.icon,
disabled: quoteItem.disabled,
action: () => {
dispatch(quoteComposeById(statusId));
},
},
] satisfies [ActionMenuItemWithIcon, ActionMenuItemWithIcon];
}, [dispatch, intl, statusId, statusState, wasBoosted]);
}, [
boost.icon,
dispatch,
intl,
quote.icon,
statusId,
statusState,
wasBoosted,
]);
const boostIcon = items[0].icon;

View File

@@ -8,7 +8,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { ImmutablePureComponent } from 'react-immutable-pure-component';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import { Hotkeys } from '@/mastodon/components/hotkeys';
import { Icon } from '@/mastodon/components/icon';
import { PictureInPicturePlaceholder } from './picture_in_picture_placeholder';
@@ -34,6 +33,7 @@ import { getHashtagBarForStatus } from './hashtag_bar';
import StatusActionBar from './action_bar';
import StatusContent from './content';
import { StatusThreadLabel } from './thread_label';
import { StatusBoostIcon } from '../icons';
const domParser = new DOMParser();
@@ -438,7 +438,7 @@ class Status extends ImmutablePureComponent {
prepend = (
<div className='status__prepend'>
<div className='status__prepend__icon'><Icon id='retweet' icon={RepeatIcon} /></div>
<div className='status__prepend__icon'><Icon id='retweet' icon={StatusBoostIcon} /></div>
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name }} tagName='span' />
</div>
);

View File

@@ -1,10 +1,10 @@
import { FormattedMessage } from 'react-intl';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import { Icon } from 'mastodon/components/icon';
import { useAppSelector } from 'mastodon/store';
import { LinkedDisplayName } from '../../display_name';
import { StatusReplyIcon } from '../icons';
export const StatusThreadLabel: React.FC<{
accountId: string;
@@ -49,7 +49,7 @@ export const StatusThreadLabel: React.FC<{
return (
<div className='status__prepend'>
<div className='status__prepend__icon'>
<Icon id='reply' icon={ReplyIcon} />
<Icon id='reply' icon={StatusReplyIcon} />
</div>
<span>{label}</span>
</div>

View File

@@ -13,6 +13,7 @@ import type {
import { LinkedDisplayName } from '@/mastodon/components/display_name';
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
import { StatusReplyIcon } from '@/mastodon/components/status/icons';
import StatusContent from '@/mastodon/components/status/legacy/content';
import type { Account } from '@/mastodon/models/account';
import type { StatusShape } from '@/mastodon/models/status';
@@ -22,7 +23,6 @@ import {
useAppSelector,
} from '@/mastodon/store';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import { replyCompose } from 'mastodon/actions/compose';
import {
markConversationRead,
@@ -256,7 +256,7 @@ export const Conversation: React.FC<{
className='status__action-bar-button'
title={intl.formatMessage(messages.reply)}
icon='reply'
iconComponent={ReplyIcon}
iconComponent={StatusReplyIcon}
onClick={handleReply}
/>

View File

@@ -10,11 +10,15 @@ import type { Map as ImmutableMap } from 'immutable';
import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import {
StatusBookmarkActiveIcon,
StatusBookmarkIcon,
StatusLikeActiveIcon,
StatusLikeIcon,
} from '@/mastodon/components/status/icons';
import { useAccount } from '@/mastodon/hooks/useAccount';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react';
import CollectionsActiveIcon from '@/material-icons/400-24px/category-fill.svg?react';
import CollectionsIcon from '@/material-icons/400-24px/category.svg?react';
import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react';
@@ -26,8 +30,6 @@ import PersonAddActiveIcon from '@/material-icons/400-24px/person_add-fill.svg?r
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarIcon from '@/material-icons/400-24px/star.svg?react';
import TrendingUpIcon from '@/material-icons/400-24px/trending_up.svg?react';
import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { openNavigation, closeNavigation } from 'mastodon/actions/navigation';
@@ -345,8 +347,8 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
transparent
to='/favourites'
icon='star'
iconComponent={StarIcon}
activeIconComponent={StarActiveIcon}
iconComponent={StatusLikeIcon}
activeIconComponent={StatusLikeActiveIcon}
text={intl.formatMessage(messages.favourites)}
/>
</li>
@@ -355,8 +357,8 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
transparent
to='/bookmarks'
icon='bookmarks'
iconComponent={BookmarksIcon}
activeIconComponent={BookmarksActiveIcon}
iconComponent={StatusBookmarkIcon}
activeIconComponent={StatusBookmarkActiveIcon}
text={intl.formatMessage(messages.bookmarks)}
/>
</li>

View File

@@ -3,20 +3,17 @@ import PropTypes from 'prop-types';
import { FormattedMessage, defineMessages } from 'react-intl';
import classNames from 'classnames';
import { Link, withRouter } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { ImmutablePureComponent } from 'react-immutable-pure-component';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote-fill.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import { Account } from 'mastodon/components/account';
import { LinkedDisplayName } from '@/mastodon/components/display_name';
import { Icon } from 'mastodon/components/icon';
@@ -32,6 +29,7 @@ import { ModerationWarning } from './moderation_warning';
import { RelationshipsSeveranceEvent } from './relationships_severance_event';
import Report from './report';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import { StatusBoostIcon, StatusLikeIcon, StatusQuoteIcon } from '@/mastodon/components/status/icons';
const messages = defineMessages({
favourite: { id: 'notification.favourite', defaultMessage: '{name} favorited your post' },
@@ -190,7 +188,7 @@ class Notification extends ImmutablePureComponent {
<Hotkeys handlers={this.getHandlers()}>
<div className={classNames('notification notification-favourite focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(likeMessage, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<Icon id='star' icon={StarIcon} className='star-icon' />
<Icon id='star' icon={StatusLikeIcon} className='star-icon' />
<span title={notification.get('created_at')}>
<FormattedMessage {...likeMessage} values={{ name: link }} />
@@ -220,7 +218,7 @@ class Notification extends ImmutablePureComponent {
<Hotkeys handlers={this.getHandlers()}>
<div className={classNames('notification notification-reblog focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<Icon id='retweet' icon={RepeatIcon} />
<Icon icon={StatusBoostIcon} />
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your post' values={{ name: link }} />
@@ -250,7 +248,7 @@ class Notification extends ImmutablePureComponent {
<Hotkeys handlers={this.getHandlers()}>
<div className={classNames('notification notification-quote focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.quote, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<Icon id='quote' icon={FormatQuoteIcon} />
<Icon id='quote' icon={StatusQuoteIcon} />
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.label.quote' defaultMessage='{name} quoted your post' values={{ name: link }} />

View File

@@ -2,8 +2,8 @@ import { defineMessages, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { StatusLikeActiveIcon } from '@/mastodon/components/status/icons';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import type { NotificationGroupFavourite } from 'mastodon/models/notification_group';
import { useAppSelector } from 'mastodon/store';
@@ -117,7 +117,7 @@ export const NotificationFavourite: React.FC<{
return (
<NotificationGroupWithStatus
type='favourite'
icon={StarIcon}
icon={StatusLikeActiveIcon}
iconId='star'
accountIds={notification.sampleAccountIds}
statusId={notification.statusId}

View File

@@ -2,8 +2,8 @@ import { FormattedMessage } from 'react-intl';
import { isEqual } from 'lodash';
import { StatusReplyIcon } from '@/mastodon/components/status/icons';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import { me } from 'mastodon/initial_state';
import type { NotificationGroupMention } from 'mastodon/models/notification_group';
import { useAppSelector } from 'mastodon/store';
@@ -59,7 +59,7 @@ export const NotificationMention: React.FC<{
return (
<NotificationWithStatus
type='mention'
icon={isReply ? ReplyIcon : AlternateEmailIcon}
icon={isReply ? StatusReplyIcon : AlternateEmailIcon}
iconId='reply'
accountIds={notification.sampleAccountIds}
count={notification.notifications_count}

View File

@@ -1,6 +1,6 @@
import { FormattedMessage } from 'react-intl';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote-fill.svg?react';
import { StatusQuoteIcon } from '@/mastodon/components/status/icons';
import type { NotificationGroupQuote } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
@@ -21,7 +21,7 @@ export const NotificationQuote: React.FC<{
return (
<NotificationWithStatus
type='quote'
icon={FormatQuoteIcon}
icon={StatusQuoteIcon}
iconId='quote'
accountIds={notification.sampleAccountIds}
count={notification.notifications_count}

View File

@@ -2,7 +2,7 @@ import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import { StatusBoostIcon } from '@/mastodon/components/status/icons';
import type { NotificationGroupReblog } from 'mastodon/models/notification_group';
import { useAppSelector } from 'mastodon/store';
@@ -47,7 +47,7 @@ export const NotificationReblog: React.FC<{
return (
<NotificationGroupWithStatus
type='reblog'
icon={RepeatIcon}
icon={StatusBoostIcon}
iconId='repeat'
accountIds={notification.sampleAccountIds}
statusId={notification.statusId}

View File

@@ -3,13 +3,15 @@ import { useCallback } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import {
StatusBoostIcon,
StatusLikeIcon,
StatusReplyAllIcon,
} from '@/mastodon/components/status/icons';
import CollectionsIcon from '@/material-icons/400-24px/category.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star.svg?react';
import { setNotificationsFilter } from 'mastodon/actions/notification_groups';
import { Icon } from 'mastodon/components/icon';
import {
@@ -87,7 +89,7 @@ export const FilterBar: React.FC = () => {
key='mention'
title={intl.formatMessage(tooltips.mentions)}
>
<Icon id='reply-all' icon={ReplyAllIcon} />
<Icon id='reply-all' icon={StatusReplyAllIcon} />
</BarButton>
<BarButton
selectedFilter={selectedFilter}
@@ -95,7 +97,7 @@ export const FilterBar: React.FC = () => {
key='favourite'
title={intl.formatMessage(tooltips.favourites)}
>
<Icon id='star' icon={StarIcon} />
<Icon id='star' icon={StatusLikeIcon} />
</BarButton>
<BarButton
selectedFilter={selectedFilter}
@@ -103,7 +105,7 @@ export const FilterBar: React.FC = () => {
key='reblog'
title={intl.formatMessage(tooltips.boosts)}
>
<Icon id='retweet' icon={RepeatIcon} />
<Icon id='retweet' icon={StatusBoostIcon} />
</BarButton>
<BarButton
selectedFilter={selectedFilter}

View File

@@ -4,15 +4,10 @@ import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
import { useStatusIcons } from '@/mastodon/components/status/hooks';
import { BoostButton } from '@/mastodon/components/status/legacy/boost_button';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import { replyCompose } from 'mastodon/actions/compose';
import { toggleFavourite } from 'mastodon/actions/interactions';
import { openModal } from 'mastodon/actions/modal';
import { IconButton } from 'mastodon/components/icon_button';
import { useIdentity } from 'mastodon/identity_context';
@@ -25,26 +20,6 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store';
const messages = defineMessages({
reply: { id: 'status.reply', defaultMessage: 'Reply' },
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
reblog_private: {
id: 'status.reblog_private',
defaultMessage: 'Share again with your followers',
},
cancel_reblog_private: {
id: 'status.cancel_reblog_private',
defaultMessage: 'Unboost',
},
cannot_reblog: {
id: 'status.cannot_reblog',
defaultMessage: 'This post cannot be boosted',
},
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
removeFavourite: {
id: 'status.remove_favourite',
defaultMessage: 'Remove from favorites',
},
like: { id: 'status.like', defaultMessage: 'Like' },
unlike: { id: 'status.unlike', defaultMessage: 'Unlike' },
open: { id: 'status.open', defaultMessage: 'Expand this status' },
});
@@ -98,27 +73,6 @@ export const Footer: React.FC<{
}
}, [dispatch, status, signedIn, askReplyConfirmation, onClose]);
const handleFavouriteClick = useCallback(() => {
if (!status) {
return;
}
if (signedIn) {
dispatch(toggleFavourite(status.get('id')));
} else {
dispatch(
openModal({
modalType: 'INTERACTION',
modalProps: {
intent: 'favourite',
accountId: status.getIn(['account', 'id']),
url: status.get('uri'),
},
}),
);
}
}, [dispatch, status, signedIn]);
const handleOpenClick = useCallback(
(e: React.MouseEvent) => {
if (e.button !== 0 || !status) {
@@ -132,50 +86,22 @@ export const Footer: React.FC<{
[history, status, account, onClose],
);
const { reply, like } = useStatusIcons(statusId);
if (!status) {
return null;
}
let replyIcon, replyIconComponent, replyTitle;
if (status.get('in_reply_to_id', null) === null) {
replyIcon = 'reply';
replyIconComponent = ReplyIcon;
replyTitle = intl.formatMessage(messages.reply);
} else {
replyIcon = 'reply-all';
replyIconComponent = ReplyAllIcon;
replyTitle = intl.formatMessage(messages.replyAll);
}
let favouriteTitle = intl.formatMessage(
status.get('favourited') ? messages.removeFavourite : messages.favourite,
);
if (isRedesignEnabled()) {
favouriteTitle = intl.formatMessage(
status.get('favourited') ? messages.unlike : messages.like,
);
}
return (
<div className='picture-in-picture__footer'>
<IconButton
className='status__action-bar-button'
title={replyTitle}
icon={
status.get('in_reply_to_account_id') ===
status.getIn(['account', 'id'])
? 'reply'
: replyIcon
}
iconComponent={
status.get('in_reply_to_account_id') ===
status.getIn(['account', 'id'])
? ReplyIcon
: replyIconComponent
}
title={reply.title}
active={reply.active}
icon='reply'
iconComponent={reply.icon}
onClick={handleReplyClick}
counter={status.get('replies_count') as number}
counter={reply.counter}
/>
<BoostButton counters statusId={statusId} />
@@ -183,12 +109,12 @@ export const Footer: React.FC<{
<IconButton
className='status__action-bar-button star-icon'
animate
active={status.get('favourited') as boolean}
title={favouriteTitle}
active={like.active}
title={like.title}
icon='star'
iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon}
onClick={handleFavouriteClick}
counter={status.get('favourites_count') as number}
iconComponent={like.icon}
onClick={like.action}
counter={like.counter}
/>
{withOpenButton && (

View File

@@ -6,13 +6,7 @@ import { defineMessages } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import { injectIntl } from '@/mastodon/components/intl';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
@@ -24,6 +18,14 @@ import { BoostButton } from '@/mastodon/components/status/legacy/boost_button';
import { quoteItemState } from '@/mastodon/components/status/boost_button_utils';
import { selectStatusConditions } from '@/mastodon/selectors/statuses';
import { isRedesignEnabled } from '@/mastodon/utils/environment';
import {
StatusBookmarkActiveIcon,
StatusBookmarkIcon,
StatusLikeActiveIcon,
StatusLikeIcon,
StatusReplyAllIcon,
StatusReplyIcon,
} from '@/mastodon/components/status/icons';
const baseMessages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -331,10 +333,10 @@ class ActionBar extends PureComponent {
if (status.get('in_reply_to_id', null) === null) {
replyIcon = 'reply';
replyIconComponent = ReplyIcon;
replyIconComponent = StatusReplyIcon;
} else {
replyIcon = 'reply-all';
replyIconComponent = ReplyAllIcon;
replyIconComponent = StatusReplyAllIcon;
}
const bookmarkTitle = intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark);
@@ -342,12 +344,18 @@ class ActionBar extends PureComponent {
return (
<div className='detailed-status__action-bar'>
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} /></div>
<div className='detailed-status__button'>
<IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? StatusReplyIcon : replyIconComponent} onClick={this.handleReplyClick} />
</div>
<div className='detailed-status__button'>
<BoostButton statusId={status.get('id')} />
</div>
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div>
<div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={bookmarkTitle} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div>
<div className='detailed-status__button'>
<IconButton className='star-icon' animate active={status.get('favourited')} title={favouriteTitle} icon='star' iconComponent={status.get('favourited') ? StatusLikeActiveIcon : StatusLikeIcon} onClick={this.handleFavouriteClick} />
</div>
<div className='detailed-status__button'>
<IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={bookmarkTitle} icon='bookmark' iconComponent={status.get('bookmarked') ? StatusBookmarkActiveIcon : StatusBookmarkIcon} onClick={this.handleBookmarkClick} />
</div>
<div className='detailed-status__action-bar-dropdown'>
<Dropdown icon='ellipsis-h' iconComponent={MoreHorizIcon} status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} />

View File

@@ -11,13 +11,13 @@ import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?re
import BlockIcon from '@/material-icons/400-24px/block.svg?react';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import CollectionsIcon from '@/material-icons/400-24px/category.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'mastodon/actions/accounts';
import { closeModal } from 'mastodon/actions/modal';
import { NavigationFocusTarget } from 'mastodon/components/navigation_focus_target';
import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon';
import { StatusReplyIcon } from '@/mastodon/components/status/icons';
export const BlockModal = ({ accountId, acct }) => {
const dispatch = useDispatch();
@@ -71,7 +71,7 @@ export const BlockModal = ({ accountId, acct }) => {
</li>
<li>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={ReplyIcon} /></div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={StatusReplyIcon} /></div>
<div><FormattedMessage id='block_modal.they_cant_mention' defaultMessage="You can't mention, follow, or quote each other." /></div>
</li>

View File

@@ -4,7 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import classNames from 'classnames';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import { StatusBoostIcon } from '@/mastodon/components/status/icons';
import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon';
import { NavigationFocusTarget } from 'mastodon/components/navigation_focus_target';
@@ -57,7 +57,7 @@ export const BoostModal: React.FC<{
<div className='safety-action-modal__top'>
<div className='safety-action-modal__header'>
<div className='safety-action-modal__header__icon'>
<Icon icon={RepeatIcon} id='retweet' />
<Icon icon={StatusBoostIcon} id='retweet' />
</div>
<div>
@@ -81,7 +81,8 @@ export const BoostModal: React.FC<{
values={{
combo: (
<span className='hotkey-combination'>
<kbd>Shift</kbd>+<Icon id='retweet' icon={RepeatIcon} />
<kbd>Shift</kbd>+
<Icon id='retweet' icon={StatusBoostIcon} />
</span>
),
}}

View File

@@ -2,11 +2,11 @@ import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { StatusReplyIcon } from '@/mastodon/components/status/icons';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import DomainDisabledIcon from '@/material-icons/400-24px/domain_disabled.svg?react';
import HistoryIcon from '@/material-icons/400-24px/history.svg?react';
import PersonRemoveIcon from '@/material-icons/400-24px/person_remove.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { blockAccount } from 'mastodon/actions/accounts';
import { blockDomain } from 'mastodon/actions/domain_blocks';
@@ -159,7 +159,7 @@ export const DomainBlockModal: React.FC<{
<li className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={ReplyIcon} />
<Icon icon={StatusReplyIcon} />
</div>
<div>
<FormattedMessage
@@ -171,7 +171,7 @@ export const DomainBlockModal: React.FC<{
<li className='safety-action-modal__bullet-points--deemphasized'>
<div className='safety-action-modal__bullet-points__icon'>
<Icon id='' icon={HistoryIcon} />
<Icon icon={HistoryIcon} />
</div>
<div>
<FormattedMessage

View File

@@ -10,7 +10,6 @@ import { useDispatch } from 'react-redux';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
import { muteAccount } from 'mastodon/actions/accounts';
@@ -20,6 +19,7 @@ import { CheckBox } from 'mastodon/components/check_box';
import { Icon } from 'mastodon/components/icon';
import { NavigationFocusTarget } from 'mastodon/components/navigation_focus_target';
import { RadioButton } from 'mastodon/components/radio_button';
import { StatusReplyIcon } from '@/mastodon/components/status/icons';
const messages = defineMessages({
minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
@@ -109,7 +109,7 @@ export const MuteModal = ({ accountId, acct }) => {
</li>
<li>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={ReplyIcon} /></div>
<div className='safety-action-modal__bullet-points__icon'><Icon icon={StatusReplyIcon} /></div>
<div><FormattedMessage id='mute_modal.they_can_mention_and_follow' defaultMessage="They can mention and follow you, but you won't see them." /></div>
</li>
</ul>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path fill="currentColor" d="M6.875 11.725a.626.626 0 1 1 0 1.25H4.634l1.142 1.142a6.25 6.25 0 0 0 8.788.052.624.624 0 0 1 .874.893 7.45 7.45 0 0 1-5.24 2.136h-.042a7.46 7.46 0 0 1-5.263-2.197L3.75 13.858V16.1a.626.626 0 1 1-1.25 0v-3.75a.626.626 0 0 1 .625-.625zM10 5c1.072 0 2.063.34 2.877.913a1.46 1.46 0 0 0-.677.309l-.106.096a1.46 1.46 0 0 0 1.031 2.49h1.73q.143.574.145 1.192a5 5 0 0 1-8.24 3.808h.115a1.459 1.459 0 1 0 0-2.916H5.081A5 5 0 0 1 10 5m-.15-2.5a7.5 7.5 0 0 1 5.26 2.198l1.14 1.143V3.6a.626.626 0 1 1 1.25 0v3.75a.626.626 0 0 1-.625.625h-3.75a.626.626 0 1 1 0-1.25h2.241l-1.142-1.143a6.22 6.22 0 0 0-4.386-1.831h-.035a6.21 6.21 0 0 0-4.366 1.78.625.625 0 0 1-.874-.894A7.5 7.5 0 0 1 9.85 2.5"/></svg>

After

Width:  |  Height:  |  Size: 791 B