diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 36f8c1b26..8c626b3e7 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'; 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/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/ui/components/action-button.tsx b/app/soapbox/features/ui/components/action-button.tsx new file mode 100644 index 000000000..c86283d9f --- /dev/null +++ b/app/soapbox/features/ui/components/action-button.tsx @@ -0,0 +1,168 @@ +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({ + 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}' }, + unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, + edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, + blocked: { id: 'account.blocked', defaultMessage: 'Blocked' }, +}); + +interface IAccount { + account: AccountEntity + small?: boolean +} + +const ActionButton = ({ account, small }: IAccount) => { + 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 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 (!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 if (account.getIn(['relationship', 'muting'])) { + // Unmute + 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 37562454c..000000000 --- a/app/soapbox/features/ui/components/action_button.js +++ /dev/null @@ -1,180 +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, - muteAccount, - unmuteAccount, -} 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}' }, - unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{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'))); - } - }, - - onMute(account) { - if (account.getIn(['relationship', 'muting'])) { - dispatch(unmuteAccount(account.get('id'))); - } else { - dispatch(muteAccount(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); - } - - handleMute = () => { - this.props.onMute(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']) && !account.getIn(['relationship', 'muting'])) { - // Follow & Unfollow - return (); - } else if (account.getIn(['relationship', 'blocking'])) { - // Unblock - return ; - } else if (account.getIn(['relationship', 'muting'])) { - // Unmute - 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;