Handle sensitive and spoiler separately when writing a post

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-08-24 00:45:45 +02:00
parent d33589d103
commit 604ebdd24a
9 changed files with 38 additions and 102 deletions

View file

@ -188,7 +188,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
if (spoilerText) {
output.push(
<Text className='mb-2' size='2xl' weight='medium'>
<Text size='2xl' weight='medium'>
<span dangerouslySetInnerHTML={{ __html: spoilerText }} />
{expandable && (
<Button

View file

@ -92,7 +92,6 @@ const ChatMessage = (props: IChatMessage) => {
})}
media={[chatMessage.attachment]}
onOpenMedia={onOpenMedia}
visible
/>
);
};

View file

@ -12,11 +12,10 @@ import {
selectComposeSuggestion,
uploadCompose,
} from 'soapbox/actions/compose';
import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest-input';
import { Button, HStack, Stack } from 'soapbox/components/ui';
import EmojiPickerDropdown from 'soapbox/features/emoji/containers/emoji-picker-dropdown-container';
import { ComposeEditor } from 'soapbox/features/ui/util/async-components';
import { useAppDispatch, useAppSelector, useCompose, useDraggedFiles, useFeatures, useInstance, usePrevious } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector, useCompose, useDraggedFiles, useFeatures, useInstance } from 'soapbox/hooks';
import QuotedStatusContainer from '../containers/quoted-status-container';
import ReplyIndicatorContainer from '../containers/reply-indicator-container';
@ -41,6 +40,7 @@ import UploadForm from './upload-form';
import VisualCharacterCounter from './visual-character-counter';
import Warning from './warning';
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
import type { Emoji } from 'soapbox/features/emoji';
const messages = defineMessages({
@ -77,7 +77,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
const features = useFeatures();
const {
spoiler,
spoiler_text: spoilerText,
privacy,
is_submitting: isSubmitting,
@ -90,17 +89,13 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
modified_language: modifiedLanguage,
} = compose;
const prevSpoiler = usePrevious(spoiler);
const hasPoll = !!compose.poll;
const isEditing = compose.id !== null;
const anyMedia = compose.media_attachments.size > 0;
const [composeFocused, setComposeFocused] = useState(false);
const firstRender = useRef(true);
const formRef = useRef<HTMLDivElement>(null);
const spoilerTextRef = useRef<AutosuggestInput>(null);
const editorRef = useRef<LexicalEditor>(null);
const { isDraggedOver } = useDraggedFiles(formRef);
@ -171,10 +166,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
dispatch(uploadCompose(id, files, intl));
};
const focusSpoilerInput = () => {
spoilerTextRef.current?.input?.focus();
};
useEffect(() => {
document.addEventListener('click', handleClick, true);
@ -183,16 +174,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
};
}, []);
useEffect(() => {
if (spoiler && firstRender.current) {
firstRender.current = false;
} else if (!spoiler && prevSpoiler) {
//
} else if (spoiler && !prevSpoiler) {
focusSpoilerInput();
}
}, [spoiler]);
const renderButtons = useCallback(() => (
<HStack alignItems='center' space={2}>
<UploadButtonContainer composeId={id} />
@ -208,14 +189,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
<UploadForm composeId={id} onSubmit={handleSubmit} />
<PollForm composeId={id} />
<SpoilerInput
composeId={id}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSpoilerSuggestionSelected}
ref={spoilerTextRef}
/>
<ScheduleForm composeId={id} />
</Stack>
);
@ -280,6 +253,13 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
</HStack>
)}
<SpoilerInput
composeId={id}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSpoilerSuggestionSelected}
/>
<div>
<Suspense>
<ComposeEditor

View file

@ -7,8 +7,8 @@ import { useAppDispatch, useCompose } from 'soapbox/hooks';
import ComposeFormButton from './compose-form-button';
const messages = defineMessages({
marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Text is hidden behind warning' },
unmarked: { id: 'compose_form.spoiler.unmarked', defaultMessage: 'Text is not hidden' },
marked: { id: 'compose_form.spoiler.marked', defaultMessage: 'Media is marked as sensitive' },
unmarked: { id: 'compose_form.spoiler.unmarked', defaultMessage: 'Media is not marked as sensitive' },
});
interface ISpoilerButton {
@ -19,7 +19,7 @@ const SpoilerButton: React.FC<ISpoilerButton> = ({ composeId }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const active = useCompose(composeId).spoiler;
const active = useCompose(composeId).sensitive;
const onClick = () =>
dispatch(changeComposeSpoilerness(composeId));

View file

@ -1,16 +1,13 @@
import clsx from 'clsx';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { changeComposeSpoilerness, changeComposeSpoilerText } from 'soapbox/actions/compose';
import { 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' },
placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Subject (optional)' },
});
interface ISpoilerInput extends Pick<IAutosuggestInput, 'onSuggestionsFetchRequested' | 'onSuggestionsClearRequested' | 'onSuggestionSelected'> {
@ -26,56 +23,28 @@ const SpoilerInput = React.forwardRef<AutosuggestInput, ISpoilerInput>(({
}, ref) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { language, modified_language, spoiler, spoiler_text: spoilerText, spoilerTextMap, suggestions } = useCompose(composeId);
const { language, modified_language, spoiler_text: spoilerText, spoilerTextMap, suggestions } = useCompose(composeId);
const handleChangeSpoilerText: React.ChangeEventHandler<HTMLInputElement> = (e) => {
dispatch(changeComposeSpoilerText(composeId, e.target.value));
};
const handleRemove = () => {
dispatch(changeComposeSpoilerness(composeId));
};
const value = !modified_language || modified_language === language ? spoilerText : spoilerTextMap.get(modified_language, '');
return (
<Stack
space={4}
className={clsx({
'relative transition-height': true,
'hidden': !spoiler,
})}
>
<Divider />
<Stack space={2}>
<Text weight='medium'>
{intl.formatMessage(messages.title)}
</Text>
<AutosuggestInput
placeholder={intl.formatMessage(messages.placeholder)}
value={value}
onChange={handleChangeSpoilerText}
disabled={!spoiler}
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSuggestionSelected}
searchTokens={[':']}
id='cw-spoiler-input'
className='rounded-md !bg-transparent dark:!bg-transparent'
ref={ref}
autoFocus
/>
<div className='text-center'>
<button type='button' className='text-danger-500' onClick={handleRemove}>
{intl.formatMessage(messages.remove)}
</button>
</div>
</Stack>
</Stack>
<AutosuggestInput
placeholder={intl.formatMessage(messages.placeholder)}
value={value}
onChange={handleChangeSpoilerText}
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSuggestionSelected}
searchTokens={[':']}
id='cw-spoiler-input'
className='rounded-md !bg-transparent dark:!bg-transparent'
ref={ref}
/>
);
});

View file

@ -63,7 +63,6 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
media={status.media_attachments}
height={110}
onOpenMedia={noop}
visible
/>
);
}

View file

@ -485,11 +485,9 @@
"compose_form.schedule": "Schedule",
"compose_form.scheduled_statuses.click_here": "Click here",
"compose_form.scheduled_statuses.message": "You have scheduled posts. {click_here} to see them.",
"compose_form.spoiler.marked": "Text is hidden behind warning",
"compose_form.spoiler.unmarked": "Text is not hidden",
"compose_form.spoiler_placeholder": "Write your warning here (optional)",
"compose_form.spoiler_remove": "Remove sensitive",
"compose_form.spoiler_title": "Sensitive content",
"compose_form.spoiler.marked": "Media is marked as sensitive",
"compose_form.spoiler.unmarked": "Media is not marked as sensitive",
"compose_form.spoiler_placeholder": "Subject (optional)",
"confirmation_modal.cancel": "Cancel",
"confirmations.admin.deactivate_user.confirm": "Deactivate @{name}",
"confirmations.admin.deactivate_user.heading": "Deactivate @{acct}",

View file

@ -485,11 +485,9 @@
"compose_form.schedule": "Zaplanuj",
"compose_form.scheduled_statuses.click_here": "Naciśnij tutaj",
"compose_form.scheduled_statuses.message": "Masz zaplanowane wpisy. {click_here}, aby je zobaczyć.",
"compose_form.spoiler.marked": "Tekst jest ukryty za ostrzeżeniem",
"compose_form.spoiler.unmarked": "Tekst nie jest ukryty",
"compose_form.spoiler_placeholder": "Wprowadź swoje ostrzeżenie o zawartości",
"compose_form.spoiler_remove": "Usuń zaznaczenie jako wrażliwe",
"compose_form.spoiler_title": "Treści wrażliwe",
"compose_form.spoiler.marked": "Media są oznaczone jako wrażliwe",
"compose_form.spoiler.unmarked": "Media nie są oznaczone jako wrażliwe",
"compose_form.spoiler_placeholder": "Temat (nieobowiązkowy)",
"confirmation_modal.cancel": "Anuluj",
"confirmations.admin.deactivate_user.confirm": "Dezaktywuj @{name}",
"confirmations.admin.deactivate_user.heading": "Dezaktywuj @{acct}",

View file

@ -105,7 +105,6 @@ const ReducerCompose = ImmutableRecord({
resetFileKey: null as number | null,
schedule: null as Date | null,
sensitive: false,
spoiler: false,
spoiler_text: '',
spoilerTextMap: ImmutableMap<Language, string>(),
suggestions: ImmutableList<string>(),
@ -165,7 +164,7 @@ const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?
map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
map.set('idempotencyKey', uuid());
if (prevSize === 0 && (defaultSensitive || compose.spoiler)) {
if (prevSize === 0 && (defaultSensitive || compose.sensitive)) {
map.set('sensitive', true);
}
});
@ -293,9 +292,7 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | Me
}));
case COMPOSE_SPOILERNESS_CHANGE:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => {
map.set('spoiler_text', '');
map.set('spoiler', !compose.spoiler);
map.set('sensitive', !compose.spoiler);
map.set('sensitive', !compose.sensitive);
map.set('idempotencyKey', uuid());
}));
case COMPOSE_SPOILER_TEXT_CHANGE:
@ -342,7 +339,6 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | Me
map.set('idempotencyKey', uuid());
map.set('content_type', defaultCompose.content_type);
if (action.preserveSpoilers && action.status.spoiler_text) {
map.set('spoiler', true);
map.set('sensitive', true);
map.set('spoiler_text', action.status.spoiler_text);
}
@ -367,7 +363,6 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | Me
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
map.set('content_type', defaultCompose.content_type);
map.set('spoiler', false);
map.set('spoiler_text', '');
if (action.status.visibility === 'group') {
@ -484,10 +479,8 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | Me
}
if (action.status.spoiler_text.length > 0) {
map.set('spoiler', true);
map.set('spoiler_text', action.status.spoiler_text);
} else {
map.set('spoiler', false);
map.set('spoiler_text', '');
}