mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-05 08:33:00 +00:00
Removes Immutable Map from composer typing.
This commit is contained in:
parent
39a3ffaf2f
commit
d11894e7af
|
@ -90,7 +90,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
export const ensureComposeIsVisible = (getState) => {
|
||||
if (!getState().getIn(['compose', 'mounted'])) {
|
||||
if (!getState().compose.mounted) {
|
||||
browserHistory.push('/publish');
|
||||
}
|
||||
};
|
||||
|
@ -184,10 +184,14 @@ export function directCompose(account) {
|
|||
}
|
||||
|
||||
export function submitCompose() {
|
||||
/**
|
||||
* @param {import('../store').AppDispatch} dispatch
|
||||
* @param {() => import('../store').RootState} getState
|
||||
*/
|
||||
return function (dispatch, getState) {
|
||||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const statusId = getState().getIn(['compose', 'id'], null);
|
||||
const status = getState().compose.text;
|
||||
const media = getState().compose.media_attachments;
|
||||
const statusId = getState().compose.id;
|
||||
|
||||
if ((!status || !status.length) && media.size === 0) {
|
||||
return;
|
||||
|
@ -220,17 +224,17 @@ export function submitCompose() {
|
|||
method: statusId === null ? 'post' : 'put',
|
||||
data: {
|
||||
status,
|
||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||
in_reply_to_id: getState().compose.in_reply_to,
|
||||
media_ids: media.map(item => item.get('id')),
|
||||
media_attributes,
|
||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
||||
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
poll: getState().getIn(['compose', 'poll'], null),
|
||||
language: getState().getIn(['compose', 'language']),
|
||||
sensitive: getState().compose.sensitive,
|
||||
spoiler_text: getState().compose.spoiler ? getState().compose.spoiler_text : '',
|
||||
visibility: getState().compose.privacy,
|
||||
poll: getState().compose.poll,
|
||||
language: getState().compose.language,
|
||||
},
|
||||
headers: {
|
||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||
'Idempotency-Key': getState().compose.idempotencyKey,
|
||||
},
|
||||
}).then(function (response) {
|
||||
if ((browserHistory.location.pathname === '/publish' || browserHistory.location.pathname === '/statuses/new') && window.history.state) {
|
||||
|
@ -296,11 +300,16 @@ export function submitComposeFail(error) {
|
|||
};
|
||||
}
|
||||
|
||||
/** @param {FileList} files */
|
||||
export function uploadCompose(files) {
|
||||
/**
|
||||
* @param {import('../store').AppDispatch} dispatch
|
||||
* @param {() => import('../store').RootState} getState
|
||||
*/
|
||||
return function (dispatch, getState) {
|
||||
const uploadLimit = getState().getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']);
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const pending = getState().getIn(['compose', 'pending_media_attachments']);
|
||||
const media = getState().compose.media_attachments;
|
||||
const pending = getState().compose.pending_media_attachments;
|
||||
const progress = new Array(files.length).fill(0);
|
||||
|
||||
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
||||
|
@ -310,7 +319,7 @@ export function uploadCompose(files) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (getState().getIn(['compose', 'poll'])) {
|
||||
if (getState().compose.poll) {
|
||||
dispatch(showAlert({ message: messages.uploadErrorPoll }));
|
||||
return;
|
||||
}
|
||||
|
@ -438,7 +447,7 @@ export function changeUploadCompose(id, params) {
|
|||
return (dispatch, getState) => {
|
||||
dispatch(changeUploadComposeRequest());
|
||||
|
||||
let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id);
|
||||
let media = getState().compose.media_attachments.find((item) => item.get('id') === id);
|
||||
|
||||
// Editing already-attached media is deferred to editing the post itself.
|
||||
// For simplicity's sake, fake an API reply.
|
||||
|
@ -701,7 +710,7 @@ export function hydrateCompose() {
|
|||
function insertIntoTagHistory(recognizedTags, text) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const oldHistory = state.getIn(['compose', 'tagHistory']);
|
||||
const oldHistory = state.compose.tagHistory;
|
||||
const me = state.getIn(['meta', 'me']);
|
||||
|
||||
// FIXME: Matching input hashtags with recognized hashtags has become more
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { apiUpdateMedia } from 'mastodon/api/compose';
|
||||
import type { ApiMediaAttachmentJSON } from 'mastodon/api_types/media_attachments';
|
||||
|
@ -29,6 +29,10 @@ const simulateModifiedApiResponse = (
|
|||
return data;
|
||||
};
|
||||
|
||||
export const quoteComposeById = createAction<number>(
|
||||
'compose/quoteComposeById',
|
||||
);
|
||||
|
||||
export const changeUploadCompose = createDataLoadingThunk(
|
||||
'compose/changeUpload',
|
||||
async (
|
||||
|
@ -42,11 +46,9 @@ export const changeUploadCompose = createDataLoadingThunk(
|
|||
},
|
||||
{ getState },
|
||||
) => {
|
||||
const media = (
|
||||
(getState().compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
).find((item) => item.get('id') === id);
|
||||
const media = getState().compose.media_attachments.find(
|
||||
(item) => item.get('id') === id,
|
||||
);
|
||||
|
||||
// Editing already-attached media is deferred to editing the post itself.
|
||||
// For simplicity's sake, fake an API reply.
|
||||
|
|
|
@ -64,7 +64,7 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
if (state.compose.text.trim().length !== 0) {
|
||||
dispatch(openModal({ modalType: 'CONFIRM_REPLY', modalProps: { status } }));
|
||||
} else {
|
||||
dispatch(replyCompose(status));
|
||||
|
@ -118,7 +118,7 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
|
|||
onEdit (status) {
|
||||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
if (state.compose.text.trim().length !== 0) {
|
||||
dispatch(openModal({ modalType: 'CONFIRM_EDIT_STATUS', modalProps: { statusId: status.get('id') } }));
|
||||
} else {
|
||||
dispatch(editStatus(status.get('id')));
|
||||
|
|
|
@ -10,8 +10,6 @@ import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import { useSpring, animated } from '@react-spring/web';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import { length } from 'stringz';
|
||||
|
@ -28,7 +26,6 @@ import { CharacterCounter } from 'mastodon/features/compose/components/character
|
|||
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
|
||||
import { Video, getPointerPosition } from 'mastodon/features/video';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import type { MediaAttachment } from 'mastodon/models/media_attachment';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
import { assetHost } from 'mastodon/utils/config';
|
||||
|
||||
|
@ -113,11 +110,7 @@ const Preview: React.FC<{
|
|||
immediate: draggingRef.current,
|
||||
});
|
||||
const media = useAppSelector((state) =>
|
||||
(
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
).find((x) => x.get('id') === mediaId),
|
||||
state.compose.media_attachments.find((x) => x.get('id') === mediaId),
|
||||
);
|
||||
const account = useAppSelector((state) =>
|
||||
me ? state.accounts.get(me) : undefined,
|
||||
|
@ -253,18 +246,9 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const media = useAppSelector((state) =>
|
||||
(
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
).find((x) => x.get('id') === mediaId),
|
||||
);
|
||||
const lang = useAppSelector(
|
||||
(state) =>
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'language',
|
||||
) as string,
|
||||
state.compose.media_attachments.find((x) => x.get('id') === mediaId),
|
||||
);
|
||||
const lang = useAppSelector((state) => state.compose.language);
|
||||
const focusX =
|
||||
(media?.getIn(['meta', 'focus', 'x'], 0) as number | undefined) ?? 0;
|
||||
const focusY =
|
||||
|
|
|
@ -22,7 +22,7 @@ const messages = defineMessages({
|
|||
export const EditIndicator = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const id = useSelector(state => state.getIn(['compose', 'id']));
|
||||
const id = useSelector(state => state.compose.id);
|
||||
const status = useSelector(state => state.getIn(['statuses', id]));
|
||||
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
|
||||
|
||||
|
|
|
@ -332,10 +332,8 @@ export const LanguageDropdown: React.FC = () => {
|
|||
const intl = useIntl();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const value = useAppSelector(
|
||||
(state) => state.compose.get('language') as string,
|
||||
);
|
||||
const text = useAppSelector((state) => state.compose.get('text') as string);
|
||||
const value = useAppSelector((state) => state.compose.language);
|
||||
const text = useAppSelector((state) => state.compose.text);
|
||||
|
||||
const current =
|
||||
(preloadedLanguages as Language[]).find((lang) => lang[0] === value) ?? [];
|
||||
|
|
|
@ -16,9 +16,7 @@ const messages = defineMessages({
|
|||
export const NavigationBar: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const isReplying = useAppSelector(
|
||||
(state) => !!state.compose.get('in_reply_to'),
|
||||
);
|
||||
const isReplying = useAppSelector((state) => !!state.compose.in_reply_to);
|
||||
|
||||
const handleCancelClick = useCallback(() => {
|
||||
dispatch(cancelReplyCompose());
|
||||
|
|
|
@ -56,8 +56,8 @@ Select.propTypes = {
|
|||
const Option = ({ multipleChoice, index, title, autoFocus }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const suggestions = useSelector(state => state.getIn(['compose', 'suggestions']));
|
||||
const lang = useSelector(state => state.getIn(['compose', 'language']));
|
||||
const suggestions = useSelector(state => state.compose.suggestions);
|
||||
const lang = useSelector(state => state.compose.language);
|
||||
const maxOptions = useSelector(state => state.getIn(['server', 'server', 'configuration', 'polls', 'max_options']));
|
||||
|
||||
const handleChange = useCallback(({ target: { value } }) => {
|
||||
|
@ -108,7 +108,7 @@ Option.propTypes = {
|
|||
export const PollForm = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const poll = useSelector(state => state.getIn(['compose', 'poll']));
|
||||
const poll = useSelector(state => state.compose.poll);
|
||||
const options = poll?.get('options');
|
||||
const expiresIn = poll?.get('expires_in');
|
||||
const isMultiple = poll?.get('multiple');
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Icon } from 'mastodon/components/icon';
|
|||
import { EmbeddedStatusContent } from 'mastodon/features/notifications_v2/components/embedded_status_content';
|
||||
|
||||
export const ReplyIndicator = () => {
|
||||
const inReplyToId = useSelector(state => state.getIn(['compose', 'in_reply_to']));
|
||||
const inReplyToId = useSelector(state => state.compose.in_reply_to);
|
||||
const status = useSelector(state => state.getIn(['statuses', inReplyToId]));
|
||||
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
|
||||
|
@ -16,7 +14,6 @@ import { undoUploadCompose } from 'mastodon/actions/compose';
|
|||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import type { MediaAttachment } from 'mastodon/models/media_attachment';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
export const Upload: React.FC<{
|
||||
|
@ -29,15 +26,9 @@ export const Upload: React.FC<{
|
|||
}> = ({ id, dragging, draggable = true, overlay, tall, wide }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const media = useAppSelector((state) =>
|
||||
(
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
).find((item) => item.get('id') === id),
|
||||
);
|
||||
const sensitive = useAppSelector(
|
||||
(state) => state.compose.get('spoiler') as boolean,
|
||||
state.compose.media_attachments.find((item) => item.get('id') === id),
|
||||
);
|
||||
const sensitive = useAppSelector((state) => state.compose.spoiler);
|
||||
|
||||
const handleUndoClick = useCallback(() => {
|
||||
dispatch(undoUploadCompose(id));
|
||||
|
|
|
@ -2,11 +2,7 @@ import { useState, useCallback, useMemo } from 'react';
|
|||
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import type {
|
||||
List,
|
||||
Map as ImmutableMap,
|
||||
List as ImmutableList,
|
||||
} from 'immutable';
|
||||
import type { List } from 'immutable';
|
||||
|
||||
import type {
|
||||
DragStartEvent,
|
||||
|
@ -67,21 +63,13 @@ export const UploadForm: React.FC = () => {
|
|||
const intl = useIntl();
|
||||
const mediaIds = useAppSelector(
|
||||
(state) =>
|
||||
(
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
).map((item: MediaAttachment) => item.get('id')) as List<string>,
|
||||
);
|
||||
const active = useAppSelector(
|
||||
(state) => state.compose.get('is_uploading') as boolean,
|
||||
);
|
||||
const progress = useAppSelector(
|
||||
(state) => state.compose.get('progress') as number,
|
||||
);
|
||||
const isProcessing = useAppSelector(
|
||||
(state) => state.compose.get('is_processing') as boolean,
|
||||
state.compose.media_attachments.map((item: MediaAttachment) =>
|
||||
item.get('id'),
|
||||
) as List<string>,
|
||||
);
|
||||
const active = useAppSelector((state) => state.compose.is_uploading);
|
||||
const progress = useAppSelector((state) => state.compose.progress);
|
||||
const isProcessing = useAppSelector((state) => state.compose.is_processing);
|
||||
const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
|
|
|
@ -10,9 +10,9 @@ import type { RootState } from 'mastodon/store';
|
|||
import { HASHTAG_PATTERN_REGEX } from 'mastodon/utils/hashtags';
|
||||
|
||||
const selector = createSelector(
|
||||
(state: RootState) => state.compose.get('privacy') as string,
|
||||
(state: RootState) => state.compose.privacy,
|
||||
(state: RootState) => !!state.accounts.getIn([me, 'locked']),
|
||||
(state: RootState) => state.compose.get('text') as string,
|
||||
(state: RootState) => state.compose.text,
|
||||
(privacy, locked, text) => ({
|
||||
needsLockWarning: privacy === 'private' && !locked,
|
||||
hashtagWarning: privacy !== 'public' && HASHTAG_PATTERN_REGEX.test(text),
|
||||
|
|
|
@ -15,22 +15,22 @@ import { openModal } from 'mastodon/actions/modal';
|
|||
import ComposeForm from '../components/compose_form';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
spoiler: state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
focusDate: state.getIn(['compose', 'focusDate']),
|
||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
isEditing: state.getIn(['compose', 'id']) !== null,
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
missingAltText: state.getIn(['compose', 'media_attachments']).some(media => ['image', 'gifv'].includes(media.get('type')) && (media.get('description') ?? '').length === 0),
|
||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
text: state.compose.text,
|
||||
suggestions: state.compose.suggestions,
|
||||
spoiler: state.compose.spoiler,
|
||||
spoilerText: state.compose.spoiler_text,
|
||||
privacy: state.compose.privacy,
|
||||
focusDate: state.compose.focusDate,
|
||||
caretPosition: state.compose.caretPosition,
|
||||
preselectDate: state.compose.preselectDate,
|
||||
isSubmitting: state.compose.is_submitting,
|
||||
isEditing: state.compose.id !== null,
|
||||
isChangingUpload: state.compose.is_changing_upload,
|
||||
isUploading: state.compose.is_uploading,
|
||||
anyMedia: state.compose.media_attachments.size > 0,
|
||||
missingAltText: state.compose.media_attachments.some(media => ['image', 'gifv'].includes(media.get('type')) && (media.get('description') ?? '').length === 0),
|
||||
isInReply: state.compose.in_reply_to !== null,
|
||||
lang: state.compose.language,
|
||||
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
||||
});
|
||||
|
||||
|
|
|
@ -4,15 +4,15 @@ import { addPoll, removePoll } from '../../../actions/compose';
|
|||
import PollButton from '../components/poll_button';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
|
||||
active: state.getIn(['compose', 'poll']) !== null,
|
||||
disabled: state.compose.is_uploading || (state.compose.media_attachments.size > 0),
|
||||
active: state.compose.poll !== null,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onClick () {
|
||||
dispatch((_, getState) => {
|
||||
if (getState().getIn(['compose', 'poll'])) {
|
||||
if (getState().compose.poll) {
|
||||
dispatch(removePoll());
|
||||
} else {
|
||||
dispatch(addPoll());
|
||||
|
|
|
@ -6,7 +6,7 @@ import { isUserTouching } from '../../../is_mobile';
|
|||
import PrivacyDropdown from '../components/privacy_dropdown';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
value: state.getIn(['compose', 'privacy']),
|
||||
value: state.compose.privacy,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -14,8 +14,8 @@ const messages = defineMessages({
|
|||
|
||||
const mapStateToProps = (state, { intl }) => ({
|
||||
iconComponent: WarningIcon,
|
||||
title: intl.formatMessage(state.getIn(['compose', 'spoiler']) ? messages.marked : messages.unmarked),
|
||||
active: state.getIn(['compose', 'spoiler']),
|
||||
title: intl.formatMessage(state.compose.spoiler ? messages.marked : messages.unmarked),
|
||||
active: state.compose.spoiler,
|
||||
ariaControls: 'cw-spoiler-input',
|
||||
size: 18,
|
||||
inverted: true,
|
||||
|
|
|
@ -4,17 +4,17 @@ import { uploadCompose } from '../../../actions/compose';
|
|||
import UploadButton from '../components/upload_button';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const isPoll = state.getIn(['compose', 'poll']) !== null;
|
||||
const isUploading = state.getIn(['compose', 'is_uploading']);
|
||||
const readyAttachmentsSize = state.getIn(['compose', 'media_attachments']).size ?? 0;
|
||||
const pendingAttachmentsSize = state.getIn(['compose', 'pending_media_attachments']).size ?? 0;
|
||||
const isPoll = state.compose.poll !== null;
|
||||
const isUploading = state.compose.is_uploading;
|
||||
const readyAttachmentsSize = state.compose.media_attachments.size ?? 0;
|
||||
const pendingAttachmentsSize = state.compose.pending_media_attachments.size ?? 0;
|
||||
const attachmentsSize = readyAttachmentsSize + pendingAttachmentsSize;
|
||||
const isOverLimit = attachmentsSize > state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments'])-1;
|
||||
const hasVideoOrAudio = state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')));
|
||||
const hasVideoOrAudio = state.compose.media_attachments.some(m => ['video', 'audio'].includes(m.get('type')));
|
||||
|
||||
return {
|
||||
disabled: isPoll || isUploading || isOverLimit || hasVideoOrAudio,
|
||||
resetFileKey: state.getIn(['compose', 'resetFileKey']),
|
||||
resetFileKey: state.compose.resetFileKey,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown })
|
|||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
if (state.compose.text.trim().length !== 0) {
|
||||
dispatch(openModal({ modalType: 'CONFIRM_REPLY', modalProps: { status: lastStatus } }));
|
||||
} else {
|
||||
dispatch(replyCompose(lastStatus));
|
||||
|
|
|
@ -69,7 +69,7 @@ export const Footer: React.FC<{
|
|||
const status = useAppSelector((state) => getStatus(state, { id: statusId }));
|
||||
const account = status?.get('account') as Account | undefined;
|
||||
const askReplyConfirmation = useAppSelector(
|
||||
(state) => (state.compose.get('text') as string).trim().length !== 0,
|
||||
(state) => state.compose.text.trim().length !== 0,
|
||||
);
|
||||
|
||||
const handleReplyClick = useCallback(() => {
|
||||
|
|
|
@ -97,7 +97,7 @@ const makeMapStateToProps = () => {
|
|||
status,
|
||||
ancestorsIds,
|
||||
descendantsIds,
|
||||
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
askReplyConfirmation: state.compose.text.trim().length !== 0,
|
||||
domain: state.getIn(['meta', 'domain']),
|
||||
pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }),
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ export const BoostModal: React.FC<{
|
|||
const intl = useIntl();
|
||||
|
||||
const defaultPrivacy = useAppSelector(
|
||||
(state) => state.compose.get('default_privacy') as StatusVisibility,
|
||||
(state) => state.compose.default_privacy,
|
||||
);
|
||||
|
||||
const statusId = status.get('id') as string;
|
||||
|
|
|
@ -21,7 +21,7 @@ export const ComposePanel: React.FC = () => {
|
|||
}, [dispatch]);
|
||||
const { signedIn } = useIdentity();
|
||||
const hideComposer = useAppSelector((state) => {
|
||||
const mounted = state.compose.get('mounted');
|
||||
const mounted = state.compose.mounted;
|
||||
if (typeof mounted === 'number') {
|
||||
return mounted > 1;
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ export const ComposePanel: React.FC = () => {
|
|||
export const RedirectToMobileComposeIfNeeded: React.FC = () => {
|
||||
const history = useAppHistory();
|
||||
|
||||
const shouldRedirect = useAppSelector((state) =>
|
||||
state.compose.get('should_redirect_to_compose_page'),
|
||||
const shouldRedirect = useAppSelector(
|
||||
(state) => state.compose.should_redirect_to_compose_page,
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
|
|
@ -55,7 +55,7 @@ const DiscardDraftConfirmationModal: React.FC<
|
|||
} & BaseConfirmationModalProps
|
||||
> = ({ onConfirm, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const isEditing = useAppSelector((state) => !!state.compose.get('id'));
|
||||
const isEditing = useAppSelector((state) => !!state.compose.id);
|
||||
|
||||
const contextualMessages = isEditing ? editMessages : postMessages;
|
||||
|
||||
|
|
|
@ -2,11 +2,8 @@ import { useCallback } from 'react';
|
|||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { submitCompose } from 'mastodon/actions/compose';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import type { MediaAttachment } from 'mastodon/models/media_attachment';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import type { BaseConfirmationModalProps } from './confirmation_modal';
|
||||
|
@ -39,11 +36,7 @@ export const ConfirmMissingAltTextModal: React.FC<
|
|||
const dispatch = useAppDispatch();
|
||||
const mediaId = useAppSelector(
|
||||
(state) =>
|
||||
(
|
||||
(state.compose as ImmutableMap<string, unknown>).get(
|
||||
'media_attachments',
|
||||
) as ImmutableList<MediaAttachment>
|
||||
)
|
||||
state.compose.media_attachments
|
||||
.find(
|
||||
(media) =>
|
||||
['image', 'gifv'].includes(media.get('type') as string) &&
|
||||
|
|
|
@ -89,10 +89,10 @@ const messages = defineMessages({
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
layout: state.getIn(['meta', 'layout']),
|
||||
isComposing: state.getIn(['compose', 'is_composing']),
|
||||
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']),
|
||||
isComposing: state.compose.is_composing,
|
||||
hasComposingText: state.compose.text.trim().length !== 0,
|
||||
hasMediaAttachments: state.compose.media_attachments.size > 0,
|
||||
canUploadMore: !state.compose.media_attachments.some(x => ['audio', 'video'].includes(x.get('type'))) && state.compose.media_attachments.size < state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments']),
|
||||
firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
|
||||
newAccount: !state.getIn(['accounts', me, 'note']) && !state.getIn(['accounts', me, 'bot']) && state.getIn(['accounts', me, 'following_count'], 0) === 0 && state.getIn(['accounts', me, 'statuses_count'], 0) === 0,
|
||||
username: state.getIn(['accounts', me, 'username']),
|
||||
|
|
|
@ -52,44 +52,7 @@ import { me } from '../initial_state';
|
|||
import { unescapeHTML } from '../utils/html';
|
||||
import { uuid } from '../uuid';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
mounted: 0,
|
||||
sensitive: false,
|
||||
spoiler: false,
|
||||
spoiler_text: '',
|
||||
privacy: null,
|
||||
id: null,
|
||||
text: '',
|
||||
focusDate: null,
|
||||
caretPosition: null,
|
||||
preselectDate: null,
|
||||
in_reply_to: null,
|
||||
is_composing: false,
|
||||
is_submitting: false,
|
||||
is_changing_upload: false,
|
||||
is_uploading: false,
|
||||
should_redirect_to_compose_page: false,
|
||||
progress: 0,
|
||||
isUploadingThumbnail: false,
|
||||
thumbnailProgress: 0,
|
||||
media_attachments: ImmutableList(),
|
||||
pending_media_attachments: 0,
|
||||
poll: null,
|
||||
suggestion_token: null,
|
||||
suggestions: ImmutableList(),
|
||||
default_privacy: 'public',
|
||||
default_sensitive: false,
|
||||
default_language: 'en',
|
||||
resetFileKey: Math.floor((Math.random() * 0x10000)),
|
||||
idempotencyKey: null,
|
||||
tagHistory: ImmutableList(),
|
||||
});
|
||||
|
||||
const initialPoll = ImmutableMap({
|
||||
options: ImmutableList(['', '']),
|
||||
expires_in: 24 * 3600,
|
||||
multiple: false,
|
||||
});
|
||||
import { initialState, initialPollState } from './compose_typed';
|
||||
|
||||
function statusToTextMentions(state, status) {
|
||||
let set = ImmutableOrderedSet([]);
|
||||
|
@ -321,7 +284,7 @@ export const composeReducer = (state = initialState, action) => {
|
|||
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
return hydrate(state, action.state.get('compose'));
|
||||
return hydrate(state, action.state.compose);
|
||||
case COMPOSE_MOUNT:
|
||||
return state
|
||||
.set('mounted', state.get('mounted') + 1)
|
||||
|
@ -538,7 +501,7 @@ export const composeReducer = (state = initialState, action) => {
|
|||
}
|
||||
});
|
||||
case COMPOSE_POLL_ADD:
|
||||
return state.set('poll', initialPoll);
|
||||
return state.set('poll', initialPollState);
|
||||
case COMPOSE_POLL_REMOVE:
|
||||
return state.set('poll', null);
|
||||
case COMPOSE_POLL_OPTION_CHANGE:
|
||||
|
|
104
app/javascript/mastodon/reducers/compose_typed.ts
Normal file
104
app/javascript/mastodon/reducers/compose_typed.ts
Normal file
|
@ -0,0 +1,104 @@
|
|||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
import { quoteComposeById } from '../actions/compose_typed';
|
||||
import type { StatusVisibility } from '../api_types/statuses';
|
||||
import type { ApiHashtagJSON } from '../api_types/tags';
|
||||
import type { CustomEmoji } from '../models/custom_emoji';
|
||||
import type { MediaAttachment } from '../models/media_attachment';
|
||||
|
||||
type SuggestionEmoji = CustomEmoji & { type: 'emoji' };
|
||||
type SuggestionHashtag = ApiHashtagJSON & { type: 'hashtag' };
|
||||
interface SuggestionAccount {
|
||||
type: 'account';
|
||||
id: string;
|
||||
}
|
||||
type Suggestion = SuggestionEmoji | SuggestionHashtag | SuggestionAccount;
|
||||
|
||||
export interface ComposeShape {
|
||||
mounted: number;
|
||||
sensitive: boolean;
|
||||
spoiler: boolean;
|
||||
spoiler_text: string;
|
||||
privacy: StatusVisibility | null;
|
||||
id: string | null;
|
||||
text: string;
|
||||
language: string;
|
||||
focusDate: Date | null;
|
||||
caretPosition: number | null;
|
||||
preselectDate: Date | null;
|
||||
in_reply_to: string | null;
|
||||
is_composing: boolean;
|
||||
is_submitting: boolean;
|
||||
is_changing_upload: boolean;
|
||||
is_uploading: boolean;
|
||||
is_processing: boolean;
|
||||
should_redirect_to_compose_page: boolean;
|
||||
progress: number;
|
||||
isUploadingThumbnail: boolean;
|
||||
thumbnailProgress: number;
|
||||
media_attachments: ImmutableList<MediaAttachment>;
|
||||
pending_media_attachments: number;
|
||||
poll: ComposePollShape | null;
|
||||
suggestion_token: string | null;
|
||||
suggestions: ImmutableList<Suggestion>;
|
||||
default_privacy: StatusVisibility;
|
||||
default_sensitive: boolean;
|
||||
default_language: string;
|
||||
resetFileKey: number;
|
||||
idempotencyKey: string | null;
|
||||
tagHistory: ImmutableList<string>;
|
||||
}
|
||||
|
||||
export const initialState: ComposeShape = {
|
||||
mounted: 0,
|
||||
sensitive: false,
|
||||
spoiler: false,
|
||||
spoiler_text: '',
|
||||
privacy: null,
|
||||
id: null,
|
||||
text: '',
|
||||
language: 'en',
|
||||
focusDate: null,
|
||||
caretPosition: null,
|
||||
preselectDate: null,
|
||||
in_reply_to: null,
|
||||
is_composing: false,
|
||||
is_submitting: false,
|
||||
is_changing_upload: false,
|
||||
is_uploading: false,
|
||||
is_processing: false,
|
||||
should_redirect_to_compose_page: false,
|
||||
progress: 0,
|
||||
isUploadingThumbnail: false,
|
||||
thumbnailProgress: 0,
|
||||
media_attachments: ImmutableList(),
|
||||
pending_media_attachments: 0,
|
||||
poll: null,
|
||||
suggestion_token: null,
|
||||
suggestions: ImmutableList(),
|
||||
default_privacy: 'public',
|
||||
default_sensitive: false,
|
||||
default_language: 'en',
|
||||
resetFileKey: Math.floor(Math.random() * 0x10000),
|
||||
idempotencyKey: null,
|
||||
tagHistory: ImmutableList(),
|
||||
};
|
||||
|
||||
export interface ComposePollShape {
|
||||
options: string[];
|
||||
expires_in: number;
|
||||
multiple: boolean;
|
||||
}
|
||||
|
||||
export const initialPollState = {
|
||||
options: ['', ''],
|
||||
expires_in: 24 * 3600,
|
||||
multiple: false,
|
||||
} satisfies ComposePollShape;
|
||||
|
||||
export const composeReducer = createReducer(initialState, (builder) => {
|
||||
builder.addCase(quoteComposeById, (state, action) => {
|
||||
state.id = action.payload.toString(); // Temp
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user