mirror of
https://github.com/mastodon/mastodon.git
synced 2025-02-06 15:05:07 +00:00
Add reminder when about to post without alt text in web UI (#33760)
This commit is contained in:
parent
2beab34ca4
commit
1e70da5e3c
|
@ -10,6 +10,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { length } from 'stringz';
|
import { length } from 'stringz';
|
||||||
|
|
||||||
|
import { missingAltTextModal } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import AutosuggestInput from '../../../components/autosuggest_input';
|
import AutosuggestInput from '../../../components/autosuggest_input';
|
||||||
import AutosuggestTextarea from '../../../components/autosuggest_textarea';
|
import AutosuggestTextarea from '../../../components/autosuggest_textarea';
|
||||||
import { Button } from '../../../components/button';
|
import { Button } from '../../../components/button';
|
||||||
|
@ -65,6 +67,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
withoutNavigation: PropTypes.bool,
|
withoutNavigation: PropTypes.bool,
|
||||||
anyMedia: PropTypes.bool,
|
anyMedia: PropTypes.bool,
|
||||||
|
missingAltText: PropTypes.bool,
|
||||||
isInReply: PropTypes.bool,
|
isInReply: PropTypes.bool,
|
||||||
singleColumn: PropTypes.bool,
|
singleColumn: PropTypes.bool,
|
||||||
lang: PropTypes.string,
|
lang: PropTypes.string,
|
||||||
|
@ -117,7 +120,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onSubmit();
|
this.props.onSubmit(missingAltTextModal && this.props.missingAltText);
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -9,7 +9,9 @@ import {
|
||||||
changeComposeSpoilerText,
|
changeComposeSpoilerText,
|
||||||
insertEmojiCompose,
|
insertEmojiCompose,
|
||||||
uploadCompose,
|
uploadCompose,
|
||||||
} from '../../../actions/compose';
|
} from 'mastodon/actions/compose';
|
||||||
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
|
||||||
import ComposeForm from '../components/compose_form';
|
import ComposeForm from '../components/compose_form';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
@ -26,6 +28,7 @@ const mapStateToProps = state => ({
|
||||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||||
|
missingAltText: state.getIn(['compose', 'media_attachments']).some(media => ['image', 'gifv'].includes(media.get('type')) && (media.get('description') ?? '').length === 0),
|
||||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||||
lang: state.getIn(['compose', 'language']),
|
lang: state.getIn(['compose', 'language']),
|
||||||
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
||||||
|
@ -37,8 +40,15 @@ const mapDispatchToProps = (dispatch) => ({
|
||||||
dispatch(changeCompose(text));
|
dispatch(changeCompose(text));
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmit () {
|
onSubmit (missingAltText) {
|
||||||
dispatch(submitCompose());
|
if (missingAltText) {
|
||||||
|
dispatch(openModal({
|
||||||
|
modalType: 'CONFIRM_MISSING_ALT_TEXT',
|
||||||
|
modalProps: {},
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(submitCompose());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onClearSuggestions () {
|
onClearSuggestions () {
|
||||||
|
|
|
@ -56,14 +56,6 @@ export const ConfirmationModal: React.FC<
|
||||||
|
|
||||||
<div className='safety-action-modal__bottom'>
|
<div className='safety-action-modal__bottom'>
|
||||||
<div className='safety-action-modal__actions'>
|
<div className='safety-action-modal__actions'>
|
||||||
{secondary && (
|
|
||||||
<>
|
|
||||||
<Button onClick={handleSecondary}>{secondary}</Button>
|
|
||||||
|
|
||||||
<div className='spacer' />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<button onClick={handleCancel} className='link-button'>
|
<button onClick={handleCancel} className='link-button'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='confirmation_modal.cancel'
|
id='confirmation_modal.cancel'
|
||||||
|
@ -71,6 +63,15 @@ export const ConfirmationModal: React.FC<
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{secondary && (
|
||||||
|
<>
|
||||||
|
<div className='spacer' />
|
||||||
|
<button onClick={handleSecondary} className='link-button'>
|
||||||
|
{secondary}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
|
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
|
||||||
<Button onClick={handleClick} autoFocus>
|
<Button onClick={handleClick} autoFocus>
|
||||||
{confirm}
|
{confirm}
|
||||||
|
|
|
@ -7,3 +7,4 @@ export { ConfirmUnfollowModal } from './unfollow';
|
||||||
export { ConfirmClearNotificationsModal } from './clear_notifications';
|
export { ConfirmClearNotificationsModal } from './clear_notifications';
|
||||||
export { ConfirmLogOutModal } from './log_out';
|
export { ConfirmLogOutModal } from './log_out';
|
||||||
export { ConfirmFollowToListModal } from './follow_to_list';
|
export { ConfirmFollowToListModal } from './follow_to_list';
|
||||||
|
export { ConfirmMissingAltTextModal } from './missing_alt_text';
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
|
import { submitCompose } from 'mastodon/actions/compose';
|
||||||
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
import type { MediaAttachment } from 'mastodon/models/media_attachment';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
|
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||||
|
import { ConfirmationModal } from './confirmation_modal';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: {
|
||||||
|
id: 'confirmations.missing_alt_text.title',
|
||||||
|
defaultMessage: 'Add alt text?',
|
||||||
|
},
|
||||||
|
confirm: {
|
||||||
|
id: 'confirmations.missing_alt_text.confirm',
|
||||||
|
defaultMessage: 'Add alt text',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
id: 'confirmations.missing_alt_text.message',
|
||||||
|
defaultMessage:
|
||||||
|
'Your post contains media without alt text. Adding descriptions helps make your content accessible to more people.',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
id: 'confirmations.missing_alt_text.secondary',
|
||||||
|
defaultMessage: 'Post anyway',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ConfirmMissingAltTextModal: React.FC<
|
||||||
|
BaseConfirmationModalProps
|
||||||
|
> = ({ onClose }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const mediaId = useAppSelector(
|
||||||
|
(state) =>
|
||||||
|
(
|
||||||
|
(state.compose as ImmutableMap<string, unknown>).get(
|
||||||
|
'media_attachments',
|
||||||
|
) as ImmutableList<MediaAttachment>
|
||||||
|
)
|
||||||
|
.find(
|
||||||
|
(media) =>
|
||||||
|
['image', 'gifv'].includes(media.get('type') as string) &&
|
||||||
|
((media.get('description') ?? '') as string).length === 0,
|
||||||
|
)
|
||||||
|
?.get('id') as string,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleConfirm = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
openModal({
|
||||||
|
modalType: 'FOCAL_POINT',
|
||||||
|
modalProps: {
|
||||||
|
mediaId,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, [dispatch, mediaId]);
|
||||||
|
|
||||||
|
const handleSecondary = useCallback(() => {
|
||||||
|
dispatch(submitCompose());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationModal
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
message={intl.formatMessage(messages.message)}
|
||||||
|
confirm={intl.formatMessage(messages.confirm)}
|
||||||
|
secondary={intl.formatMessage(messages.secondary)}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
onSecondary={handleSecondary}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -37,6 +37,7 @@ import {
|
||||||
ConfirmClearNotificationsModal,
|
ConfirmClearNotificationsModal,
|
||||||
ConfirmLogOutModal,
|
ConfirmLogOutModal,
|
||||||
ConfirmFollowToListModal,
|
ConfirmFollowToListModal,
|
||||||
|
ConfirmMissingAltTextModal,
|
||||||
} from './confirmation_modals';
|
} from './confirmation_modals';
|
||||||
import ImageModal from './image_modal';
|
import ImageModal from './image_modal';
|
||||||
import MediaModal from './media_modal';
|
import MediaModal from './media_modal';
|
||||||
|
@ -58,6 +59,7 @@ export const MODAL_COMPONENTS = {
|
||||||
'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }),
|
'CONFIRM_CLEAR_NOTIFICATIONS': () => Promise.resolve({ default: ConfirmClearNotificationsModal }),
|
||||||
'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }),
|
'CONFIRM_LOG_OUT': () => Promise.resolve({ default: ConfirmLogOutModal }),
|
||||||
'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }),
|
'CONFIRM_FOLLOW_TO_LIST': () => Promise.resolve({ default: ConfirmFollowToListModal }),
|
||||||
|
'CONFIRM_MISSING_ALT_TEXT': () => Promise.resolve({ default: ConfirmMissingAltTextModal }),
|
||||||
'MUTE': MuteModal,
|
'MUTE': MuteModal,
|
||||||
'BLOCK': BlockModal,
|
'BLOCK': BlockModal,
|
||||||
'DOMAIN_BLOCK': DomainBlockModal,
|
'DOMAIN_BLOCK': DomainBlockModal,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
* @property {string} admin
|
* @property {string} admin
|
||||||
* @property {boolean=} boost_modal
|
* @property {boolean=} boost_modal
|
||||||
* @property {boolean=} delete_modal
|
* @property {boolean=} delete_modal
|
||||||
|
* @property {boolean=} missing_alt_text_modal
|
||||||
* @property {boolean=} disable_swiping
|
* @property {boolean=} disable_swiping
|
||||||
* @property {boolean=} disable_hover_cards
|
* @property {boolean=} disable_hover_cards
|
||||||
* @property {string=} disabled_account_id
|
* @property {string=} disabled_account_id
|
||||||
|
@ -88,6 +89,7 @@ export const activityApiEnabled = getMeta('activity_api_enabled');
|
||||||
export const autoPlayGif = getMeta('auto_play_gif');
|
export const autoPlayGif = getMeta('auto_play_gif');
|
||||||
export const boostModal = getMeta('boost_modal');
|
export const boostModal = getMeta('boost_modal');
|
||||||
export const deleteModal = getMeta('delete_modal');
|
export const deleteModal = getMeta('delete_modal');
|
||||||
|
export const missingAltTextModal = getMeta('missing_alt_text_modal');
|
||||||
export const disableSwiping = getMeta('disable_swiping');
|
export const disableSwiping = getMeta('disable_swiping');
|
||||||
export const disableHoverCards = getMeta('disable_hover_cards');
|
export const disableHoverCards = getMeta('disable_hover_cards');
|
||||||
export const disabledAccountId = getMeta('disabled_account_id');
|
export const disabledAccountId = getMeta('disabled_account_id');
|
||||||
|
|
|
@ -218,6 +218,10 @@
|
||||||
"confirmations.logout.confirm": "Log out",
|
"confirmations.logout.confirm": "Log out",
|
||||||
"confirmations.logout.message": "Are you sure you want to log out?",
|
"confirmations.logout.message": "Are you sure you want to log out?",
|
||||||
"confirmations.logout.title": "Log out?",
|
"confirmations.logout.title": "Log out?",
|
||||||
|
"confirmations.missing_alt_text.confirm": "Add alt text",
|
||||||
|
"confirmations.missing_alt_text.message": "Your post contains media without alt text. Adding descriptions helps make your content accessible to more people.",
|
||||||
|
"confirmations.missing_alt_text.secondary": "Post anyway",
|
||||||
|
"confirmations.missing_alt_text.title": "Add alt text?",
|
||||||
"confirmations.mute.confirm": "Mute",
|
"confirmations.mute.confirm": "Mute",
|
||||||
"confirmations.redraft.confirm": "Delete & redraft",
|
"confirmations.redraft.confirm": "Delete & redraft",
|
||||||
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.",
|
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.",
|
||||||
|
|
|
@ -29,6 +29,7 @@ class UserSettings
|
||||||
setting :disable_hover_cards, default: false
|
setting :disable_hover_cards, default: false
|
||||||
setting :delete_modal, default: true
|
setting :delete_modal, default: true
|
||||||
setting :reblog_modal, default: false
|
setting :reblog_modal, default: false
|
||||||
|
setting :missing_alt_text_modal, default: true
|
||||||
setting :reduce_motion, default: false
|
setting :reduce_motion, default: false
|
||||||
setting :expand_content_warnings, default: false
|
setting :expand_content_warnings, default: false
|
||||||
setting :display_media, default: 'default', in: %w(default show_all hide_all)
|
setting :display_media, default: 'default', in: %w(default show_all hide_all)
|
||||||
|
|
|
@ -12,13 +12,14 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||||
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||||
has_one :role, serializer: REST::RoleSerializer
|
has_one :role, serializer: REST::RoleSerializer
|
||||||
|
|
||||||
def meta
|
def meta # rubocop:disable Metrics/AbcSize
|
||||||
store = default_meta_store
|
store = default_meta_store
|
||||||
|
|
||||||
if object.current_account
|
if object.current_account
|
||||||
store[:me] = object.current_account.id.to_s
|
store[:me] = object.current_account.id.to_s
|
||||||
store[:boost_modal] = object_account_user.setting_boost_modal
|
store[:boost_modal] = object_account_user.setting_boost_modal
|
||||||
store[:delete_modal] = object_account_user.setting_delete_modal
|
store[:delete_modal] = object_account_user.setting_delete_modal
|
||||||
|
store[:missing_alt_text_modal] = object_account_user.settings['web.missing_alt_text_modal']
|
||||||
store[:auto_play_gif] = object_account_user.setting_auto_play_gif
|
store[:auto_play_gif] = object_account_user.setting_auto_play_gif
|
||||||
store[:display_media] = object_account_user.setting_display_media
|
store[:display_media] = object_account_user.setting_display_media
|
||||||
store[:expand_spoilers] = object_account_user.setting_expand_spoilers
|
store[:expand_spoilers] = object_account_user.setting_expand_spoilers
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
.fields-group
|
.fields-group
|
||||||
= ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
|
= ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
|
||||||
= ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
|
= ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
|
||||||
|
= ff.input :'web.missing_alt_text_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_missing_alt_text_modal')
|
||||||
|
|
||||||
%h4= t 'appearance.sensitive_content'
|
%h4= t 'appearance.sensitive_content'
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,7 @@ en:
|
||||||
setting_display_media_show_all: Show all
|
setting_display_media_show_all: Show all
|
||||||
setting_expand_spoilers: Always expand posts marked with content warnings
|
setting_expand_spoilers: Always expand posts marked with content warnings
|
||||||
setting_hide_network: Hide your social graph
|
setting_hide_network: Hide your social graph
|
||||||
|
setting_missing_alt_text_modal: Show confirmation dialog before posting media without alt text
|
||||||
setting_reduce_motion: Reduce motion in animations
|
setting_reduce_motion: Reduce motion in animations
|
||||||
setting_system_font_ui: Use system's default font
|
setting_system_font_ui: Use system's default font
|
||||||
setting_system_scrollbars_ui: Use system's default scrollbar
|
setting_system_scrollbars_ui: Use system's default scrollbar
|
||||||
|
|
Loading…
Reference in New Issue
Block a user