kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Compose: overhaul Spoiler feature
rodzic
9ea0b9cdbe
commit
f51f2984a5
|
@ -45,7 +45,7 @@ const textAtCursorMatchesToken = (str: string, caretPosition: number, searchToke
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IAutosuggestInput extends Pick<React.HTMLAttributes<HTMLInputElement>, 'onChange' | 'onKeyUp' | 'onKeyDown'> {
|
export interface IAutosuggestInput extends Pick<React.HTMLAttributes<HTMLInputElement>, 'onChange' | 'onKeyUp' | 'onKeyDown'> {
|
||||||
value: string,
|
value: string,
|
||||||
suggestions: ImmutableList<any>,
|
suggestions: ImmutableList<any>,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
clearComposeSuggestions,
|
clearComposeSuggestions,
|
||||||
fetchComposeSuggestions,
|
fetchComposeSuggestions,
|
||||||
selectComposeSuggestion,
|
selectComposeSuggestion,
|
||||||
changeComposeSpoilerText,
|
|
||||||
insertEmojiCompose,
|
insertEmojiCompose,
|
||||||
uploadCompose,
|
uploadCompose,
|
||||||
} from 'soapbox/actions/compose';
|
} from 'soapbox/actions/compose';
|
||||||
|
@ -38,6 +37,7 @@ import UploadButtonContainer from '../containers/upload_button_container';
|
||||||
import WarningContainer from '../containers/warning_container';
|
import WarningContainer from '../containers/warning_container';
|
||||||
import { countableText } from '../util/counter';
|
import { countableText } from '../util/counter';
|
||||||
|
|
||||||
|
import SpoilerInput from './spoiler-input';
|
||||||
import TextCharacterCounter from './text_character_counter';
|
import TextCharacterCounter from './text_character_counter';
|
||||||
import VisualCharacterCounter from './visual_character_counter';
|
import VisualCharacterCounter from './visual_character_counter';
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u20
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What\'s on your mind?' },
|
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What\'s on your mind?' },
|
||||||
pollPlaceholder: { id: 'compose_form.poll_placeholder', defaultMessage: 'Add a poll topic...' },
|
pollPlaceholder: { id: 'compose_form.poll_placeholder', defaultMessage: 'Add a poll topic...' },
|
||||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
|
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here (optional)' },
|
||||||
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
|
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
|
||||||
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||||
message: { id: 'compose_form.message', defaultMessage: 'Message' },
|
message: { id: 'compose_form.message', defaultMessage: 'Message' },
|
||||||
|
@ -165,10 +165,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||||
dispatch(selectComposeSuggestion(id, tokenStart, token, value, ['spoiler_text']));
|
dispatch(selectComposeSuggestion(id, tokenStart, token, value, ['spoiler_text']));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeSpoilerText: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
||||||
dispatch(changeComposeSpoilerText(id, e.target.value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCursor = (start: number, end: number = start) => {
|
const setCursor = (start: number, end: number = start) => {
|
||||||
if (!autosuggestTextareaRef.current?.textarea) return;
|
if (!autosuggestTextareaRef.current?.textarea) return;
|
||||||
autosuggestTextareaRef.current.textarea.setSelectionRange(start, end);
|
autosuggestTextareaRef.current.textarea.setSelectionRange(start, end);
|
||||||
|
@ -265,7 +261,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className='w-full' space={1} ref={formRef} onClick={handleClick}>
|
<Stack className='w-full' space={4} ref={formRef} onClick={handleClick}>
|
||||||
{scheduledStatusCount > 0 && (
|
{scheduledStatusCount > 0 && (
|
||||||
<Warning
|
<Warning
|
||||||
message={(
|
message={(
|
||||||
|
@ -291,30 +287,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||||
|
|
||||||
{!shouldCondense && <ReplyMentions composeId={id} />}
|
{!shouldCondense && <ReplyMentions composeId={id} />}
|
||||||
|
|
||||||
<div
|
|
||||||
className={classNames({
|
|
||||||
'relative transition-height': true,
|
|
||||||
'hidden': !spoiler,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<AutosuggestInput
|
|
||||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
|
||||||
value={spoilerText}
|
|
||||||
onChange={handleChangeSpoilerText}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
disabled={!spoiler}
|
|
||||||
ref={spoilerTextRef}
|
|
||||||
suggestions={suggestions}
|
|
||||||
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
|
||||||
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
|
||||||
onSuggestionSelected={onSpoilerSuggestionSelected}
|
|
||||||
searchTokens={[':']}
|
|
||||||
id='cw-spoiler-input'
|
|
||||||
className='border-none shadow-none px-0 py-2 text-base'
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AutosuggestTextarea
|
<AutosuggestTextarea
|
||||||
ref={(isModalOpen && shouldCondense) ? undefined : autosuggestTextareaRef}
|
ref={(isModalOpen && shouldCondense) ? undefined : autosuggestTextareaRef}
|
||||||
placeholder={intl.formatMessage(hasPoll ? messages.pollPlaceholder : messages.placeholder)}
|
placeholder={intl.formatMessage(hasPoll ? messages.pollPlaceholder : messages.placeholder)}
|
||||||
|
@ -334,11 +306,18 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
!condensed &&
|
!condensed &&
|
||||||
<div className='compose-form__modifiers'>
|
<Stack space={4} className='compose-form__modifiers'>
|
||||||
<UploadForm composeId={id} />
|
<UploadForm composeId={id} />
|
||||||
<PollForm composeId={id} />
|
<PollForm composeId={id} />
|
||||||
<ScheduleFormContainer composeId={id} />
|
<ScheduleFormContainer composeId={id} />
|
||||||
</div>
|
|
||||||
|
<SpoilerInput
|
||||||
|
composeId={id}
|
||||||
|
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||||
|
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||||
|
onSuggestionSelected={onSpoilerSuggestionSelected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
}
|
}
|
||||||
</AutosuggestTextarea>
|
</AutosuggestTextarea>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import classNames from 'clsx';
|
||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { changeComposeSpoilerness, changeComposeSpoilerText } from 'soapbox/actions/compose';
|
||||||
|
import AutosuggestInput, { IAutosuggestInput } from 'soapbox/components/autosuggest_input';
|
||||||
|
import { Divider, Stack, Text } from 'soapbox/components/ui';
|
||||||
|
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'compose_form.spoiler_title', defaultMessage: 'Sensitive content' },
|
||||||
|
placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here (optional)' },
|
||||||
|
remove: { id: 'compose_form.spoiler_remove', defaultMessage: 'Remove sensitive' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ISpoilerInput extends Pick<IAutosuggestInput, 'onSuggestionsFetchRequested' | 'onSuggestionsClearRequested' | 'onSuggestionSelected'> {
|
||||||
|
composeId: string extends 'default' ? never : string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Text input for content warning in composer. */
|
||||||
|
const SpoilerInput: React.FC<ISpoilerInput> = ({
|
||||||
|
composeId,
|
||||||
|
onSuggestionsFetchRequested,
|
||||||
|
onSuggestionsClearRequested,
|
||||||
|
onSuggestionSelected,
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const compose = useCompose(composeId);
|
||||||
|
|
||||||
|
const handleChangeSpoilerText: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
dispatch(changeComposeSpoilerText(composeId, e.target.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemove = () => {
|
||||||
|
dispatch(changeComposeSpoilerness(composeId));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
space={4}
|
||||||
|
className={classNames({
|
||||||
|
'relative transition-height': true,
|
||||||
|
'hidden': !compose.spoiler,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Stack space={2}>
|
||||||
|
<Text weight='medium'>
|
||||||
|
{intl.formatMessage(messages.title)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<AutosuggestInput
|
||||||
|
placeholder={intl.formatMessage(messages.placeholder)}
|
||||||
|
value={compose.spoiler_text}
|
||||||
|
onChange={handleChangeSpoilerText}
|
||||||
|
disabled={!compose.spoiler}
|
||||||
|
suggestions={compose.suggestions}
|
||||||
|
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||||
|
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||||
|
onSuggestionSelected={onSuggestionSelected}
|
||||||
|
searchTokens={[':']}
|
||||||
|
id='cw-spoiler-input'
|
||||||
|
className='rounded-md dark:!bg-transparent !bg-transparent'
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='text-center'>
|
||||||
|
<button className='text-danger-500' onClick={handleRemove}>
|
||||||
|
{intl.formatMessage(messages.remove)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpoilerInput;
|
|
@ -288,7 +288,7 @@
|
||||||
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",
|
||||||
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
"compose_form.spoiler.marked": "Text is hidden behind warning",
|
||||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||||
"compose_form.spoiler_placeholder": "Write your warning here",
|
"compose_form.spoiler_placeholder": "Write your warning here (optional)",
|
||||||
"confirmation_modal.cancel": "Cancel",
|
"confirmation_modal.cancel": "Cancel",
|
||||||
"confirmations.admin.deactivate_user.confirm": "Deactivate @{name}",
|
"confirmations.admin.deactivate_user.confirm": "Deactivate @{name}",
|
||||||
"confirmations.admin.deactivate_user.heading": "Deactivate @{acct}",
|
"confirmations.admin.deactivate_user.heading": "Deactivate @{acct}",
|
||||||
|
|
Ładowanie…
Reference in New Issue