diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx
index 36f8c1b26..f47d66bfe 100644
--- a/app/soapbox/components/account.tsx
+++ b/app/soapbox/components/account.tsx
@@ -3,7 +3,7 @@ import { Link, useHistory } from 'react-router-dom';
import HoverRefWrapper from 'soapbox/components/hover_ref_wrapper';
import VerificationBadge from 'soapbox/components/verification_badge';
-import ActionButton from 'soapbox/features/ui/components/action_button';
+import ActionButton from 'soapbox/features/ui/components/action-button';
import { useAppSelector, useOnScreen } from 'soapbox/hooks';
import { getAcct } from 'soapbox/utils/accounts';
import { displayFqn } from 'soapbox/utils/state';
@@ -46,6 +46,8 @@ interface IAccount {
actionAlignment?: 'center' | 'top',
actionIcon?: string,
actionTitle?: string,
+ /** Override other actions for specificity like mute/unmute. */
+ actionType?: 'muting' | 'blocking',
avatarSize?: number,
hidden?: boolean,
hideActions?: boolean,
@@ -60,6 +62,7 @@ interface IAccount {
const Account = ({
account,
+ actionType,
action,
actionIcon,
actionTitle,
@@ -111,7 +114,7 @@ const Account = ({
}
if (account.id !== me) {
- return ;
+ return ;
}
return null;
diff --git a/app/soapbox/components/profile_hover_card.tsx b/app/soapbox/components/profile_hover_card.tsx
index 4474d8d21..8511839fb 100644
--- a/app/soapbox/components/profile_hover_card.tsx
+++ b/app/soapbox/components/profile_hover_card.tsx
@@ -10,7 +10,7 @@ import {
updateProfileHoverCard,
} from 'soapbox/actions/profile_hover_card';
import Badge from 'soapbox/components/badge';
-import ActionButton from 'soapbox/features/ui/components/action_button';
+import ActionButton from 'soapbox/features/ui/components/action-button';
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
import { UserPanel } from 'soapbox/features/ui/util/async-components';
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js
index 1db5b1dd3..c070c003a 100644
--- a/app/soapbox/features/account/components/header.js
+++ b/app/soapbox/features/account/components/header.js
@@ -16,7 +16,7 @@ import Badge from 'soapbox/components/badge';
import StillImage from 'soapbox/components/still_image';
import { HStack, IconButton, Menu, MenuButton, MenuItem, MenuList, MenuLink, MenuDivider } from 'soapbox/components/ui';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
-import ActionButton from 'soapbox/features/ui/components/action_button';
+import ActionButton from 'soapbox/features/ui/components/action-button';
import {
isLocal,
isRemote,
diff --git a/app/soapbox/features/blocks/index.tsx b/app/soapbox/features/blocks/index.tsx
index 90e41bf65..8b56fc262 100644
--- a/app/soapbox/features/blocks/index.tsx
+++ b/app/soapbox/features/blocks/index.tsx
@@ -48,7 +48,7 @@ const Blocks: React.FC = () => {
itemClassName='pb-4'
>
{accountIds.map((id: string) =>
- ,
+ ,
)}
diff --git a/app/soapbox/features/directory/components/account_card.js b/app/soapbox/features/directory/components/account_card.js
index 83a48b256..0acab06d5 100644
--- a/app/soapbox/features/directory/components/account_card.js
+++ b/app/soapbox/features/directory/components/account_card.js
@@ -12,7 +12,7 @@ import DisplayName from 'soapbox/components/display_name';
import Permalink from 'soapbox/components/permalink';
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
import { Text } from 'soapbox/components/ui';
-import ActionButton from 'soapbox/features/ui/components/action_button';
+import ActionButton from 'soapbox/features/ui/components/action-button';
import { makeGetAccount } from 'soapbox/selectors';
import { shortNumberFormat } from 'soapbox/utils/numbers';
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
@@ -82,4 +82,4 @@ class AccountCard extends ImmutablePureComponent {
);
}
-}
\ No newline at end of file
+}
diff --git a/app/soapbox/features/follow_recommendations/components/account.tsx b/app/soapbox/features/follow_recommendations/components/account.tsx
index 8ba56b497..6ba90ecd2 100644
--- a/app/soapbox/features/follow_recommendations/components/account.tsx
+++ b/app/soapbox/features/follow_recommendations/components/account.tsx
@@ -3,7 +3,7 @@ import React from 'react';
import Avatar from 'soapbox/components/avatar';
import DisplayName from 'soapbox/components/display_name';
import Permalink from 'soapbox/components/permalink';
-import ActionButton from 'soapbox/features/ui/components/action_button';
+import ActionButton from 'soapbox/features/ui/components/action-button';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
diff --git a/app/soapbox/features/mutes/index.tsx b/app/soapbox/features/mutes/index.tsx
index 06bf89e21..adaa390e9 100644
--- a/app/soapbox/features/mutes/index.tsx
+++ b/app/soapbox/features/mutes/index.tsx
@@ -48,7 +48,7 @@ const Mutes: React.FC = () => {
itemClassName='pb-4'
>
{accountIds.map((id: string) =>
- ,
+ ,
)}
diff --git a/app/soapbox/features/ui/components/action-button.tsx b/app/soapbox/features/ui/components/action-button.tsx
new file mode 100644
index 000000000..e79c22567
--- /dev/null
+++ b/app/soapbox/features/ui/components/action-button.tsx
@@ -0,0 +1,199 @@
+import React from 'react';
+import { defineMessages, useIntl } from 'react-intl';
+import { useDispatch } from 'react-redux';
+
+import {
+ followAccount,
+ unfollowAccount,
+ blockAccount,
+ unblockAccount,
+ muteAccount,
+ unmuteAccount,
+} from 'soapbox/actions/accounts';
+import { openModal } from 'soapbox/actions/modals';
+import { Button } from 'soapbox/components/ui';
+import { useAppSelector, useFeatures } from 'soapbox/hooks';
+
+import type { Account as AccountEntity } from 'soapbox/types/entities';
+
+const messages = defineMessages({
+ block: { id: 'account.block', defaultMessage: 'Block @{name}' },
+ blocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
+ edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+ follow: { id: 'account.follow', defaultMessage: 'Follow' },
+ mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
+ remote_follow: { id: 'account.remote_follow', defaultMessage: 'Remote follow' },
+ requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
+ requested_small: { id: 'account.requested_small', defaultMessage: 'Awaiting approval' },
+ unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
+ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
+ unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+});
+
+interface iActionButton {
+ account: AccountEntity
+ actionType?: 'muting' | 'blocking'
+ small?: boolean
+}
+
+const ActionButton = ({ account, actionType, small }: iActionButton) => {
+ const dispatch = useDispatch();
+ const features = useFeatures();
+ const intl = useIntl();
+
+ const me = useAppSelector((state) => state.me);
+
+ const handleFollow = () => {
+ if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
+ dispatch(unfollowAccount(account.get('id')));
+ } else {
+ dispatch(followAccount(account.get('id')));
+ }
+ };
+
+ const handleBlock = () => {
+ if (account.getIn(['relationship', 'blocking'])) {
+ dispatch(unblockAccount(account.get('id')));
+ } else {
+ dispatch(blockAccount(account.get('id')));
+ }
+ };
+
+ const handleMute = () => {
+ if (account.getIn(['relationship', 'muting'])) {
+ dispatch(unmuteAccount(account.get('id')));
+ } else {
+ dispatch(muteAccount(account.get('id')));
+ }
+ };
+
+ const handleRemoteFollow = () => {
+ dispatch(openModal('UNAUTHORIZED', {
+ action: 'FOLLOW',
+ account: account.get('id'),
+ ap_id: account.get('url'),
+ }));
+ };
+
+ const mutingAction = () => {
+ const isMuted = account.getIn(['relationship', 'muting']);
+ const messageKey = isMuted ? messages.unmute : messages.mute;
+ const text = intl.formatMessage(messageKey, { name: account.get('username') });
+
+ return (
+
+ );
+ };
+
+ const blockingAction = () => {
+ const isBlocked = account.getIn(['relationship', 'blocking']);
+ const messageKey = isBlocked ? messages.unblock : messages.block;
+ const text = intl.formatMessage(messageKey, { name: account.get('username') });
+
+ return (
+
+ );
+ };
+
+ const empty = <>>;
+
+ if (!me) {
+ // Remote follow
+ if (features.remoteInteractionsAPI) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ }
+
+ if (me !== account.get('id')) {
+ const isFollowing = account.getIn(['relationship', 'following']);
+ const blockedBy = account.getIn(['relationship', 'blocked_by']) as boolean;
+
+ if (actionType) {
+ if (actionType === 'muting') {
+ return mutingAction();
+ } else if (actionType === 'blocking') {
+ return blockingAction();
+ }
+ }
+
+ if (!account.get('relationship')) {
+ // Wait until the relationship is loaded
+ return empty;
+ } else if (account.getIn(['relationship', 'requested'])) {
+ // Awaiting acceptance
+ return (
+
+ );
+ } else if (!account.getIn(['relationship', 'blocking']) && !account.getIn(['relationship', 'muting'])) {
+ // Follow & Unfollow
+ return (
+
+ );
+ } else if (account.getIn(['relationship', 'blocking'])) {
+ // Unblock
+ return (
+
+ );
+ }
+ } else {
+ // Edit profile
+ return (
+
+ );
+ }
+
+ return empty;
+};
+
+export default ActionButton;
diff --git a/app/soapbox/features/ui/components/action_button.js b/app/soapbox/features/ui/components/action_button.js
deleted file mode 100644
index 926134c39..000000000
--- a/app/soapbox/features/ui/components/action_button.js
+++ /dev/null
@@ -1,162 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-import { defineMessages, injectIntl } from 'react-intl';
-import { connect } from 'react-redux';
-
-import {
- followAccount,
- unfollowAccount,
- blockAccount,
- unblockAccount,
-} from 'soapbox/actions/accounts';
-import { openModal } from 'soapbox/actions/modals';
-import Icon from 'soapbox/components/icon';
-import { Button } from 'soapbox/components/ui';
-import { getFeatures } from 'soapbox/utils/features';
-
-const messages = defineMessages({
- unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
- follow: { id: 'account.follow', defaultMessage: 'Follow' },
- remote_follow: { id: 'account.remote_follow', defaultMessage: 'Remote follow' },
- requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
- requested_small: { id: 'account.requested_small', defaultMessage: 'Awaiting approval' },
- unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
- edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
- blocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
-});
-
-const mapStateToProps = state => {
- const me = state.get('me');
- const instance = state.get('instance');
-
- return {
- me,
- features: getFeatures(instance),
- };
-};
-
-const mapDispatchToProps = (dispatch) => ({
- onFollow(account) {
- if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
- dispatch(unfollowAccount(account.get('id')));
- } else {
- dispatch(followAccount(account.get('id')));
- }
- },
-
- onBlock(account) {
- if (account.getIn(['relationship', 'blocking'])) {
- dispatch(unblockAccount(account.get('id')));
- } else {
- dispatch(blockAccount(account.get('id')));
- }
- },
-
- onOpenUnauthorizedModal(account) {
- dispatch(openModal('UNAUTHORIZED', {
- action: 'FOLLOW',
- account: account.get('id'),
- ap_id: account.get('url'),
- }));
- },
-});
-
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
-class ActionButton extends ImmutablePureComponent {
-
- static propTypes = {
- account: ImmutablePropTypes.record.isRequired,
- onFollow: PropTypes.func.isRequired,
- onBlock: PropTypes.func.isRequired,
- onOpenUnauthorizedModal: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
- small: PropTypes.bool,
- features: PropTypes.object.isRequired,
- };
-
- static defaultProps = {
- small: false,
- }
-
- componentDidMount() {
- window.addEventListener('resize', this.handleResize, { passive: true });
- }
-
- componentWillUnmount() {
- window.removeEventListener('resize', this.handleResize);
- }
-
- handleFollow = () => {
- this.props.onFollow(this.props.account);
- }
-
- handleBlock = () => {
- this.props.onBlock(this.props.account);
- }
-
- handleRemoteFollow = () => {
- this.props.onOpenUnauthorizedModal(this.props.account);
- }
-
- render() {
- const { account, intl, me, small, features } = this.props;
- const empty = <>>;
-
- if (!me) {
- // Remote follow
- if (features.remoteInteractionsAPI) {
- return ();
- }
-
- return ();
- }
-
- if (me !== account.get('id')) {
- const isFollowing = account.getIn(['relationship', 'following']);
- const blockedBy = account.getIn(['relationship', 'blocked_by']);
-
- if (!account.get('relationship')) { // Wait until the relationship is loaded
- return empty;
- } else if (account.getIn(['relationship', 'requested'])) {
- // Awaiting acceptance
- return ;
- } else if (!account.getIn(['relationship', 'blocking'])) {
- // Follow & Unfollow
- return ();
- } else if (account.getIn(['relationship', 'blocking'])) {
- // Unblock
- return ;
- }
- } else {
- // Edit profile
- return ;
- }
- return empty;
- }
-
-}
diff --git a/app/styles/components/buttons.scss b/app/styles/components/buttons.scss
index f095682e6..dc5bce4f4 100644
--- a/app/styles/components/buttons.scss
+++ b/app/styles/components/buttons.scss
@@ -114,18 +114,6 @@ a.button {
}
}
-.button--follow {
- display: flex;
- align-items: center;
- justify-content: center;
-
- .svg-icon {
- margin: 0 0 0 6px;
- width: 20px;
- height: 20px;
- }
-}
-
.button--welcome {
.emojione {
margin: -1px 6px 0 -4px;