diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2024-04-24 04:52:29 +0200 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2024-04-24 04:52:29 +0200 |
commit | b2cca3f711547d2d57f3b200d6d54570370d868f (patch) | |
tree | 11b61f7a36e9ea5eb83f2e7097c971c5a114b882 | |
parent | 3f6887557b23d363e7f8f18518db4447739d64bb (diff) |
Change mute options to be in dropdown on muted users list in web UIfix-mute-buttons
-rw-r--r-- | app/javascript/mastodon/components/account.jsx | 272 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/components.scss | 14 |
2 files changed, 150 insertions, 136 deletions
diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx index 4a99dd0bbf1..3282696d341 100644 --- a/app/javascript/mastodon/components/account.jsx +++ b/app/javascript/mastodon/components/account.jsx @@ -1,17 +1,19 @@ import PropTypes from 'prop-types'; +import { useCallback } from 'react'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; +import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; import { EmptyAccount } from 'mastodon/components/empty_account'; import { ShortNumber } from 'mastodon/components/short_number'; import { VerifiedBadge } from 'mastodon/components/verified_badge'; +import DropdownMenuContainer from '../containers/dropdown_menu_container'; import { me } from '../initial_state'; import { Avatar } from './avatar'; @@ -30,151 +32,151 @@ const messages = defineMessages({ unmute_notifications: { id: 'account.unmute_notifications_short', defaultMessage: 'Unmute notifications' }, mute: { id: 'account.mute_short', defaultMessage: 'Mute' }, block: { id: 'account.block_short', defaultMessage: 'Block' }, + more: { id: 'status.more', defaultMessage: 'More' }, }); -class Account extends ImmutablePureComponent { - - static propTypes = { - size: PropTypes.number, - account: ImmutablePropTypes.record, - onFollow: PropTypes.func, - onBlock: PropTypes.func, - onMute: PropTypes.func, - onMuteNotifications: PropTypes.func, - intl: PropTypes.object.isRequired, - hidden: PropTypes.bool, - minimal: PropTypes.bool, - defaultAction: PropTypes.string, - withBio: PropTypes.bool, - }; - - static defaultProps = { - size: 46, - }; - - handleFollow = () => { - this.props.onFollow(this.props.account); - }; - - handleBlock = () => { - this.props.onBlock(this.props.account); - }; - - handleMute = () => { - this.props.onMute(this.props.account); - }; - - handleMuteNotifications = () => { - this.props.onMuteNotifications(this.props.account, true); - }; - - handleUnmuteNotifications = () => { - this.props.onMuteNotifications(this.props.account, false); - }; - - render () { - const { account, intl, hidden, withBio, defaultAction, size, minimal } = this.props; - - if (!account) { - return <EmptyAccount size={size} minimal={minimal} />; - } +const Account = ({ size = 46, account, onFollow, onBlock, onMute, onMuteNotifications, hidden, minimal, defaultAction, withBio }) => { + const intl = useIntl(); - if (hidden) { - return ( - <> - {account.get('display_name')} - {account.get('username')} - </> - ); - } + const handleFollow = useCallback(() => { + onFollow(account); + }, [onFollow, account]); + + const handleBlock = useCallback(() => { + onBlock(account); + }, [onBlock, account]); + + const handleMute = useCallback(() => { + onMute(account); + }, [onMute, account]); + + const handleMuteNotifications = useCallback(() => { + onMuteNotifications(account, true); + }, [onMuteNotifications, account]); + + const handleUnmuteNotifications = useCallback(() => { + onMuteNotifications(account, false); + }, [onMuteNotifications, account]); + + if (!account) { + return <EmptyAccount size={size} minimal={minimal} />; + } + + if (hidden) { + return ( + <> + {account.get('display_name')} + {account.get('username')} + </> + ); + } - let buttons; - - if (account.get('id') !== me && account.get('relationship', null) !== null) { - const following = account.getIn(['relationship', 'following']); - const requested = account.getIn(['relationship', 'requested']); - const blocking = account.getIn(['relationship', 'blocking']); - const muting = account.getIn(['relationship', 'muting']); - - if (requested) { - buttons = <Button text={intl.formatMessage(messages.cancel_follow_request)} onClick={this.handleFollow} />; - } else if (blocking) { - buttons = <Button text={intl.formatMessage(messages.unblock)} onClick={this.handleBlock} />; - } else if (muting) { - let hidingNotificationsButton; - - if (account.getIn(['relationship', 'muting_notifications'])) { - hidingNotificationsButton = <Button text={intl.formatMessage(messages.unmute_notifications)} onClick={this.handleUnmuteNotifications} />; - } else { - hidingNotificationsButton = <Button text={intl.formatMessage(messages.mute_notifications)} onClick={this.handleMuteNotifications} />; - } - - buttons = ( - <> - <Button text={intl.formatMessage(messages.unmute)} onClick={this.handleMute} /> - {hidingNotificationsButton} - </> - ); - } else if (defaultAction === 'mute') { - buttons = <Button title={intl.formatMessage(messages.mute)} onClick={this.handleMute} />; - } else if (defaultAction === 'block') { - buttons = <Button text={intl.formatMessage(messages.block)} onClick={this.handleBlock} />; - } else if (!account.get('suspended') && !account.get('moved') || following) { - buttons = <Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} />; + let buttons; + + if (account.get('id') !== me && account.get('relationship', null) !== null) { + const following = account.getIn(['relationship', 'following']); + const requested = account.getIn(['relationship', 'requested']); + const blocking = account.getIn(['relationship', 'blocking']); + const muting = account.getIn(['relationship', 'muting']); + + if (requested) { + buttons = <Button text={intl.formatMessage(messages.cancel_follow_request)} onClick={handleFollow} />; + } else if (blocking) { + buttons = <Button text={intl.formatMessage(messages.unblock)} onClick={handleBlock} />; + } else if (muting) { + let menu; + + if (account.getIn(['relationship', 'muting_notifications'])) { + menu = [{ text: intl.formatMessage(messages.unmute_notifications), action: handleUnmuteNotifications }]; + } else { + menu = [{ text: intl.formatMessage(messages.mute_notifications), action: handleMuteNotifications }]; } - } - let muteTimeRemaining; + buttons = ( + <> + <DropdownMenuContainer + items={menu} + icon='ellipsis-h' + iconComponent={MoreHorizIcon} + direction='right' + title={intl.formatMessage(messages.more)} + /> - if (account.get('mute_expires_at')) { - muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>; + <Button text={intl.formatMessage(messages.unmute)} onClick={handleMute} /> + </> + ); + } else if (defaultAction === 'mute') { + buttons = <Button title={intl.formatMessage(messages.mute)} onClick={handleMute} />; + } else if (defaultAction === 'block') { + buttons = <Button text={intl.formatMessage(messages.block)} onClick={handleBlock} />; + } else if (!account.get('suspended') && !account.get('moved') || following) { + buttons = <Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={handleFollow} />; } + } + + let muteTimeRemaining; - let verification; + if (account.get('mute_expires_at')) { + muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>; + } - const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); + let verification; - if (firstVerifiedField) { - verification = <VerifiedBadge link={firstVerifiedField.get('value')} />; - } + const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); - return ( - <div className={classNames('account', { 'account--minimal': minimal })}> - <div className='account__wrapper'> - <Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`}> - <div className='account__avatar-wrapper'> - <Avatar account={account} size={size} /> - </div> - - <div className='account__contents'> - <DisplayName account={account} /> - {!minimal && ( - <div className='account__details'> - <ShortNumber value={account.get('followers_count')} renderer={FollowersCounter} /> {verification} {muteTimeRemaining} - </div> - )} - </div> - </Link> - - {!minimal && ( - <div className='account__relationship'> - {buttons} - </div> - )} - </div> - - {withBio && (account.get('note').length > 0 ? ( - <div - className='account__note translate' - dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }} - /> - ) : ( - <div className='account__note account__note--missing'><FormattedMessage id='account.no_bio' defaultMessage='No description provided.' /></div> - ))} - </div> - ); + if (firstVerifiedField) { + verification = <VerifiedBadge link={firstVerifiedField.get('value')} />; } -} + return ( + <div className={classNames('account', { 'account--minimal': minimal })}> + <div className='account__wrapper'> + <Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`}> + <div className='account__avatar-wrapper'> + <Avatar account={account} size={size} /> + </div> + + <div className='account__contents'> + <DisplayName account={account} /> + {!minimal && ( + <div className='account__details'> + <ShortNumber value={account.get('followers_count')} renderer={FollowersCounter} /> {verification} {muteTimeRemaining} + </div> + )} + </div> + </Link> + + {!minimal && ( + <div className='account__relationship'> + {buttons} + </div> + )} + </div> -export default injectIntl(Account); + {withBio && (account.get('note').length > 0 ? ( + <div + className='account__note translate' + dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }} + /> + ) : ( + <div className='account__note account__note--missing'><FormattedMessage id='account.no_bio' defaultMessage='No description provided.' /></div> + ))} + </div> + ); +}; + +Account.propTypes = { + size: PropTypes.number, + account: ImmutablePropTypes.record, + onFollow: PropTypes.func, + onBlock: PropTypes.func, + onMute: PropTypes.func, + onMuteNotifications: PropTypes.func, + intl: PropTypes.object.isRequired, + hidden: PropTypes.bool, + minimal: PropTypes.bool, + defaultAction: PropTypes.string, + withBio: PropTypes.bool, +}; + +export default Account; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 87e13ee45e2..b2139169a59 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2015,7 +2015,19 @@ a .account__avatar { white-space: nowrap; display: flex; align-items: center; - gap: 4px; + gap: 8px; + + .icon-button { + border: 1px solid var(--background-border-color); + border-radius: 4px; + box-sizing: content-box; + padding: 5px; + + .icon { + width: 24px; + height: 24px; + } + } } .account-authorize { |