mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-06 09:02:43 +00:00
Update "Follow" button labels (#36264)
This commit is contained in:
parent
e07b9dfdc1
commit
cb5bbbfb05
|
@ -5,24 +5,61 @@ 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 { fetchRelationships, followAccount } from 'mastodon/actions/accounts';
|
import {
|
||||||
|
fetchRelationships,
|
||||||
|
followAccount,
|
||||||
|
unblockAccount,
|
||||||
|
unmuteAccount,
|
||||||
|
} from 'mastodon/actions/accounts';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import { me } from 'mastodon/initial_state';
|
import { me } from 'mastodon/initial_state';
|
||||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
const messages = defineMessages({
|
import { useBreakpoint } from '../features/ui/hooks/useBreakpoint';
|
||||||
|
|
||||||
|
const longMessages = defineMessages({
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
|
||||||
|
unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
|
followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
|
||||||
|
followRequest: {
|
||||||
|
id: 'account.follow_request',
|
||||||
|
defaultMessage: 'Request to follow',
|
||||||
|
},
|
||||||
|
followRequestCancel: {
|
||||||
|
id: 'account.follow_request_cancel',
|
||||||
|
defaultMessage: 'Cancel request',
|
||||||
|
},
|
||||||
editProfile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
editProfile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const shortMessages = {
|
||||||
|
...longMessages, // Align type signature of shortMessages and longMessages
|
||||||
|
...defineMessages({
|
||||||
|
followBack: {
|
||||||
|
id: 'account.follow_back_short',
|
||||||
|
defaultMessage: 'Follow back',
|
||||||
|
},
|
||||||
|
followRequest: {
|
||||||
|
id: 'account.follow_request_short',
|
||||||
|
defaultMessage: 'Request',
|
||||||
|
},
|
||||||
|
followRequestCancel: {
|
||||||
|
id: 'account.follow_request_cancel_short',
|
||||||
|
defaultMessage: 'Cancel',
|
||||||
|
},
|
||||||
|
editProfile: { id: 'account.edit_profile_short', defaultMessage: 'Edit' },
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export const FollowButton: React.FC<{
|
export const FollowButton: React.FC<{
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
}> = ({ accountId, compact }) => {
|
labelLength?: 'auto' | 'short' | 'long';
|
||||||
|
}> = ({ accountId, compact, labelLength = 'auto' }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { signedIn } = useIdentity();
|
const { signedIn } = useIdentity();
|
||||||
|
@ -57,29 +94,48 @@ export const FollowButton: React.FC<{
|
||||||
|
|
||||||
if (accountId === me) {
|
if (accountId === me) {
|
||||||
return;
|
return;
|
||||||
|
} else if (relationship.muting) {
|
||||||
|
dispatch(unmuteAccount(accountId));
|
||||||
} else if (account && (relationship.following || relationship.requested)) {
|
} else if (account && (relationship.following || relationship.requested)) {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({ modalType: 'CONFIRM_UNFOLLOW', modalProps: { account } }),
|
openModal({ modalType: 'CONFIRM_UNFOLLOW', modalProps: { account } }),
|
||||||
);
|
);
|
||||||
|
} else if (relationship.blocking) {
|
||||||
|
dispatch(unblockAccount(accountId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(followAccount(accountId));
|
dispatch(followAccount(accountId));
|
||||||
}
|
}
|
||||||
}, [dispatch, accountId, relationship, account, signedIn]);
|
}, [dispatch, accountId, relationship, account, signedIn]);
|
||||||
|
|
||||||
|
const isNarrow = useBreakpoint('narrow');
|
||||||
|
const useShortLabel =
|
||||||
|
labelLength === 'short' || (labelLength === 'auto' && isNarrow);
|
||||||
|
const messages = useShortLabel ? shortMessages : longMessages;
|
||||||
|
|
||||||
|
const followMessage = account?.locked
|
||||||
|
? messages.followRequest
|
||||||
|
: messages.follow;
|
||||||
|
|
||||||
let label;
|
let label;
|
||||||
|
|
||||||
if (!signedIn) {
|
if (!signedIn) {
|
||||||
label = intl.formatMessage(messages.follow);
|
label = intl.formatMessage(followMessage);
|
||||||
} else if (accountId === me) {
|
} else if (accountId === me) {
|
||||||
label = intl.formatMessage(messages.editProfile);
|
label = intl.formatMessage(messages.editProfile);
|
||||||
} else if (!relationship) {
|
} else if (!relationship) {
|
||||||
label = <LoadingIndicator />;
|
label = <LoadingIndicator />;
|
||||||
} else if (relationship.following || relationship.requested) {
|
} else if (relationship.muting) {
|
||||||
|
label = intl.formatMessage(messages.unmute);
|
||||||
|
} else if (relationship.following) {
|
||||||
label = intl.formatMessage(messages.unfollow);
|
label = intl.formatMessage(messages.unfollow);
|
||||||
} else if (relationship.followed_by) {
|
} else if (relationship.blocking) {
|
||||||
|
label = intl.formatMessage(messages.unblock);
|
||||||
|
} else if (relationship.requested) {
|
||||||
|
label = intl.formatMessage(messages.followRequestCancel);
|
||||||
|
} else if (relationship.followed_by && !account?.locked) {
|
||||||
label = intl.formatMessage(messages.followBack);
|
label = intl.formatMessage(messages.followBack);
|
||||||
} else {
|
} else {
|
||||||
label = intl.formatMessage(messages.follow);
|
label = intl.formatMessage(followMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountId === me) {
|
if (accountId === me) {
|
||||||
|
|
|
@ -1,134 +1,23 @@
|
||||||
import { useCallback } from 'react';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
|
||||||
followAccount,
|
|
||||||
unblockAccount,
|
|
||||||
unmuteAccount,
|
|
||||||
} from 'mastodon/actions/accounts';
|
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { Button } from 'mastodon/components/button';
|
|
||||||
import { DisplayName } from 'mastodon/components/display_name';
|
import { DisplayName } from 'mastodon/components/display_name';
|
||||||
|
import { FollowButton } from 'mastodon/components/follow_button';
|
||||||
import { ShortNumber } from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { autoPlayGif, me } from 'mastodon/initial_state';
|
import { autoPlayGif } from 'mastodon/initial_state';
|
||||||
import type { Account } from 'mastodon/models/account';
|
import type { Account } from 'mastodon/models/account';
|
||||||
import { makeGetAccount } from 'mastodon/selectors';
|
import { makeGetAccount } from 'mastodon/selectors';
|
||||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
import { useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
|
||||||
cancel_follow_request: {
|
|
||||||
id: 'account.cancel_follow_request',
|
|
||||||
defaultMessage: 'Withdraw follow request',
|
|
||||||
},
|
|
||||||
requested: {
|
|
||||||
id: 'account.requested',
|
|
||||||
defaultMessage: 'Awaiting approval. Click to cancel follow request',
|
|
||||||
},
|
|
||||||
unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
|
|
||||||
unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
|
|
||||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const getAccount = makeGetAccount();
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
const intl = useIntl();
|
|
||||||
const account = useAppSelector((s) => getAccount(s, accountId));
|
const account = useAppSelector((s) => getAccount(s, accountId));
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const handleFollow = useCallback(() => {
|
|
||||||
if (!account) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
account.getIn(['relationship', 'following']) ||
|
|
||||||
account.getIn(['relationship', 'requested'])
|
|
||||||
) {
|
|
||||||
dispatch(
|
|
||||||
openModal({ modalType: 'CONFIRM_UNFOLLOW', modalProps: { account } }),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
dispatch(followAccount(account.get('id')));
|
|
||||||
}
|
|
||||||
}, [account, dispatch]);
|
|
||||||
|
|
||||||
const handleBlock = useCallback(() => {
|
|
||||||
if (account?.relationship?.blocking) {
|
|
||||||
dispatch(unblockAccount(account.get('id')));
|
|
||||||
}
|
|
||||||
}, [account, dispatch]);
|
|
||||||
|
|
||||||
const handleMute = useCallback(() => {
|
|
||||||
if (account?.relationship?.muting) {
|
|
||||||
dispatch(unmuteAccount(account.get('id')));
|
|
||||||
}
|
|
||||||
}, [account, dispatch]);
|
|
||||||
|
|
||||||
const handleEditProfile = useCallback(() => {
|
|
||||||
window.open('/settings/profile', '_blank');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
let actionBtn;
|
|
||||||
|
|
||||||
if (me !== account.get('id')) {
|
|
||||||
if (!account.get('relationship')) {
|
|
||||||
// Wait until the relationship is loaded
|
|
||||||
actionBtn = '';
|
|
||||||
} else if (account.getIn(['relationship', 'requested'])) {
|
|
||||||
actionBtn = (
|
|
||||||
<Button
|
|
||||||
text={intl.formatMessage(messages.cancel_follow_request)}
|
|
||||||
title={intl.formatMessage(messages.requested)}
|
|
||||||
onClick={handleFollow}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (account.getIn(['relationship', 'muting'])) {
|
|
||||||
actionBtn = (
|
|
||||||
<Button
|
|
||||||
text={intl.formatMessage(messages.unmute)}
|
|
||||||
onClick={handleMute}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (!account.getIn(['relationship', 'blocking'])) {
|
|
||||||
actionBtn = (
|
|
||||||
<Button
|
|
||||||
disabled={account.relationship?.blocked_by}
|
|
||||||
className={classNames({
|
|
||||||
'button--destructive': account.getIn(['relationship', 'following']),
|
|
||||||
})}
|
|
||||||
text={intl.formatMessage(
|
|
||||||
account.getIn(['relationship', 'following'])
|
|
||||||
? messages.unfollow
|
|
||||||
: messages.follow,
|
|
||||||
)}
|
|
||||||
onClick={handleFollow}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
|
||||||
actionBtn = (
|
|
||||||
<Button
|
|
||||||
text={intl.formatMessage(messages.unblock)}
|
|
||||||
onClick={handleBlock}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
actionBtn = (
|
|
||||||
<Button
|
|
||||||
text={intl.formatMessage(messages.edit_profile)}
|
|
||||||
onClick={handleEditProfile}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='account-card'>
|
<div className='account-card'>
|
||||||
<Link to={`/@${account.get('acct')}`} className='account-card__permalink'>
|
<Link to={`/@${account.get('acct')}`} className='account-card__permalink'>
|
||||||
|
@ -186,7 +75,9 @@ export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account-card__actions__button'>{actionBtn}</div>
|
<div className='account-card__actions__button'>
|
||||||
|
<FollowButton accountId={account.get('id')} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,8 +25,6 @@ import { domain } from 'mastodon/initial_state';
|
||||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
|
||||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
|
||||||
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
||||||
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
||||||
dismiss: {
|
dismiss: {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
const breakpoints = {
|
const breakpoints = {
|
||||||
|
narrow: 479, // Device width under which horizontal space is constrained
|
||||||
openable: 759, // Device width at which the sidebar becomes an openable hamburger menu
|
openable: 759, // Device width at which the sidebar becomes an openable hamburger menu
|
||||||
full: 1174, // Device width at which all 3 columns can be displayed
|
full: 1174, // Device width at which all 3 columns can be displayed
|
||||||
};
|
};
|
||||||
|
|
||||||
type Breakpoint = 'openable' | 'full';
|
type Breakpoint = keyof typeof breakpoints;
|
||||||
|
|
||||||
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
export const useBreakpoint = (breakpoint: Breakpoint) => {
|
||||||
const [isMatching, setIsMatching] = useState(false);
|
const [isMatching, setIsMatching] = useState(false);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
"account.disable_notifications": "Stop notifying me when @{name} posts",
|
||||||
"account.domain_blocking": "Blocking domain",
|
"account.domain_blocking": "Blocking domain",
|
||||||
"account.edit_profile": "Edit profile",
|
"account.edit_profile": "Edit profile",
|
||||||
|
"account.edit_profile_short": "Edit",
|
||||||
"account.enable_notifications": "Notify me when @{name} posts",
|
"account.enable_notifications": "Notify me when @{name} posts",
|
||||||
"account.endorse": "Feature on profile",
|
"account.endorse": "Feature on profile",
|
||||||
"account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}",
|
"account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}",
|
||||||
|
@ -40,6 +41,11 @@
|
||||||
"account.featured_tags.last_status_never": "No posts",
|
"account.featured_tags.last_status_never": "No posts",
|
||||||
"account.follow": "Follow",
|
"account.follow": "Follow",
|
||||||
"account.follow_back": "Follow back",
|
"account.follow_back": "Follow back",
|
||||||
|
"account.follow_back_short": "Follow back",
|
||||||
|
"account.follow_request": "Request to follow",
|
||||||
|
"account.follow_request_cancel": "Cancel request",
|
||||||
|
"account.follow_request_cancel_short": "Cancel",
|
||||||
|
"account.follow_request_short": "Request",
|
||||||
"account.followers": "Followers",
|
"account.followers": "Followers",
|
||||||
"account.followers.empty": "No one follows this user yet.",
|
"account.followers.empty": "No one follows this user yet.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
|
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
|
||||||
|
@ -70,7 +76,6 @@
|
||||||
"account.posts_with_replies": "Posts and replies",
|
"account.posts_with_replies": "Posts and replies",
|
||||||
"account.remove_from_followers": "Remove {name} from followers",
|
"account.remove_from_followers": "Remove {name} from followers",
|
||||||
"account.report": "Report @{name}",
|
"account.report": "Report @{name}",
|
||||||
"account.requested": "Awaiting approval. Click to cancel follow request",
|
|
||||||
"account.requested_follow": "{name} has requested to follow you",
|
"account.requested_follow": "{name} has requested to follow you",
|
||||||
"account.requests_to_follow_you": "Requests to follow you",
|
"account.requests_to_follow_you": "Requests to follow you",
|
||||||
"account.share": "Share @{name}'s profile",
|
"account.share": "Share @{name}'s profile",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user