kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Introduce useCompose hook
Signed-off-by: marcin mikolajczak <git@mkljczk.pl>environments/review-compose-fe5g2p/deployments/978
rodzic
2b6d06ce01
commit
6cce0a0291
|
@ -215,7 +215,7 @@ const needsDescriptions = (state: RootState, composeId: string) => {
|
|||
};
|
||||
|
||||
const validateSchedule = (state: RootState, composeId: string) => {
|
||||
const schedule = state.compose.get(composeId)!.schedule;
|
||||
const schedule = state.compose.get(composeId)?.schedule;
|
||||
if (!schedule) return true;
|
||||
|
||||
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
|
||||
|
|
|
@ -24,8 +24,8 @@ export const checkComposeContent = compose => {
|
|||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
hasComposeContent: checkComposeContent(state.compose.get('modal')),
|
||||
isEditing: state.compose.get('modal')?.id !== null,
|
||||
hasComposeContent: checkComposeContent(state.compose.get('compose-modal')),
|
||||
isEditing: state.compose.get('compose-modal')?.id !== null,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
|
|
@ -18,7 +18,7 @@ import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest
|
|||
import AutosuggestTextarea from 'soapbox/components/autosuggest_textarea';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { Button, Stack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'soapbox/hooks';
|
||||
import { isMobile } from 'soapbox/is_mobile';
|
||||
|
||||
import EmojiPickerDropdown from '../components/emoji-picker/emoji-picker-dropdown';
|
||||
|
@ -68,7 +68,7 @@ const ComposeForm: React.FC<IComposeForm> = ({ id, shouldCondense, autoFocus, cl
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const compose = useAppSelector((state) => state.compose.get(id)!);
|
||||
const compose = useCompose(id);
|
||||
const showSearch = useAppSelector((state) => state.search.submitted && !state.search.hidden);
|
||||
const isModalOpen = useAppSelector((state) => !!(state.modals.size && state.modals.last()!.modalType === 'COMPOSE'));
|
||||
const maxTootChars = useAppSelector((state) => state.instance.getIn(['configuration', 'statuses', 'max_characters'])) as number;
|
||||
|
@ -218,6 +218,18 @@ const ComposeForm: React.FC<IComposeForm> = ({ id, shouldCondense, autoFocus, cl
|
|||
}
|
||||
}, [focusDate]);
|
||||
|
||||
const renderButtons = useCallback(() => (
|
||||
<div className='flex items-center space-x-2'>
|
||||
{features.media && <UploadButtonContainer composeId={id} />}
|
||||
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} />
|
||||
{features.polls && <PollButton composeId={id} />}
|
||||
{features.privacyScopes && <PrivacyDropdown composeId={id} />}
|
||||
{features.scheduledStatuses && <ScheduleButton composeId={id} />}
|
||||
{features.spoilers && <SpoilerButton composeId={id} />}
|
||||
{features.richText && <MarkdownButton composeId={id} />}
|
||||
</div>
|
||||
), [features, id]);
|
||||
|
||||
const condensed = shouldCondense && !composeFocused && isEmpty() && !isUploading;
|
||||
const disabled = isSubmitting;
|
||||
const countedText = [spoilerText, countableText(text)].join('');
|
||||
|
@ -335,15 +347,7 @@ const ComposeForm: React.FC<IComposeForm> = ({ id, shouldCondense, autoFocus, cl
|
|||
'hidden': condensed,
|
||||
})}
|
||||
>
|
||||
<div className='flex items-center space-x-2'>
|
||||
{features.media && <UploadButtonContainer composeId={id} />}
|
||||
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} />
|
||||
{features.polls && <PollButton composeId={id} />}
|
||||
{features.privacyScopes && <PrivacyDropdown composeId={id} />}
|
||||
{features.scheduledStatuses && <ScheduleButton composeId={id} />}
|
||||
{features.spoilers && <SpoilerButton composeId={id} />}
|
||||
{features.richText && <MarkdownButton composeId={id} />}
|
||||
</div>
|
||||
{renderButtons()}
|
||||
|
||||
<div className='flex items-center space-x-4 ml-auto'>
|
||||
{maxTootChars && (
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeComposeContentType } from 'soapbox/actions/compose';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import ComposeFormButton from './compose_form_button';
|
||||
|
||||
|
@ -19,7 +19,7 @@ const MarkdownButton: React.FC<IMarkdownButton> = ({ composeId }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const active = useAppSelector((state) => state.compose.get(composeId)!.content_type === 'text/markdown');
|
||||
const active = useCompose(composeId).content_type === 'text/markdown';
|
||||
|
||||
const onClick = () => dispatch(changeComposeContentType(composeId, active ? 'text/plain' : 'text/markdown'));
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { addPoll, removePoll } from 'soapbox/actions/compose';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import ComposeFormButton from './compose_form_button';
|
||||
|
||||
|
@ -20,8 +20,10 @@ const PollButton: React.FC<IPollButton> = ({ composeId, disabled }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const unavailable = useAppSelector((state) => state.compose.get(composeId)!.is_uploading);
|
||||
const active = useAppSelector((state) => state.compose.get(composeId)!.poll !== null);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const unavailable = compose.is_uploading;
|
||||
const active = compose.poll !== null;
|
||||
|
||||
const onClick = () => {
|
||||
if (active) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|||
import { addPollOption, changePollOption, changePollSettings, clearComposeSuggestions, fetchComposeSuggestions, removePoll, removePollOption, selectComposeSuggestion } from 'soapbox/actions/compose';
|
||||
import AutosuggestInput from 'soapbox/components/autosuggest_input';
|
||||
import { Button, Divider, HStack, Stack, Text, Toggle } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import DurationSelector from './duration-selector';
|
||||
|
||||
|
@ -49,7 +49,7 @@ const Option: React.FC<IOption> = ({
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const suggestions = useAppSelector((state) => state.compose.get(composeId)!.suggestions);
|
||||
const suggestions = useCompose(composeId).suggestions;
|
||||
|
||||
const handleOptionTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => onChange(index, event.target.value);
|
||||
|
||||
|
@ -110,10 +110,12 @@ const PollForm: React.FC<IPollForm> = ({ composeId }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const pollLimits = useAppSelector((state) => state.instance.getIn(['configuration', 'polls']) as any);
|
||||
const options = useAppSelector((state) => state.compose.get(composeId)!.poll?.options);
|
||||
const expiresIn = useAppSelector((state) => state.compose.get(composeId)!.poll?.expires_in);
|
||||
const isMultiple = useAppSelector((state) => state.compose.get(composeId)!.poll?.multiple);
|
||||
const options = compose.poll?.options;
|
||||
const expiresIn = compose.poll?.expires_in;
|
||||
const isMultiple = compose.poll?.multiple;
|
||||
|
||||
const maxOptions = pollLimits.get('max_options');
|
||||
const maxOptionChars = pollLimits.get('max_characters_per_option');
|
||||
|
|
|
@ -10,7 +10,7 @@ import { changeComposeVisibility } from 'soapbox/actions/compose';
|
|||
import { closeModal, openModal } from 'soapbox/actions/modals';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { IconButton } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
import { isUserTouching } from 'soapbox/is_mobile';
|
||||
|
||||
import Motion from '../../ui/util/optional_motion';
|
||||
|
@ -151,8 +151,10 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
|
|||
const node = useRef<HTMLDivElement>(null);
|
||||
const activeElement = useRef<HTMLElement | null>(null);
|
||||
|
||||
const value = useAppSelector(state => state.compose.get(composeId)!.privacy);
|
||||
const unavailable = useAppSelector(state => !!state.compose.get(composeId)!.id);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const value = compose.privacy;
|
||||
const unavailable = compose.id;
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [placement, setPlacement] = useState('bottom');
|
||||
|
|
|
@ -3,7 +3,7 @@ import { FormattedList, FormattedMessage } from 'react-intl';
|
|||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
|
||||
import { makeGetStatus } from 'soapbox/selectors';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
|
@ -16,10 +16,12 @@ interface IReplyMentions {
|
|||
|
||||
const ReplyMentions: React.FC<IReplyMentions> = ({ composeId }) => {
|
||||
const dispatch = useDispatch();
|
||||
const instance = useAppSelector((state) => state.instance);
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.get(composeId)!.in_reply_to! }));
|
||||
|
||||
const to = useAppSelector((state) => state.compose.get(composeId)!.to);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const instance = useAppSelector((state) => state.instance);
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: compose.in_reply_to! }));
|
||||
const to = compose.to;
|
||||
const account = useAppSelector((state) => state.accounts.get(state.me));
|
||||
|
||||
const { explicitAddressing } = getFeatures(instance);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { addSchedule, removeSchedule } from 'soapbox/actions/compose';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import ComposeFormButton from './compose_form_button';
|
||||
|
||||
|
@ -20,8 +20,10 @@ const ScheduleButton: React.FC<IScheduleButton> = ({ composeId, disabled }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const active = useAppSelector((state) => !!state.compose.get(composeId)!.schedule);
|
||||
const unavailable = useAppSelector((state) => !!state.compose.get(composeId)!.id);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const active = compose.schedule;
|
||||
const unavailable = compose.id;
|
||||
|
||||
const handleClick = () => {
|
||||
if (active) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import IconButton from 'soapbox/components/icon_button';
|
|||
import { HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
const isCurrentOrFutureDate = (date: Date) => {
|
||||
return date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
|
||||
|
@ -35,7 +35,7 @@ const ScheduleForm: React.FC<IScheduleForm> = ({ composeId }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const scheduledAt = useAppSelector((state) => state.compose.get(composeId)!.schedule);
|
||||
const scheduledAt = useCompose(composeId).schedule;
|
||||
const active = !!scheduledAt;
|
||||
|
||||
const onSchedule = (date: Date) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|||
|
||||
import { changeComposeSensitivity } from 'soapbox/actions/compose';
|
||||
import { FormGroup, Checkbox } from 'soapbox/components/ui';
|
||||
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
const messages = defineMessages({
|
||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
||||
|
@ -19,8 +19,10 @@ const SensitiveButton: React.FC<ISensitiveButton> = ({ composeId }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const active = useAppSelector(state => state.compose.get(composeId)!.sensitive === true);
|
||||
const disabled = useAppSelector(state => state.compose.get(composeId)!.spoiler === true);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const active = compose.sensitive === true;
|
||||
const disabled = compose.spoiler === true;
|
||||
|
||||
const onClick = () => {
|
||||
dispatch(changeComposeSensitivity(composeId));
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeComposeSpoilerness } from 'soapbox/actions/compose';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import ComposeFormButton from './compose_form_button';
|
||||
|
||||
|
@ -19,7 +19,7 @@ const SpoilerButton: React.FC<ISpoilerButton> = ({ composeId }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const active = useAppSelector((state) => state.compose.get(composeId)!.spoiler);
|
||||
const active = useCompose(composeId).spoiler;
|
||||
|
||||
const onClick = () =>
|
||||
dispatch(changeComposeSpoilerness(composeId));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import UploadProgress from 'soapbox/components/upload-progress';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useCompose } from 'soapbox/hooks';
|
||||
|
||||
interface IComposeUploadProgress {
|
||||
composeId: string,
|
||||
|
@ -9,8 +9,10 @@ interface IComposeUploadProgress {
|
|||
|
||||
/** File upload progress bar for post composer. */
|
||||
const ComposeUploadProgress: React.FC<IComposeUploadProgress> = ({ composeId }) => {
|
||||
const active = useAppSelector((state) => state.compose.get(composeId)!.is_uploading);
|
||||
const progress = useAppSelector((state) => state.compose.get(composeId)!.progress);
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const active = compose.is_uploading;
|
||||
const progress = compose.progress;
|
||||
|
||||
if (!active) {
|
||||
return null;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { openModal } from 'soapbox/actions/modals';
|
|||
import Blurhash from 'soapbox/components/blurhash';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import Motion from '../../ui/util/optional_motion';
|
||||
|
||||
|
@ -71,7 +71,7 @@ const Upload: React.FC<IUpload> = ({ composeId, id }) => {
|
|||
const history = useHistory();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const media = useAppSelector((state) => state.compose.get(composeId)!.media_attachments.find(item => item.get('id') === id)!);
|
||||
const media = useCompose(composeId).media_attachments.find(item => item.get('id') === id)!;
|
||||
const descriptionLimit = useAppSelector((state) => state.instance.get('description_limit'));
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import classNames from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useCompose } from 'soapbox/hooks';
|
||||
|
||||
import SensitiveButton from './sensitive-button';
|
||||
import Upload from './upload';
|
||||
|
@ -14,7 +14,7 @@ interface IUploadForm {
|
|||
}
|
||||
|
||||
const UploadForm: React.FC<IUploadForm> = ({ composeId }) => {
|
||||
const mediaIds = useAppSelector((state) => state.compose.get(composeId)!.media_attachments.map((item: AttachmentEntity) => item.id));
|
||||
const mediaIds = useCompose(composeId).media_attachments.map((item: AttachmentEntity) => item.id);
|
||||
const classes = classNames('compose-form__uploads-wrapper', {
|
||||
'contains-media': mediaIds.size !== 0,
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ interface IQuotedStatusContainer {
|
|||
/** QuotedStatus shown in post composer. */
|
||||
const QuotedStatusContainer: React.FC<IQuotedStatusContainer> = ({ composeId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const status = useAppSelector(state => getStatus(state, { id: state.compose.get(composeId)!.quote! }));
|
||||
const status = useAppSelector(state => getStatus(state, { id: state.compose.get(composeId)?.quote! }));
|
||||
|
||||
const onCancel = () => {
|
||||
dispatch(cancelQuoteCompose());
|
||||
|
|
|
@ -12,8 +12,8 @@ const makeMapStateToProps = () => {
|
|||
const getStatus = makeGetStatus();
|
||||
|
||||
const mapStateToProps = (state: RootState, { composeId }: { composeId: string }) => {
|
||||
const statusId = state.compose.get(composeId)!.in_reply_to!;
|
||||
const editing = !!state.compose.get(composeId)!.id;
|
||||
const statusId = state.compose.get(composeId)?.in_reply_to!;
|
||||
const editing = !!state.compose.get(composeId)?.id;
|
||||
|
||||
return {
|
||||
status: getStatus(state, { id: statusId }) as Status,
|
||||
|
|
|
@ -8,8 +8,8 @@ import type { IntlShape } from 'react-intl';
|
|||
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||
|
||||
const mapStateToProps = (state: RootState, { composeId }: { composeId: string }) => ({
|
||||
disabled: state.compose.get(composeId)!.is_uploading,
|
||||
resetFileKey: state.compose.get(composeId)!.resetFileKey,
|
||||
disabled: state.compose.get(composeId)?.is_uploading,
|
||||
resetFileKey: state.compose.get(composeId)?.resetFileKey,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: AppDispatch, { composeId }: { composeId: string }) => ({
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import Warning from '../components/warning';
|
||||
|
||||
|
@ -13,11 +13,13 @@ interface IWarningWrapper {
|
|||
}
|
||||
|
||||
const WarningWrapper: React.FC<IWarningWrapper> = ({ composeId }) => {
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const me = useAppSelector((state) => state.me);
|
||||
|
||||
const needsLockWarning = useAppSelector(state => state.compose.get(composeId)!.privacy === 'private' && !state.accounts.get(me)!.locked);
|
||||
const hashtagWarning = useAppSelector(state => state.compose.get(composeId)!.privacy !== 'public' && APPROX_HASHTAG_RE.test(state.compose.get(composeId)!.text));
|
||||
const directMessageWarning = useAppSelector(state => state.compose.get(composeId)!.privacy === 'direct');
|
||||
const needsLockWarning = useAppSelector((state) => compose.privacy === 'private' && !state.accounts.get(me)!.locked);
|
||||
const hashtagWarning = compose.privacy !== 'public' && APPROX_HASHTAG_RE.test(compose.text);
|
||||
const directMessageWarning = compose.privacy === 'direct';
|
||||
|
||||
if (needsLockWarning) {
|
||||
return <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <Link to='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></Link> }} />} />;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { addToMentions, removeFromMentions } from 'soapbox/actions/compose';
|
|||
import Avatar from 'soapbox/components/avatar';
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -27,7 +27,7 @@ const Account: React.FC<IAccount> = ({ composeId, accountId, author }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
|
||||
const account = useAppSelector((state) => getAccount(state, accountId));
|
||||
const added = useAppSelector((state) => !!account && state.compose.get(composeId)!.to?.includes(account.acct));
|
||||
const added = !!account && useCompose(composeId).to?.includes(account.acct);
|
||||
|
||||
const onRemove = () => dispatch(removeFromMentions(accountId));
|
||||
const onAdd = () => dispatch(addToMentions(accountId));
|
||||
|
|
|
@ -5,7 +5,7 @@ import { cancelReplyCompose } from 'soapbox/actions/compose';
|
|||
import { openModal, closeModal } from 'soapbox/actions/modals';
|
||||
import { checkComposeContent } from 'soapbox/components/modal_root';
|
||||
import { Modal } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import ComposeForm from '../../compose/components/compose-form';
|
||||
|
||||
|
@ -23,7 +23,7 @@ const ComposeModal: React.FC<IComposeModal> = ({ onClose }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const compose = useAppSelector((state) => state.compose.get('compose-modal'));
|
||||
const compose = useCompose('compose-modal');
|
||||
|
||||
const { id: statusId, privacy, in_reply_to: inReplyTo, quote } = compose!;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Modal } from 'soapbox/components/ui';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
import { statusToMentionsAccountIdsArray } from 'soapbox/reducers/compose';
|
||||
import { makeGetStatus } from 'soapbox/selectors';
|
||||
|
||||
|
@ -16,7 +16,9 @@ interface IReplyMentionsModal {
|
|||
}
|
||||
|
||||
const ReplyMentionsModal: React.FC<IReplyMentionsModal> = ({ composeId, onClose }) => {
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.get(composeId)?.in_reply_to! }));
|
||||
const compose = useCompose(composeId);
|
||||
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: compose.in_reply_to! }));
|
||||
const account = useAppSelector((state) => state.accounts.get(state.me));
|
||||
|
||||
const mentions = statusToMentionsAccountIdsArray(status!, account!);
|
||||
|
|
|
@ -2,6 +2,7 @@ export { useAccount } from './useAccount';
|
|||
export { useApi } from './useApi';
|
||||
export { useAppDispatch } from './useAppDispatch';
|
||||
export { useAppSelector } from './useAppSelector';
|
||||
export { useCompose } from './useCompose';
|
||||
export { useDimensions } from './useDimensions';
|
||||
export { useFeatures } from './useFeatures';
|
||||
export { useLocale } from './useLocale';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import type { ReducerCompose } from 'soapbox/reducers/compose';
|
||||
|
||||
/** Get compose for given key with fallback to 'default' */
|
||||
export const useCompose = <ID extends string>(composeId: ID extends 'default' ? never : ID): ReturnType<typeof ReducerCompose> => {
|
||||
return useAppSelector((state) => state.compose.get(composeId, state.compose.get('default')!));
|
||||
};
|
|
@ -66,7 +66,7 @@ import type {
|
|||
Tag,
|
||||
} from 'soapbox/types/entities';
|
||||
|
||||
// const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
|
||||
const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
|
||||
|
||||
const PollRecord = ImmutableRecord({
|
||||
options: ImmutableList(['', '']),
|
||||
|
@ -138,14 +138,6 @@ export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: A
|
|||
.delete(account.id) as ImmutableOrderedSet<string>;
|
||||
};
|
||||
|
||||
function clearAll(compose: Compose) {
|
||||
return ReducerCompose({
|
||||
content_type: compose.default_content_type,
|
||||
privacy: compose.default_privacy,
|
||||
idempotencyKey: uuid(),
|
||||
});
|
||||
}
|
||||
|
||||
function appendMedia(compose: Compose, media: APIEntity) {
|
||||
const prevSize = compose.media_attachments.size;
|
||||
|
||||
|
@ -284,11 +276,10 @@ const updateSetting = (compose: Compose, path: string[], value: string) => {
|
|||
};
|
||||
|
||||
const updateCompose = (state: State, key: string, updater: (compose: Compose) => Compose) =>
|
||||
state.update(key, ReducerCompose(), updater);
|
||||
state.update(key, state.get('default')!, updater);
|
||||
|
||||
const initialState: State = ImmutableMap({
|
||||
default: ReducerCompose(),
|
||||
home: ReducerCompose(),
|
||||
default: ReducerCompose({ idempotencyKey: uuid(), resetFileKey: getResetFileKey() }),
|
||||
});
|
||||
|
||||
export default function compose(state = initialState, action: AnyAction) {
|
||||
|
@ -370,7 +361,7 @@ export default function compose(state = initialState, action: AnyAction) {
|
|||
case COMPOSE_QUOTE_CANCEL:
|
||||
case COMPOSE_RESET:
|
||||
case COMPOSE_SUBMIT_SUCCESS:
|
||||
return updateCompose(state, action.id, clearAll);
|
||||
return state.get('default')!.set('idempotencyKey', uuid());
|
||||
case COMPOSE_SUBMIT_FAIL:
|
||||
return updateCompose(state, action.id, compose => compose.set('is_submitting', false));
|
||||
case COMPOSE_UPLOAD_CHANGE_FAIL:
|
||||
|
|
Ładowanie…
Reference in New Issue