kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge remote-tracking branch 'origin/chats' into chats
commit
d0960de07c
|
@ -54,7 +54,6 @@ const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
||||||
|
|
||||||
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
||||||
|
|
||||||
const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
|
|
||||||
const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
||||||
const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE';
|
const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE';
|
||||||
const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
||||||
|
@ -600,11 +599,6 @@ const insertIntoTagHistory = (composeId: string, recognizedTags: APIEntity[], te
|
||||||
dispatch(updateTagHistory(composeId, newHistory));
|
dispatch(updateTagHistory(composeId, newHistory));
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeComposeSensitivity = (composeId: string) => ({
|
|
||||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
id: composeId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const changeComposeSpoilerness = (composeId: string) => ({
|
const changeComposeSpoilerness = (composeId: string) => ({
|
||||||
type: COMPOSE_SPOILERNESS_CHANGE,
|
type: COMPOSE_SPOILERNESS_CHANGE,
|
||||||
id: composeId,
|
id: composeId,
|
||||||
|
@ -741,7 +735,6 @@ export {
|
||||||
COMPOSE_SUGGESTION_SELECT,
|
COMPOSE_SUGGESTION_SELECT,
|
||||||
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||||
COMPOSE_TAG_HISTORY_UPDATE,
|
COMPOSE_TAG_HISTORY_UPDATE,
|
||||||
COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
COMPOSE_SPOILERNESS_CHANGE,
|
COMPOSE_SPOILERNESS_CHANGE,
|
||||||
COMPOSE_TYPE_CHANGE,
|
COMPOSE_TYPE_CHANGE,
|
||||||
COMPOSE_SPOILER_TEXT_CHANGE,
|
COMPOSE_SPOILER_TEXT_CHANGE,
|
||||||
|
@ -796,7 +789,6 @@ export {
|
||||||
selectComposeSuggestion,
|
selectComposeSuggestion,
|
||||||
updateSuggestionTags,
|
updateSuggestionTags,
|
||||||
updateTagHistory,
|
updateTagHistory,
|
||||||
changeComposeSensitivity,
|
|
||||||
changeComposeSpoilerness,
|
changeComposeSpoilerness,
|
||||||
changeComposeContentType,
|
changeComposeContentType,
|
||||||
changeComposeSpoilerText,
|
changeComposeSpoilerText,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
List as ImmutableList,
|
|
||||||
Map as ImmutableMap,
|
Map as ImmutableMap,
|
||||||
} from 'immutable';
|
} from 'immutable';
|
||||||
import IntlMessageFormat from 'intl-messageformat';
|
import IntlMessageFormat from 'intl-messageformat';
|
||||||
|
@ -12,6 +11,7 @@ import { getFilters, regexFromFilters } from 'soapbox/selectors';
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
||||||
import { unescapeHTML } from 'soapbox/utils/html';
|
import { unescapeHTML } from 'soapbox/utils/html';
|
||||||
|
import { NOTIFICATION_TYPES } from 'soapbox/utils/notification';
|
||||||
import { joinPublicPath } from 'soapbox/utils/static';
|
import { joinPublicPath } from 'soapbox/utils/static';
|
||||||
|
|
||||||
import { fetchRelationships } from './accounts';
|
import { fetchRelationships } from './accounts';
|
||||||
|
@ -168,11 +168,8 @@ const dequeueNotifications = () =>
|
||||||
dispatch(markReadNotifications());
|
dispatch(markReadNotifications());
|
||||||
};
|
};
|
||||||
|
|
||||||
// const excludeTypesFromSettings = (getState: () => RootState) => (getSettings(getState()).getIn(['notifications', 'shows']) as ImmutableMap<string, boolean>).filter(enabled => !enabled).keySeq().toJS();
|
|
||||||
|
|
||||||
const excludeTypesFromFilter = (filter: string) => {
|
const excludeTypesFromFilter = (filter: string) => {
|
||||||
const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'status', 'poll', 'move', 'pleroma:emoji_reaction']);
|
return NOTIFICATION_TYPES.filter(item => item !== filter);
|
||||||
return allTypes.filterNot(item => item === filter).toJS();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const noOp = () => new Promise(f => f(undefined));
|
const noOp = () => new Promise(f => f(undefined));
|
||||||
|
@ -182,6 +179,7 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
|
||||||
if (!isLoggedIn(getState)) return dispatch(noOp);
|
if (!isLoggedIn(getState)) return dispatch(noOp);
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
const features = getFeatures(state.instance);
|
||||||
const activeFilter = getSettings(state).getIn(['notifications', 'quickFilter', 'active']) as string;
|
const activeFilter = getSettings(state).getIn(['notifications', 'quickFilter', 'active']) as string;
|
||||||
const notifications = state.notifications;
|
const notifications = state.notifications;
|
||||||
const isLoadingMore = !!maxId;
|
const isLoadingMore = !!maxId;
|
||||||
|
@ -195,10 +193,11 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
|
||||||
max_id: maxId,
|
max_id: maxId,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (activeFilter !== 'all') {
|
if (activeFilter === 'all') {
|
||||||
const instance = state.instance;
|
if (features.notificationsIncludeTypes) {
|
||||||
const features = getFeatures(instance);
|
params.types = NOTIFICATION_TYPES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (features.notificationsIncludeTypes) {
|
if (features.notificationsIncludeTypes) {
|
||||||
params.types = [activeFilter];
|
params.types = [activeFilter];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -105,10 +105,6 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExpandedToggle = (): void => {
|
|
||||||
dispatch(toggleStatusHidden(actualStatus));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHotkeyOpenMedia = (e?: KeyboardEvent): void => {
|
const handleHotkeyOpenMedia = (e?: KeyboardEvent): void => {
|
||||||
const status = actualStatus;
|
const status = actualStatus;
|
||||||
const firstAttachment = status.media_attachments.first();
|
const firstAttachment = status.media_attachments.first();
|
||||||
|
@ -301,7 +297,7 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
const accountAction = props.accountAction || reblogElement;
|
const accountAction = props.accountAction || reblogElement;
|
||||||
|
|
||||||
const inReview = status.visibility === 'self';
|
const inReview = status.visibility === 'self';
|
||||||
const isSensitive = status.sensitive;
|
const isSensitive = status.hidden;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers} data-testid='status'>
|
<HotKeys handlers={handlers} data-testid='status'>
|
||||||
|
@ -382,8 +378,6 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={actualStatus}
|
status={actualStatus}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
expanded={!status.hidden}
|
|
||||||
onExpandedToggle={handleExpandedToggle}
|
|
||||||
collapsable
|
collapsable
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -35,49 +35,16 @@ const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => (
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
interface ISpoilerButton {
|
|
||||||
onClick: React.MouseEventHandler,
|
|
||||||
hidden: boolean,
|
|
||||||
tabIndex?: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Button to expand status text behind a content warning */
|
|
||||||
const SpoilerButton: React.FC<ISpoilerButton> = ({ onClick, hidden, tabIndex }) => (
|
|
||||||
<button
|
|
||||||
tabIndex={tabIndex}
|
|
||||||
className={classNames(
|
|
||||||
'inline-block rounded-md px-1.5 py-0.5 ml-[0.5em]',
|
|
||||||
'text-gray-900 dark:text-gray-100',
|
|
||||||
'font-bold text-[11px] uppercase',
|
|
||||||
'bg-primary-100 dark:bg-primary-800',
|
|
||||||
'hover:bg-primary-300 dark:hover:bg-primary-600',
|
|
||||||
'focus:bg-primary-200 dark:focus:bg-primary-600',
|
|
||||||
'hover:no-underline',
|
|
||||||
'duration-100',
|
|
||||||
)}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{hidden ? (
|
|
||||||
<FormattedMessage id='status.show_more' defaultMessage='Show more' />
|
|
||||||
) : (
|
|
||||||
<FormattedMessage id='status.show_less' defaultMessage='Show less' />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
interface IStatusContent {
|
interface IStatusContent {
|
||||||
status: Status,
|
status: Status,
|
||||||
expanded?: boolean,
|
|
||||||
onExpandedToggle?: () => void,
|
|
||||||
onClick?: () => void,
|
onClick?: () => void,
|
||||||
collapsable?: boolean,
|
collapsable?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Renders the text content of a status */
|
/** Renders the text content of a status */
|
||||||
const StatusContent: React.FC<IStatusContent> = ({ status, expanded = false, onExpandedToggle, onClick, collapsable = false }) => {
|
const StatusContent: React.FC<IStatusContent> = ({ status, onClick, collapsable = false }) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const [hidden, setHidden] = useState(true);
|
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
const [onlyEmoji, setOnlyEmoji] = useState(false);
|
const [onlyEmoji, setOnlyEmoji] = useState(false);
|
||||||
|
|
||||||
|
@ -186,18 +153,6 @@ const StatusContent: React.FC<IStatusContent> = ({ status, expanded = false, onE
|
||||||
startXY.current = undefined;
|
startXY.current = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSpoilerClick: React.EventHandler<React.MouseEvent> = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (onExpandedToggle) {
|
|
||||||
// The parent manages the state
|
|
||||||
onExpandedToggle();
|
|
||||||
} else {
|
|
||||||
setHidden(!hidden);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsedHtml = useMemo((): string => {
|
const parsedHtml = useMemo((): string => {
|
||||||
const { contentHtml: html } = status;
|
const { contentHtml: html } = status;
|
||||||
|
|
||||||
|
@ -212,13 +167,11 @@ const StatusContent: React.FC<IStatusContent> = ({ status, expanded = false, onE
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isHidden = onExpandedToggle ? !expanded : hidden;
|
|
||||||
const withSpoiler = status.spoiler_text.length > 0;
|
const withSpoiler = status.spoiler_text.length > 0;
|
||||||
|
|
||||||
const baseClassName = 'text-gray-900 dark:text-gray-100 break-words text-ellipsis overflow-hidden relative focus:outline-none';
|
const baseClassName = 'text-gray-900 dark:text-gray-100 break-words text-ellipsis overflow-hidden relative focus:outline-none';
|
||||||
|
|
||||||
const content = { __html: parsedHtml };
|
const content = { __html: parsedHtml };
|
||||||
const spoilerContent = { __html: status.spoilerHtml };
|
|
||||||
const directionStyle: React.CSSProperties = { direction: 'ltr' };
|
const directionStyle: React.CSSProperties = { direction: 'ltr' };
|
||||||
const className = classNames(baseClassName, 'status-content', {
|
const className = classNames(baseClassName, 'status-content', {
|
||||||
'cursor-pointer': onClick,
|
'cursor-pointer': onClick,
|
||||||
|
@ -231,37 +184,7 @@ const StatusContent: React.FC<IStatusContent> = ({ status, expanded = false, onE
|
||||||
directionStyle.direction = 'rtl';
|
directionStyle.direction = 'rtl';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.spoiler_text.length > 0) {
|
if (onClick) {
|
||||||
return (
|
|
||||||
<div className={className} ref={node} tabIndex={0} style={directionStyle} onMouseDown={handleMouseDown} onMouseUp={handleMouseUp}>
|
|
||||||
<p style={{ marginBottom: isHidden && status.mentions.isEmpty() ? 0 : undefined }}>
|
|
||||||
<span dangerouslySetInnerHTML={spoilerContent} lang={status.language || undefined} />
|
|
||||||
|
|
||||||
<SpoilerButton
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={handleSpoilerClick}
|
|
||||||
hidden={isHidden}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div
|
|
||||||
tabIndex={!isHidden ? 0 : undefined}
|
|
||||||
className={classNames({
|
|
||||||
'whitespace-pre-wrap': withSpoiler,
|
|
||||||
'hidden': isHidden,
|
|
||||||
'block': !isHidden,
|
|
||||||
})}
|
|
||||||
style={directionStyle}
|
|
||||||
dangerouslySetInnerHTML={content}
|
|
||||||
lang={status.language || undefined}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{!isHidden && status.poll && typeof status.poll === 'string' && (
|
|
||||||
<Poll id={status.poll} status={status.url} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (onClick) {
|
|
||||||
const output = [
|
const output = [
|
||||||
<div
|
<div
|
||||||
ref={node}
|
ref={node}
|
||||||
|
|
|
@ -81,6 +81,14 @@ const SensitiveContentOverlay = (props: ISensitiveContentOverlay) => {
|
||||||
<Text theme='white' size='sm' weight='medium'>
|
<Text theme='white' size='sm' weight='medium'>
|
||||||
{intl.formatMessage(isUnderReview ? messages.underReviewSubtitle : messages.sensitiveSubtitle)}
|
{intl.formatMessage(isUnderReview ? messages.underReviewSubtitle : messages.sensitiveSubtitle)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{status.spoiler_text && (
|
||||||
|
<div className='py-4 italic'>
|
||||||
|
<Text theme='white' size='md' weight='medium'>
|
||||||
|
“<span dangerouslySetInnerHTML={{ __html: status.spoilerHtml }} />”
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HStack alignItems='center' justifyContent='center' space={2}>
|
<HStack alignItems='center' justifyContent='center' space={2}>
|
||||||
|
|
|
@ -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,19 @@ 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}
|
||||||
|
ref={spoilerTextRef}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
}
|
}
|
||||||
</AutosuggestTextarea>
|
</AutosuggestTextarea>
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
import { changeComposeSensitivity } from 'soapbox/actions/compose';
|
|
||||||
import { FormGroup, Checkbox } from 'soapbox/components/ui';
|
|
||||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
|
||||||
unmarked: { id: 'compose_form.sensitive.unmarked', defaultMessage: 'Media is not marked as sensitive' },
|
|
||||||
});
|
|
||||||
|
|
||||||
interface ISensitiveButton {
|
|
||||||
composeId: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Button to mark own media as sensitive. */
|
|
||||||
const SensitiveButton: React.FC<ISensitiveButton> = ({ composeId }) => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const compose = useCompose(composeId);
|
|
||||||
|
|
||||||
const active = compose.sensitive === true;
|
|
||||||
const disabled = compose.spoiler === true;
|
|
||||||
|
|
||||||
const onClick = () => {
|
|
||||||
dispatch(changeComposeSensitivity(composeId));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='px-2.5 py-1'>
|
|
||||||
<FormGroup
|
|
||||||
labelText={<FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />}
|
|
||||||
labelTitle={intl.formatMessage(active ? messages.marked : messages.unmarked)}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
name='mark-sensitive'
|
|
||||||
checked={active}
|
|
||||||
onChange={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SensitiveButton;
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
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.forwardRef<AutosuggestInput, ISpoilerInput>(({
|
||||||
|
composeId,
|
||||||
|
onSuggestionsFetchRequested,
|
||||||
|
onSuggestionsClearRequested,
|
||||||
|
onSuggestionSelected,
|
||||||
|
}, ref) => {
|
||||||
|
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'
|
||||||
|
ref={ref}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='text-center'>
|
||||||
|
<button className='text-danger-500' onClick={handleRemove}>
|
||||||
|
{intl.formatMessage(messages.remove)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SpoilerInput;
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
||||||
|
|
||||||
import { useCompose } from 'soapbox/hooks';
|
import { useCompose } from 'soapbox/hooks';
|
||||||
|
|
||||||
import SensitiveButton from './sensitive-button';
|
|
||||||
import Upload from './upload';
|
import Upload from './upload';
|
||||||
import UploadProgress from './upload-progress';
|
import UploadProgress from './upload-progress';
|
||||||
|
|
||||||
|
@ -28,8 +27,6 @@ const UploadForm: React.FC<IUploadForm> = ({ composeId }) => {
|
||||||
<Upload id={id} key={id} composeId={composeId} />
|
<Upload id={id} key={id} composeId={composeId} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!mediaIds.isEmpty() && <SensitiveButton composeId={composeId} />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -268,7 +268,7 @@ const Notification: React.FC<INotificaton> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
switch (type) {
|
switch (type as NotificationType) {
|
||||||
case 'follow':
|
case 'follow':
|
||||||
case 'user_approved':
|
case 'user_approved':
|
||||||
return account && typeof account === 'object' ? (
|
return account && typeof account === 'object' ? (
|
||||||
|
|
|
@ -77,9 +77,9 @@ const languages = {
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.preferences', defaultMessage: 'Preferences' },
|
heading: { id: 'column.preferences', defaultMessage: 'Preferences' },
|
||||||
display_media_default: { id: 'preferences.fields.display_media.default', defaultMessage: 'Hide media marked as sensitive' },
|
displayPostsDefault: { id: 'preferences.fields.display_media.default', defaultMessage: 'Hide posts marked as sensitive' },
|
||||||
display_media_hide_all: { id: 'preferences.fields.display_media.hide_all', defaultMessage: 'Always hide media' },
|
displayPostsHideAll: { id: 'preferences.fields.display_media.hide_all', defaultMessage: 'Always hide posts' },
|
||||||
display_media_show_all: { id: 'preferences.fields.display_media.show_all', defaultMessage: 'Always show media' },
|
displayPostsShowAll: { id: 'preferences.fields.display_media.show_all', defaultMessage: 'Always show posts' },
|
||||||
privacy_public: { id: 'preferences.options.privacy_public', defaultMessage: 'Public' },
|
privacy_public: { id: 'preferences.options.privacy_public', defaultMessage: 'Public' },
|
||||||
privacy_unlisted: { id: 'preferences.options.privacy_unlisted', defaultMessage: 'Unlisted' },
|
privacy_unlisted: { id: 'preferences.options.privacy_unlisted', defaultMessage: 'Unlisted' },
|
||||||
privacy_followers_only: { id: 'preferences.options.privacy_followers_only', defaultMessage: 'Followers-only' },
|
privacy_followers_only: { id: 'preferences.options.privacy_followers_only', defaultMessage: 'Followers-only' },
|
||||||
|
@ -102,9 +102,9 @@ const Preferences = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const displayMediaOptions = React.useMemo(() => ({
|
const displayMediaOptions = React.useMemo(() => ({
|
||||||
default: intl.formatMessage(messages.display_media_default),
|
default: intl.formatMessage(messages.displayPostsDefault),
|
||||||
hide_all: intl.formatMessage(messages.display_media_hide_all),
|
hide_all: intl.formatMessage(messages.displayPostsHideAll),
|
||||||
show_all: intl.formatMessage(messages.display_media_show_all),
|
show_all: intl.formatMessage(messages.displayPostsShowAll),
|
||||||
}), []);
|
}), []);
|
||||||
|
|
||||||
const defaultPrivacyOptions = React.useMemo(() => ({
|
const defaultPrivacyOptions = React.useMemo(() => ({
|
||||||
|
@ -149,7 +149,7 @@ const Preferences = () => {
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem label={<FormattedMessage id='preferences.fields.media_display_label' defaultMessage='Media display' />}>
|
<ListItem label={<FormattedMessage id='preferences.fields.media_display_label' defaultMessage='Sensitive content' />}>
|
||||||
<SelectDropdown
|
<SelectDropdown
|
||||||
items={displayMediaOptions}
|
items={displayMediaOptions}
|
||||||
defaultValue={settings.get('displayMedia') as string | undefined}
|
defaultValue={settings.get('displayMedia') as string | undefined}
|
||||||
|
|
|
@ -44,7 +44,6 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={status}
|
status={status}
|
||||||
expanded
|
|
||||||
collapsable
|
collapsable
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,6 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const node = useRef<HTMLDivElement>(null);
|
const node = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const handleExpandedToggle = () => {
|
|
||||||
onToggleHidden(status);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenCompareHistoryModal = () => {
|
const handleOpenCompareHistoryModal = () => {
|
||||||
onOpenCompareHistoryModal(status);
|
onOpenCompareHistoryModal(status);
|
||||||
};
|
};
|
||||||
|
@ -51,7 +47,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
|
||||||
if (!account || typeof account !== 'object') return null;
|
if (!account || typeof account !== 'object') return null;
|
||||||
|
|
||||||
const isUnderReview = actualStatus.visibility === 'self';
|
const isUnderReview = actualStatus.visibility === 'self';
|
||||||
const isSensitive = actualStatus.sensitive;
|
const isSensitive = actualStatus.hidden;
|
||||||
|
|
||||||
let statusTypeIcon = null;
|
let statusTypeIcon = null;
|
||||||
|
|
||||||
|
@ -105,11 +101,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent status={actualStatus} />
|
||||||
status={actualStatus}
|
|
||||||
expanded={!actualStatus.hidden}
|
|
||||||
onExpandedToggle={handleExpandedToggle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StatusMedia
|
<StatusMedia
|
||||||
status={actualStatus}
|
status={actualStatus}
|
||||||
|
|
|
@ -59,7 +59,6 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={status}
|
status={status}
|
||||||
expanded
|
|
||||||
collapsable
|
collapsable
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,6 @@ const PendingStatus: React.FC<IPendingStatus> = ({ idempotencyKey, className, mu
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={status}
|
status={status}
|
||||||
expanded
|
|
||||||
collapsable
|
collapsable
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,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}",
|
||||||
|
@ -796,12 +796,12 @@
|
||||||
"preferences.fields.autoload_timelines_label": "Automatically load new posts when scrolled to the top of the page",
|
"preferences.fields.autoload_timelines_label": "Automatically load new posts when scrolled to the top of the page",
|
||||||
"preferences.fields.boost_modal_label": "Show confirmation dialog before reposting",
|
"preferences.fields.boost_modal_label": "Show confirmation dialog before reposting",
|
||||||
"preferences.fields.delete_modal_label": "Show confirmation dialog before deleting a post",
|
"preferences.fields.delete_modal_label": "Show confirmation dialog before deleting a post",
|
||||||
"preferences.fields.display_media.default": "Hide media marked as sensitive",
|
"preferences.fields.display_media.default": "Hide posts marked as sensitive",
|
||||||
"preferences.fields.display_media.hide_all": "Always hide media",
|
"preferences.fields.display_media.hide_all": "Always hide posts",
|
||||||
"preferences.fields.display_media.show_all": "Always show media",
|
"preferences.fields.display_media.show_all": "Always show posts",
|
||||||
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
|
"preferences.fields.expand_spoilers_label": "Always expand posts marked with content warnings",
|
||||||
"preferences.fields.language_label": "Display Language",
|
"preferences.fields.language_label": "Display Language",
|
||||||
"preferences.fields.media_display_label": "Media display",
|
"preferences.fields.media_display_label": "Sensitive content",
|
||||||
"preferences.hints.feed": "In your home feed",
|
"preferences.hints.feed": "In your home feed",
|
||||||
"privacy.change": "Adjust post privacy",
|
"privacy.change": "Adjust post privacy",
|
||||||
"privacy.direct.long": "Post to mentioned users only",
|
"privacy.direct.long": "Post to mentioned users only",
|
||||||
|
|
|
@ -149,6 +149,13 @@ const fixFiltered = (status: ImmutableMap<string, any>) => {
|
||||||
status.delete('filtered');
|
status.delete('filtered');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** If the status contains spoiler text, treat it as sensitive. */
|
||||||
|
const fixSensitivity = (status: ImmutableMap<string, any>) => {
|
||||||
|
if (status.get('spoiler_text')) {
|
||||||
|
status.set('sensitive', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const normalizeStatus = (status: Record<string, any>) => {
|
export const normalizeStatus = (status: Record<string, any>) => {
|
||||||
return StatusRecord(
|
return StatusRecord(
|
||||||
ImmutableMap(fromJS(status)).withMutations(status => {
|
ImmutableMap(fromJS(status)).withMutations(status => {
|
||||||
|
@ -161,6 +168,7 @@ export const normalizeStatus = (status: Record<string, any>) => {
|
||||||
addSelfMention(status);
|
addSelfMention(status);
|
||||||
fixQuote(status);
|
fixQuote(status);
|
||||||
fixFiltered(status);
|
fixFiltered(status);
|
||||||
|
fixSensitivity(status);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -181,30 +181,8 @@ describe('compose reducer', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle COMPOSE_SENSITIVITY_CHANGE on Mark Sensitive click, don\'t toggle if spoiler active', () => {
|
|
||||||
const state = initialState.set('home', ReducerCompose({ spoiler: true, sensitive: true, idempotencyKey: '' }));
|
|
||||||
const action = {
|
|
||||||
type: actions.COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
id: 'home',
|
|
||||||
};
|
|
||||||
expect(reducer(state, action).toJS().home).toMatchObject({
|
|
||||||
sensitive: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle COMPOSE_SENSITIVITY_CHANGE on Mark Sensitive click, toggle if spoiler inactive', () => {
|
|
||||||
const state = initialState.set('home', ReducerCompose({ spoiler: false, sensitive: true }));
|
|
||||||
const action = {
|
|
||||||
type: actions.COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
id: 'home',
|
|
||||||
};
|
|
||||||
expect(reducer(state, action).toJS().home).toMatchObject({
|
|
||||||
sensitive: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle COMPOSE_SPOILERNESS_CHANGE on CW button click', () => {
|
it('should handle COMPOSE_SPOILERNESS_CHANGE on CW button click', () => {
|
||||||
const state = initialState.set('home', ReducerCompose({ spoiler_text: 'spoiler text', spoiler: true, media_attachments: ImmutableList() }));
|
const state = initialState.set('home', ReducerCompose({ spoiler_text: 'spoiler text', spoiler: true, sensitive: true, media_attachments: ImmutableList() }));
|
||||||
const action = {
|
const action = {
|
||||||
type: actions.COMPOSE_SPOILERNESS_CHANGE,
|
type: actions.COMPOSE_SPOILERNESS_CHANGE,
|
||||||
id: 'home',
|
id: 'home',
|
||||||
|
@ -212,6 +190,7 @@ describe('compose reducer', () => {
|
||||||
expect(reducer(state, action).toJS().home).toMatchObject({
|
expect(reducer(state, action).toJS().home).toMatchObject({
|
||||||
spoiler: false,
|
spoiler: false,
|
||||||
spoiler_text: '',
|
spoiler_text: '',
|
||||||
|
sensitive: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ import {
|
||||||
COMPOSE_SUGGESTION_SELECT,
|
COMPOSE_SUGGESTION_SELECT,
|
||||||
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||||
COMPOSE_TAG_HISTORY_UPDATE,
|
COMPOSE_TAG_HISTORY_UPDATE,
|
||||||
COMPOSE_SENSITIVITY_CHANGE,
|
|
||||||
COMPOSE_SPOILERNESS_CHANGE,
|
COMPOSE_SPOILERNESS_CHANGE,
|
||||||
COMPOSE_TYPE_CHANGE,
|
COMPOSE_TYPE_CHANGE,
|
||||||
COMPOSE_SPOILER_TEXT_CHANGE,
|
COMPOSE_SPOILER_TEXT_CHANGE,
|
||||||
|
@ -279,14 +278,6 @@ export const initialState: State = ImmutableMap({
|
||||||
|
|
||||||
export default function compose(state = initialState, action: AnyAction) {
|
export default function compose(state = initialState, action: AnyAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case COMPOSE_SENSITIVITY_CHANGE:
|
|
||||||
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
|
||||||
if (!compose.spoiler) {
|
|
||||||
map.set('sensitive', !compose.sensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
map.set('idempotencyKey', uuid());
|
|
||||||
}));
|
|
||||||
case COMPOSE_TYPE_CHANGE:
|
case COMPOSE_TYPE_CHANGE:
|
||||||
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
||||||
map.set('content_type', action.value);
|
map.set('content_type', action.value);
|
||||||
|
@ -296,11 +287,8 @@ export default function compose(state = initialState, action: AnyAction) {
|
||||||
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
return updateCompose(state, action.id, compose => compose.withMutations(map => {
|
||||||
map.set('spoiler_text', '');
|
map.set('spoiler_text', '');
|
||||||
map.set('spoiler', !compose.spoiler);
|
map.set('spoiler', !compose.spoiler);
|
||||||
|
map.set('sensitive', !compose.spoiler);
|
||||||
map.set('idempotencyKey', uuid());
|
map.set('idempotencyKey', uuid());
|
||||||
|
|
||||||
if (!compose.sensitive && compose.media_attachments.size >= 1) {
|
|
||||||
map.set('sensitive', true);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
case COMPOSE_SPOILER_TEXT_CHANGE:
|
case COMPOSE_SPOILER_TEXT_CHANGE:
|
||||||
return updateCompose(state, action.id, compose => compose
|
return updateCompose(state, action.id, compose => compose
|
||||||
|
@ -328,14 +316,6 @@ export default function compose(state = initialState, action: AnyAction) {
|
||||||
map.set('caretPosition', null);
|
map.set('caretPosition', null);
|
||||||
map.set('idempotencyKey', uuid());
|
map.set('idempotencyKey', uuid());
|
||||||
map.set('content_type', defaultCompose.content_type);
|
map.set('content_type', defaultCompose.content_type);
|
||||||
|
|
||||||
if (action.status.get('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', '');
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
case COMPOSE_QUOTE:
|
case COMPOSE_QUOTE:
|
||||||
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
|
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
} from 'immutable';
|
} from 'immutable';
|
||||||
|
|
||||||
import { normalizeNotification } from 'soapbox/normalizers/notification';
|
import { normalizeNotification } from 'soapbox/normalizers/notification';
|
||||||
|
import { validType } from 'soapbox/utils/notification';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ACCOUNT_BLOCK_SUCCESS,
|
ACCOUNT_BLOCK_SUCCESS,
|
||||||
|
@ -67,6 +68,11 @@ const fixNotification = notification => {
|
||||||
|
|
||||||
const isValid = notification => {
|
const isValid = notification => {
|
||||||
try {
|
try {
|
||||||
|
// Ensure the notification is a known type
|
||||||
|
if (!validType(notification.type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
|
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
|
||||||
if (!notification.account.id) {
|
if (!notification.account.id) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Ładowanie…
Reference in New Issue