mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-05 16:42:47 +00:00
Fix logged-out quote menu UX, simplify Interaction dialog copy (#36124)
This commit is contained in:
parent
943cdc5b21
commit
38fa0102c1
|
@ -46,7 +46,6 @@ export const FollowButton: React.FC<{
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'follow',
|
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
url: account?.url,
|
url: account?.url,
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,7 +109,6 @@ export const Poll: React.FC<PollProps> = ({ pollId, disabled, status }) => {
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'vote',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -72,6 +72,18 @@ export const StatusBoostButton: FC<ReblogButtonProps> = ({
|
||||||
const statusId = status.get('id') as string;
|
const statusId = status.get('id') as string;
|
||||||
const wasBoosted = !!status.get('reblogged');
|
const wasBoosted = !!status.get('reblogged');
|
||||||
|
|
||||||
|
const showLoginPrompt = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'INTERACTION',
|
||||||
|
modalProps: {
|
||||||
|
accountId: status.getIn(['account', 'id']),
|
||||||
|
url: status.get('uri'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, [dispatch, status]);
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const boostItem = boostItemState(statusState);
|
const boostItem = boostItemState(statusState);
|
||||||
const quoteItem = quoteItemState(statusState);
|
const quoteItem = quoteItemState(statusState);
|
||||||
|
@ -87,6 +99,8 @@ export const StatusBoostButton: FC<ReblogButtonProps> = ({
|
||||||
action: (event) => {
|
action: (event) => {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
dispatch(toggleReblog(statusId, event.shiftKey));
|
dispatch(toggleReblog(statusId, event.shiftKey));
|
||||||
|
} else {
|
||||||
|
showLoginPrompt();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -100,34 +114,37 @@ export const StatusBoostButton: FC<ReblogButtonProps> = ({
|
||||||
action: () => {
|
action: () => {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
dispatch(quoteComposeById(statusId));
|
dispatch(quoteComposeById(statusId));
|
||||||
|
} else {
|
||||||
|
showLoginPrompt();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] satisfies [ActionMenuItemWithIcon, ActionMenuItemWithIcon];
|
] satisfies [ActionMenuItemWithIcon, ActionMenuItemWithIcon];
|
||||||
}, [dispatch, intl, isLoggedIn, statusId, statusState, wasBoosted]);
|
}, [
|
||||||
|
dispatch,
|
||||||
|
intl,
|
||||||
|
isLoggedIn,
|
||||||
|
showLoginPrompt,
|
||||||
|
statusId,
|
||||||
|
statusState,
|
||||||
|
wasBoosted,
|
||||||
|
]);
|
||||||
|
|
||||||
const boostIcon = items[0].icon;
|
const boostIcon = items[0].icon;
|
||||||
|
|
||||||
const handleDropdownOpen = useCallback(
|
const handleDropdownOpen = useCallback(
|
||||||
(event: MouseEvent | KeyboardEvent) => {
|
(event: MouseEvent | KeyboardEvent) => {
|
||||||
if (!isLoggedIn) {
|
if (event.shiftKey) {
|
||||||
dispatch(
|
if (!isLoggedIn) {
|
||||||
openModal({
|
showLoginPrompt();
|
||||||
modalType: 'INTERACTION',
|
return false;
|
||||||
modalProps: {
|
}
|
||||||
type: 'reblog',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
|
||||||
url: status.get('uri'),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else if (event.shiftKey) {
|
|
||||||
dispatch(toggleReblog(status.get('id'), true));
|
dispatch(toggleReblog(status.get('id'), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[dispatch, isLoggedIn, status],
|
[dispatch, isLoggedIn, showLoginPrompt, status],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -223,7 +240,6 @@ export const LegacyReblogButton: FC<ReblogButtonProps> = ({
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'reblog',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -129,6 +129,7 @@ export function boostItemState({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function quoteItemState({
|
export function quoteItemState({
|
||||||
|
isLoggedIn,
|
||||||
isMine,
|
isMine,
|
||||||
isQuoteAutomaticallyAccepted,
|
isQuoteAutomaticallyAccepted,
|
||||||
isQuoteManuallyAccepted,
|
isQuoteManuallyAccepted,
|
||||||
|
@ -149,7 +150,8 @@ export function quoteItemState({
|
||||||
} else if (isQuoteManuallyAccepted) {
|
} else if (isQuoteManuallyAccepted) {
|
||||||
iconText.title = messages.request_quote;
|
iconText.title = messages.request_quote;
|
||||||
iconText.meta = messages.quote_manual_review;
|
iconText.meta = messages.quote_manual_review;
|
||||||
} else {
|
// We don't show the disabled state when logged out
|
||||||
|
} else if (isLoggedIn) {
|
||||||
iconText.disabled = true;
|
iconText.disabled = true;
|
||||||
iconText.iconComponent = FormatQuoteOff;
|
iconText.iconComponent = FormatQuoteOff;
|
||||||
iconText.meta = isQuoteFollowersOnly
|
iconText.meta = isQuoteFollowersOnly
|
||||||
|
|
|
@ -122,7 +122,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
this.props.onReply(this.props.status);
|
this.props.onReply(this.props.status);
|
||||||
} else {
|
} else {
|
||||||
this.props.onInteractionModal('reply', this.props.status);
|
this.props.onInteractionModal(this.props.status);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
this.props.onFavourite(this.props.status);
|
this.props.onFavourite(this.props.status);
|
||||||
} else {
|
} else {
|
||||||
this.props.onInteractionModal('favourite', this.props.status);
|
this.props.onInteractionModal(this.props.status);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -235,11 +235,10 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
||||||
dispatch(deployPictureInPicture({statusId: status.get('id'), accountId: status.getIn(['account', 'id']), playerType: type, props: mediaProps}));
|
dispatch(deployPictureInPicture({statusId: status.get('id'), accountId: status.getIn(['account', 'id']), playerType: type, props: mediaProps}));
|
||||||
},
|
},
|
||||||
|
|
||||||
onInteractionModal (type, status) {
|
onInteractionModal (status) {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type,
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,15 +7,9 @@ import classNames from 'classnames';
|
||||||
import { escapeRegExp } from 'lodash';
|
import { escapeRegExp } from 'lodash';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
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 ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
|
||||||
import StarIcon from '@/material-icons/400-24px/star.svg?react';
|
|
||||||
import { openModal, closeModal } from 'mastodon/actions/modal';
|
import { openModal, closeModal } from 'mastodon/actions/modal';
|
||||||
import { apiRequest } from 'mastodon/api';
|
import { apiRequest } from 'mastodon/api';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
|
||||||
import {
|
import {
|
||||||
domain as localDomain,
|
domain as localDomain,
|
||||||
registrationsOpen,
|
registrationsOpen,
|
||||||
|
@ -408,8 +402,7 @@ const LoginForm: React.FC<{
|
||||||
const InteractionModal: React.FC<{
|
const InteractionModal: React.FC<{
|
||||||
accountId: string;
|
accountId: string;
|
||||||
url: string;
|
url: string;
|
||||||
type: 'reply' | 'reblog' | 'favourite' | 'follow' | 'vote';
|
}> = ({ accountId, url }) => {
|
||||||
}> = ({ accountId, url, type }) => {
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const displayNameHtml = useAppSelector(
|
const displayNameHtml = useAppSelector(
|
||||||
(state) => state.accounts.get(accountId)?.display_name_html ?? '',
|
(state) => state.accounts.get(accountId)?.display_name_html ?? '',
|
||||||
|
@ -437,93 +430,6 @@ const InteractionModal: React.FC<{
|
||||||
);
|
);
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
let title: React.ReactNode,
|
|
||||||
icon: React.ReactNode,
|
|
||||||
actionPrompt: React.ReactNode;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'reply':
|
|
||||||
icon = <Icon id='reply' icon={ReplyIcon} />;
|
|
||||||
title = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.title.reply'
|
|
||||||
defaultMessage="Reply to {name}'s post"
|
|
||||||
values={{ name }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
actionPrompt = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.action.reply'
|
|
||||||
defaultMessage='To continue, you need to reply from your account.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'reblog':
|
|
||||||
icon = <Icon id='retweet' icon={RepeatIcon} />;
|
|
||||||
title = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.title.reblog'
|
|
||||||
defaultMessage="Boost {name}'s post"
|
|
||||||
values={{ name }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
actionPrompt = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.action.reblog'
|
|
||||||
defaultMessage='To continue, you need to reblog from your account.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'favourite':
|
|
||||||
icon = <Icon id='star' icon={StarIcon} />;
|
|
||||||
title = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.title.favourite'
|
|
||||||
defaultMessage="Favorite {name}'s post"
|
|
||||||
values={{ name }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
actionPrompt = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.action.favourite'
|
|
||||||
defaultMessage='To continue, you need to favorite from your account.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'follow':
|
|
||||||
icon = <Icon id='user-plus' icon={PersonAddIcon} />;
|
|
||||||
title = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.title.follow'
|
|
||||||
defaultMessage='Follow {name}'
|
|
||||||
values={{ name }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
actionPrompt = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.action.follow'
|
|
||||||
defaultMessage='To continue, you need to follow from your account.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'vote':
|
|
||||||
icon = <Icon id='tasks' icon={InsertChartIcon} />;
|
|
||||||
title = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.title.vote'
|
|
||||||
defaultMessage="Vote in {name}'s poll"
|
|
||||||
values={{ name }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
actionPrompt = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='interaction_modal.action.vote'
|
|
||||||
defaultMessage='To continue, you need to vote from your account.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let signupButton;
|
let signupButton;
|
||||||
|
|
||||||
if (sso_redirect) {
|
if (sso_redirect) {
|
||||||
|
@ -559,9 +465,18 @@ const InteractionModal: React.FC<{
|
||||||
<div className='modal-root__modal interaction-modal'>
|
<div className='modal-root__modal interaction-modal'>
|
||||||
<div className='interaction-modal__lead'>
|
<div className='interaction-modal__lead'>
|
||||||
<h3>
|
<h3>
|
||||||
<span className='interaction-modal__icon'>{icon}</span> {title}
|
<FormattedMessage
|
||||||
|
id='interaction_modal.title'
|
||||||
|
defaultMessage='Sign in to continue'
|
||||||
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<p>{actionPrompt}</p>
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id='interaction_modal.action'
|
||||||
|
defaultMessage="To interact with {name}'s post, you need to sign into your account on whatever Mastodon server you use."
|
||||||
|
values={{ name }}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LoginForm resourceUrl={url} />
|
<LoginForm resourceUrl={url} />
|
||||||
|
|
|
@ -92,7 +92,6 @@ export const Footer: React.FC<{
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'reply',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
@ -113,7 +112,6 @@ export const Footer: React.FC<{
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'favourite',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
@ -135,7 +133,6 @@ export const Footer: React.FC<{
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'reblog',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -186,7 +186,6 @@ class Status extends ImmutablePureComponent {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'favourite',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
@ -216,7 +215,6 @@ class Status extends ImmutablePureComponent {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'reply',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
@ -234,7 +232,6 @@ class Status extends ImmutablePureComponent {
|
||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'INTERACTION',
|
modalType: 'INTERACTION',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
type: 'reblog',
|
|
||||||
accountId: status.getIn(['account', 'id']),
|
accountId: status.getIn(['account', 'id']),
|
||||||
url: status.get('uri'),
|
url: status.get('uri'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -453,20 +453,12 @@
|
||||||
"ignore_notifications_modal.private_mentions_title": "Ignore notifications from unsolicited Private Mentions?",
|
"ignore_notifications_modal.private_mentions_title": "Ignore notifications from unsolicited Private Mentions?",
|
||||||
"info_button.label": "Help",
|
"info_button.label": "Help",
|
||||||
"info_button.what_is_alt_text": "<h1>What is alt text?</h1> <p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p> <p>You can improve accessibility and understanding for everyone by writing clear, concise, and objective alt text.</p> <ul> <li>Capture important elements</li> <li>Summarize text in images</li> <li>Use regular sentence structure</li> <li>Avoid redundant information</li> <li>Focus on trends and key findings in complex visuals (like diagrams or maps)</li> </ul>",
|
"info_button.what_is_alt_text": "<h1>What is alt text?</h1> <p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p> <p>You can improve accessibility and understanding for everyone by writing clear, concise, and objective alt text.</p> <ul> <li>Capture important elements</li> <li>Summarize text in images</li> <li>Use regular sentence structure</li> <li>Avoid redundant information</li> <li>Focus on trends and key findings in complex visuals (like diagrams or maps)</li> </ul>",
|
||||||
"interaction_modal.action.favourite": "To continue, you need to favorite from your account.",
|
"interaction_modal.action": "To interact with {name}'s post, you need to sign into your account on whatever Mastodon server you use.",
|
||||||
"interaction_modal.action.follow": "To continue, you need to follow from your account.",
|
|
||||||
"interaction_modal.action.reblog": "To continue, you need to reblog from your account.",
|
|
||||||
"interaction_modal.action.reply": "To continue, you need to reply from your account.",
|
|
||||||
"interaction_modal.action.vote": "To continue, you need to vote from your account.",
|
|
||||||
"interaction_modal.go": "Go",
|
"interaction_modal.go": "Go",
|
||||||
"interaction_modal.no_account_yet": "Don't have an account yet?",
|
"interaction_modal.no_account_yet": "Don't have an account yet?",
|
||||||
"interaction_modal.on_another_server": "On a different server",
|
"interaction_modal.on_another_server": "On a different server",
|
||||||
"interaction_modal.on_this_server": "On this server",
|
"interaction_modal.on_this_server": "On this server",
|
||||||
"interaction_modal.title.favourite": "Favorite {name}'s post",
|
"interaction_modal.title": "Sign in to continue",
|
||||||
"interaction_modal.title.follow": "Follow {name}",
|
|
||||||
"interaction_modal.title.reblog": "Boost {name}'s post",
|
|
||||||
"interaction_modal.title.reply": "Reply to {name}'s post",
|
|
||||||
"interaction_modal.title.vote": "Vote in {name}'s poll",
|
|
||||||
"interaction_modal.username_prompt": "E.g. {example}",
|
"interaction_modal.username_prompt": "E.g. {example}",
|
||||||
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
|
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
|
||||||
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
|
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
|
||||||
|
|
|
@ -9533,13 +9533,6 @@ noscript {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
|
||||||
color: $highlight-text-color;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__lead {
|
&__lead {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user