Change design of confirmation modals in web UI (#30884)
Co-authored-by: Renaud Chaput <renchap@gmail.com>
This commit is contained in:
parent
ff6d2ec343
commit
8818748b90
31 changed files with 554 additions and 489 deletions
|
@ -0,0 +1,46 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { clearNotifications } from 'mastodon/actions/notification_groups';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
clearTitle: {
|
||||
id: 'notifications.clear_title',
|
||||
defaultMessage: 'Clear notifications?',
|
||||
},
|
||||
clearMessage: {
|
||||
id: 'notifications.clear_confirmation',
|
||||
defaultMessage:
|
||||
'Are you sure you want to permanently clear all your notifications?',
|
||||
},
|
||||
clearConfirm: {
|
||||
id: 'notifications.clear',
|
||||
defaultMessage: 'Clear notifications',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmClearNotificationsModal: React.FC<
|
||||
BaseConfirmationModalProps
|
||||
> = ({ onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
void dispatch(clearNotifications());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.clearTitle)}
|
||||
message={intl.formatMessage(messages.clearMessage)}
|
||||
confirm={intl.formatMessage(messages.clearConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,79 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button } from 'mastodon/components/button';
|
||||
|
||||
export interface BaseConfirmationModalProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const ConfirmationModal: React.FC<
|
||||
{
|
||||
title: React.ReactNode;
|
||||
message: React.ReactNode;
|
||||
confirm: React.ReactNode;
|
||||
secondary?: React.ReactNode;
|
||||
onSecondary?: () => void;
|
||||
onConfirm: () => void;
|
||||
closeWhenConfirm?: boolean;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({
|
||||
title,
|
||||
message,
|
||||
confirm,
|
||||
onClose,
|
||||
onConfirm,
|
||||
secondary,
|
||||
onSecondary,
|
||||
closeWhenConfirm = true,
|
||||
}) => {
|
||||
const handleClick = useCallback(() => {
|
||||
if (closeWhenConfirm) {
|
||||
onClose();
|
||||
}
|
||||
|
||||
onConfirm();
|
||||
}, [onClose, onConfirm, closeWhenConfirm]);
|
||||
|
||||
const handleSecondary = useCallback(() => {
|
||||
onClose();
|
||||
onSecondary?.();
|
||||
}, [onClose, onSecondary]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal safety-action-modal'>
|
||||
<div className='safety-action-modal__top'>
|
||||
<div className='safety-action-modal__confirmation'>
|
||||
<h1>{title}</h1>
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='safety-action-modal__bottom'>
|
||||
<div className='safety-action-modal__actions'>
|
||||
{secondary && (
|
||||
<>
|
||||
<Button onClick={handleSecondary}>{secondary}</Button>
|
||||
|
||||
<div className='spacer' />
|
||||
</>
|
||||
)}
|
||||
|
||||
<button onClick={handleCancel} className='link-button'>
|
||||
<FormattedMessage
|
||||
id='confirmation_modal.cancel'
|
||||
defaultMessage='Cancel'
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Button onClick={handleClick}>{confirm}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { useHistory } from 'react-router';
|
||||
|
||||
import { removeColumn } from 'mastodon/actions/columns';
|
||||
import { deleteList } from 'mastodon/actions/lists';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteListTitle: {
|
||||
id: 'confirmations.delete_list.title',
|
||||
defaultMessage: 'Delete list?',
|
||||
},
|
||||
deleteListMessage: {
|
||||
id: 'confirmations.delete_list.message',
|
||||
defaultMessage: 'Are you sure you want to permanently delete this list?',
|
||||
},
|
||||
deleteListConfirm: {
|
||||
id: 'confirmations.delete_list.confirm',
|
||||
defaultMessage: 'Delete',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmDeleteListModal: React.FC<
|
||||
{
|
||||
listId: string;
|
||||
columnId: string;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ listId, columnId, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
dispatch(deleteList(listId));
|
||||
|
||||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
history.push('/lists');
|
||||
}
|
||||
}, [dispatch, history, columnId, listId]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.deleteListTitle)}
|
||||
message={intl.formatMessage(messages.deleteListMessage)}
|
||||
confirm={intl.formatMessage(messages.deleteListConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { deleteStatus } from 'mastodon/actions/statuses';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteAndRedraftTitle: {
|
||||
id: 'confirmations.redraft.title',
|
||||
defaultMessage: 'Delete & redraft post?',
|
||||
},
|
||||
deleteAndRedraftMessage: {
|
||||
id: 'confirmations.redraft.message',
|
||||
defaultMessage:
|
||||
'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.',
|
||||
},
|
||||
deleteAndRedraftConfirm: {
|
||||
id: 'confirmations.redraft.confirm',
|
||||
defaultMessage: 'Delete & redraft',
|
||||
},
|
||||
deleteTitle: {
|
||||
id: 'confirmations.delete.title',
|
||||
defaultMessage: 'Delete post?',
|
||||
},
|
||||
deleteMessage: {
|
||||
id: 'confirmations.delete.message',
|
||||
defaultMessage: 'Are you sure you want to delete this status?',
|
||||
},
|
||||
deleteConfirm: {
|
||||
id: 'confirmations.delete.confirm',
|
||||
defaultMessage: 'Delete',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmDeleteStatusModal: React.FC<
|
||||
{
|
||||
statusId: string;
|
||||
withRedraft: boolean;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ statusId, withRedraft, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
dispatch(deleteStatus(statusId, withRedraft));
|
||||
}, [dispatch, statusId, withRedraft]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(
|
||||
withRedraft ? messages.deleteAndRedraftTitle : messages.deleteTitle,
|
||||
)}
|
||||
message={intl.formatMessage(
|
||||
withRedraft ? messages.deleteAndRedraftMessage : messages.deleteMessage,
|
||||
)}
|
||||
confirm={intl.formatMessage(
|
||||
withRedraft ? messages.deleteAndRedraftConfirm : messages.deleteConfirm,
|
||||
)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { editStatus } from 'mastodon/actions/statuses';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
editTitle: {
|
||||
id: 'confirmations.edit.title',
|
||||
defaultMessage: 'Overwrite post?',
|
||||
},
|
||||
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
|
||||
editMessage: {
|
||||
id: 'confirmations.edit.message',
|
||||
defaultMessage:
|
||||
'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmEditStatusModal: React.FC<
|
||||
{
|
||||
statusId: string;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ statusId, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
dispatch(editStatus(statusId));
|
||||
}, [dispatch, statusId]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.editTitle)}
|
||||
message={intl.formatMessage(messages.editMessage)}
|
||||
confirm={intl.formatMessage(messages.editConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
export { ConfirmationModal } from './confirmation_modal';
|
||||
export { ConfirmDeleteStatusModal } from './delete_status';
|
||||
export { ConfirmDeleteListModal } from './delete_list';
|
||||
export { ConfirmReplyModal } from './reply';
|
||||
export { ConfirmEditStatusModal } from './edit_status';
|
||||
export { ConfirmUnfollowModal } from './unfollow';
|
||||
export { ConfirmClearNotificationsModal } from './clear_notifications';
|
||||
export { ConfirmLogOutModal } from './log_out';
|
|
@ -0,0 +1,40 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
logoutTitle: { id: 'confirmations.logout.title', defaultMessage: 'Log out?' },
|
||||
logoutMessage: {
|
||||
id: 'confirmations.logout.message',
|
||||
defaultMessage: 'Are you sure you want to log out?',
|
||||
},
|
||||
logoutConfirm: {
|
||||
id: 'confirmations.logout.confirm',
|
||||
defaultMessage: 'Log out',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmLogOutModal: React.FC<BaseConfirmationModalProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
logOut();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.logoutTitle)}
|
||||
message={intl.formatMessage(messages.logoutMessage)}
|
||||
confirm={intl.formatMessage(messages.logoutConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { replyCompose } from 'mastodon/actions/compose';
|
||||
import type { Status } from 'mastodon/models/status';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
replyTitle: {
|
||||
id: 'confirmations.reply.title',
|
||||
defaultMessage: 'Overwrite post?',
|
||||
},
|
||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: {
|
||||
id: 'confirmations.reply.message',
|
||||
defaultMessage:
|
||||
'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmReplyModal: React.FC<
|
||||
{
|
||||
status: Status;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ status, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
dispatch(replyCompose(status));
|
||||
}, [dispatch, status]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.replyTitle)}
|
||||
message={intl.formatMessage(messages.replyMessage)}
|
||||
confirm={intl.formatMessage(messages.replyConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { unfollowAccount } from 'mastodon/actions/accounts';
|
||||
import type { Account } from 'mastodon/models/account';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
import { ConfirmationModal } from './confirmation_modal';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollowTitle: {
|
||||
id: 'confirmations.unfollow.title',
|
||||
defaultMessage: 'Unfollow user?',
|
||||
},
|
||||
unfollowConfirm: {
|
||||
id: 'confirmations.unfollow.confirm',
|
||||
defaultMessage: 'Unfollow',
|
||||
},
|
||||
});
|
||||
|
||||
export const ConfirmUnfollowModal: React.FC<
|
||||
{
|
||||
account: Account;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ account, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onConfirm = useCallback(() => {
|
||||
dispatch(unfollowAccount(account.id));
|
||||
}, [dispatch, account.id]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.unfollowTitle)}
|
||||
message={
|
||||
<FormattedMessage
|
||||
id='confirmations.unfollow.message'
|
||||
defaultMessage='Are you sure you want to unfollow {name}?'
|
||||
values={{ name: <strong>@{account.acct}</strong> }}
|
||||
/>
|
||||
}
|
||||
confirm={intl.formatMessage(messages.unfollowConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue