pl-fe: remove most of immutable from compose reducer

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-13 19:47:07 +01:00
parent 36b27a3410
commit a21f75c82c
9 changed files with 398 additions and 292 deletions

View file

@ -297,7 +297,7 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, c
const state = getState(); const state = getState();
const accountUrl = getAccount(state, state.me as string)!.url; const accountUrl = getAccount(state, state.me as string)!.url;
const draftId = getState().compose.get(composeId)!.draft_id; const draftId = getState().compose[composeId]!.draft_id;
dispatch(submitComposeSuccess(composeId, data, accountUrl, draftId)); dispatch(submitComposeSuccess(composeId, data, accountUrl, draftId));
if (data.scheduled_at === null) { if (data.scheduled_at === null) {
@ -315,7 +315,7 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, c
}; };
const needsDescriptions = (state: RootState, composeId: string) => { const needsDescriptions = (state: RootState, composeId: string) => {
const media = state.compose.get(composeId)!.media_attachments; const media = state.compose[composeId]!.media_attachments;
const missingDescriptionModal = useSettingsStore.getState().settings.missingDescriptionModal; const missingDescriptionModal = useSettingsStore.getState().settings.missingDescriptionModal;
const hasMissing = media.filter(item => !item.description).size > 0; const hasMissing = media.filter(item => !item.description).size > 0;
@ -324,7 +324,7 @@ const needsDescriptions = (state: RootState, composeId: string) => {
}; };
const validateSchedule = (state: RootState, composeId: string) => { const validateSchedule = (state: RootState, composeId: string) => {
const schedule = state.compose.get(composeId)?.schedule; const schedule = state.compose[composeId]?.schedule;
if (!schedule) return true; if (!schedule) return true;
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
@ -345,7 +345,7 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
if (!isLoggedIn(getState)) return; if (!isLoggedIn(getState)) return;
const state = getState(); const state = getState();
const compose = state.compose.get(composeId)!; const compose = state.compose[composeId]!;
const status = compose.text; const status = compose.text;
const media = compose.media_attachments; const media = compose.media_attachments;
@ -467,7 +467,7 @@ const uploadCompose = (composeId: string, files: FileList, intl: IntlShape) =>
if (!isLoggedIn(getState)) return; if (!isLoggedIn(getState)) return;
const attachmentLimit = getState().instance.configuration.statuses.max_media_attachments; const attachmentLimit = getState().instance.configuration.statuses.max_media_attachments;
const media = getState().compose.get(composeId)?.media_attachments; const media = getState().compose[composeId]?.media_attachments;
const progress = new Array(files.length).fill(0); const progress = new Array(files.length).fill(0);
let total = Array.from(files).reduce((a, v) => a + v.size, 0); let total = Array.from(files).reduce((a, v) => a + v.size, 0);
@ -673,10 +673,10 @@ interface ComposeSuggestionSelectAction {
position: number; position: number;
token: string | null; token: string | null;
completion: string; completion: string;
path: Array<string | number>; path: ['spoiler_text'] | ['poll', 'options', number];
} }
const selectComposeSuggestion = (composeId: string, position: number, token: string | null, suggestion: AutoSuggestion, path: Array<string | number>) => const selectComposeSuggestion = (composeId: string, position: number, token: string | null, suggestion: AutoSuggestion, path: ComposeSuggestionSelectAction['path']) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
let completion = '', startPosition = position; let completion = '', startPosition = position;
@ -719,7 +719,7 @@ const updateTagHistory = (composeId: string, tags: string[]) => ({
const insertIntoTagHistory = (composeId: string, recognizedTags: Array<Tag>, text: string) => const insertIntoTagHistory = (composeId: string, recognizedTags: Array<Tag>, text: string) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState(); const state = getState();
const oldHistory = state.compose.get(composeId)!.tagHistory; const oldHistory = state.compose[composeId]!.tagHistory;
const me = state.me; const me = state.me;
const names = recognizedTags const names = recognizedTags
.filter(tag => text.match(new RegExp(`#${tag.name}`, 'i'))) .filter(tag => text.match(new RegExp(`#${tag.name}`, 'i')))
@ -1100,5 +1100,6 @@ export {
addSuggestedLanguage, addSuggestedLanguage,
changeComposeFederated, changeComposeFederated,
type ComposeReplyAction, type ComposeReplyAction,
type ComposeSuggestionSelectAction,
type ComposeAction, type ComposeAction,
}; };

View file

@ -44,10 +44,10 @@ const saveDraftStatus = (composeId: string) =>
const state = getState(); const state = getState();
const accountUrl = getAccount(state, state.me as string)!.url; const accountUrl = getAccount(state, state.me as string)!.url;
const compose = state.compose.get(composeId)!; const compose = state.compose[composeId]!;
const draft = { const draft = {
...compose.toJS(), ...compose,
draft_id: compose.draft_id || crypto.randomUUID(), draft_id: compose.draft_id || crypto.randomUUID(),
}; };

View file

@ -10,7 +10,7 @@ import { usePrevious } from 'pl-fe/hooks/use-previous';
import { useModalsStore } from 'pl-fe/stores/modals'; import { useModalsStore } from 'pl-fe/stores/modals';
import type { ModalType } from 'pl-fe/features/ui/components/modal-root'; import type { ModalType } from 'pl-fe/features/ui/components/modal-root';
import type { ReducerCompose } from 'pl-fe/reducers/compose'; import type { Compose } from 'pl-fe/reducers/compose';
const messages = defineMessages({ const messages = defineMessages({
confirm: { id: 'confirmations.cancel.confirm', defaultMessage: 'Discard' }, confirm: { id: 'confirmations.cancel.confirm', defaultMessage: 'Discard' },
@ -18,7 +18,7 @@ const messages = defineMessages({
saveDraft: { id: 'confirmations.cancel_editing.save_draft', defaultMessage: 'Save draft' }, saveDraft: { id: 'confirmations.cancel_editing.save_draft', defaultMessage: 'Save draft' },
}); });
const checkComposeContent = (compose?: ReturnType<typeof ReducerCompose>) => const checkComposeContent = (compose?: Compose) =>
!!compose && [ !!compose && [
compose.editorState && compose.editorState.length > 0, compose.editorState && compose.editorState.length > 0,
compose.spoiler_text.length > 0, compose.spoiler_text.length > 0,
@ -59,7 +59,7 @@ const ModalRoot: React.FC<IModalRoot> = ({ children, onCancel, onClose, type })
const handleOnClose = () => { const handleOnClose = () => {
dispatch((_, getState) => { dispatch((_, getState) => {
const compose = getState().compose.get('compose-modal'); const compose = getState().compose['compose-modal'];
const hasComposeContent = checkComposeContent(compose); const hasComposeContent = checkComposeContent(compose);
if (hasComposeContent && type === 'COMPOSE') { if (hasComposeContent && type === 'COMPOSE') {

View file

@ -16,7 +16,7 @@ const ReplyGroupIndicator = (props: IReplyGroupIndicator) => {
const getStatus = useCallback(makeGetStatus(), []); const getStatus = useCallback(makeGetStatus(), []);
const status = useAppSelector((state) => getStatus(state, { id: state.compose.get(composeId)?.in_reply_to! })); const status = useAppSelector((state) => getStatus(state, { id: state.compose[composeId]?.in_reply_to! }));
const group = status?.group; const group = status?.group;
if (!group) { if (!group) {

View file

@ -15,7 +15,7 @@ const QuotedStatusContainer: React.FC<IQuotedStatusContainer> = ({ composeId })
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const getStatus = useCallback(makeGetStatus(), []); const getStatus = useCallback(makeGetStatus(), []);
const status = useAppSelector(state => getStatus(state, { id: state.compose.get(composeId)?.quote! })); const status = useAppSelector(state => getStatus(state, { id: state.compose[composeId]?.quote! }));
const onCancel = () => { const onCancel = () => {
dispatch(cancelQuoteCompose(composeId)); dispatch(cancelQuoteCompose(composeId));

View file

@ -113,7 +113,7 @@ const ComposeEditor = React.forwardRef<LexicalEditor, IComposeEditor>(({
theme, theme,
editorState: dispatch((_, getState) => { editorState: dispatch((_, getState) => {
const state = getState(); const state = getState();
const compose = state.compose.get(composeId); const compose = state.compose[composeId];
if (!compose) return; if (!compose) return;

View file

@ -29,7 +29,7 @@ const StatePlugin: React.FC<IStatePlugin> = ({ composeId, isWysiwyg }) => {
const getQuoteSuggestions = useCallback(debounce((text: string) => { const getQuoteSuggestions = useCallback(debounce((text: string) => {
dispatch(async (_, getState) => { dispatch(async (_, getState) => {
const state = getState(); const state = getState();
const compose = state.compose.get(composeId); const compose = state.compose[composeId];
if (!features.quotePosts || compose?.quote) return; if (!features.quotePosts || compose?.quote) return;
@ -60,7 +60,7 @@ const StatePlugin: React.FC<IStatePlugin> = ({ composeId, isWysiwyg }) => {
const detectLanguage = useCallback(debounce(async (text: string) => { const detectLanguage = useCallback(debounce(async (text: string) => {
dispatch(async (dispatch, getState) => { dispatch(async (dispatch, getState) => {
const state = getState(); const state = getState();
const compose = state.compose.get(composeId); const compose = state.compose[composeId];
if (!features.postLanguages || features.languageDetection || compose?.language) return; if (!features.postLanguages || features.languageDetection || compose?.language) return;

View file

@ -1,9 +1,9 @@
import { useAppSelector } from './use-app-selector'; import { useAppSelector } from './use-app-selector';
import type { ReducerCompose } from 'pl-fe/reducers/compose'; import type { Compose } from 'pl-fe/reducers/compose';
/** Get compose for given key with fallback to 'default' */ /** Get compose for given key with fallback to 'default' */
const useCompose = <ID extends string>(composeId: ID extends 'default' ? never : ID): ReturnType<typeof ReducerCompose> => const useCompose = <ID extends string>(composeId: ID extends 'default' ? never : ID): Compose =>
useAppSelector((state) => state.compose.get(composeId, state.compose.get('default')!)); useAppSelector((state) => state.compose[composeId] || state.compose.default);
export { useCompose }; export { useCompose };

View file

@ -1,4 +1,5 @@
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord, fromJS } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
import { create } from 'mutative';
import { Instance, PLEROMA, type CredentialAccount, type MediaAttachment, type Tag } from 'pl-api'; import { Instance, PLEROMA, type CredentialAccount, type MediaAttachment, type Tag } from 'pl-api';
import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance'; import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance';
@ -59,7 +60,8 @@ import {
COMPOSE_CHANGE_MEDIA_ORDER, COMPOSE_CHANGE_MEDIA_ORDER,
COMPOSE_ADD_SUGGESTED_QUOTE, COMPOSE_ADD_SUGGESTED_QUOTE,
COMPOSE_FEDERATED_CHANGE, COMPOSE_FEDERATED_CHANGE,
ComposeAction, type ComposeAction,
type ComposeSuggestionSelectAction,
} from '../actions/compose'; } from '../actions/compose';
import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET, type EventsAction } from '../actions/events'; import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET, type EventsAction } from '../actions/events';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me'; import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me';
@ -75,57 +77,108 @@ import type { APIEntity } from 'pl-fe/types/entities';
const getResetFileKey = () => Math.floor((Math.random() * 0x10000)); const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
const PollRecord = ImmutableRecord({ interface ComposePoll {
options: ImmutableList<string>;
options_map: ImmutableList<ImmutableMap<Language, string>>;
expires_in: number;
multiple: boolean;
hide_totals: boolean;
}
const newPoll = (params: Partial<ComposePoll> = {}): ComposePoll => ({
options: ImmutableList(['', '']), options: ImmutableList(['', '']),
options_map: ImmutableList<ImmutableMap<Language, string>>([ImmutableMap(), ImmutableMap()]), options_map: ImmutableList([ImmutableMap(), ImmutableMap()]),
expires_in: 24 * 3600, expires_in: 24 * 3600,
multiple: false, multiple: false,
hide_totals: false, hide_totals: false,
...params,
}); });
const ReducerCompose = ImmutableRecord({ interface Compose {
caretPosition: null as number | null, caretPosition: number | null;
content_type: string;
draft_id: string | null;
editorState: string | null;
editorStateMap: ImmutableMap<Language, string | null>;
focusDate: Date | null;
group_id: string | null;
idempotencyKey: string;
id: string | null;
in_reply_to: string | null;
is_changing_upload: boolean;
is_composing: boolean;
is_submitting: boolean;
is_uploading: boolean;
media_attachments: ImmutableList<MediaAttachment>;
poll: ComposePoll | null;
privacy: string;
progress: number;
quote: string | null;
resetFileKey: number | null;
schedule: Date | null;
sensitive: boolean;
spoiler_text: string;
spoilerTextMap: ImmutableMap<Language, string>;
suggestions: ImmutableList<string>;
suggestion_token: string | null;
tagHistory: ImmutableList<string>;
text: string;
textMap: ImmutableMap<Language, string>;
to: ImmutableOrderedSet<string>;
parent_reblogged_by: string | null;
dismissed_quotes: ImmutableOrderedSet<string>;
language: Language | null;
modified_language: Language | null;
suggested_language: string | null;
federated: boolean;
approvalRequired: boolean;
}
const newCompose = (params: Partial<Compose> = {}): Compose => ({
caretPosition: null,
content_type: 'text/plain', content_type: 'text/plain',
draft_id: null as string | null, draft_id: null,
editorState: null as string | null, editorState: null,
editorStateMap: ImmutableMap<Language, string | null>(), editorStateMap: ImmutableMap<Language, string | null>(),
focusDate: null as Date | null, focusDate: null,
group_id: null as string | null, group_id: null,
idempotencyKey: '', idempotencyKey: '',
id: null as string | null, id: null,
in_reply_to: null as string | null, in_reply_to: null,
is_changing_upload: false, is_changing_upload: false,
is_composing: false, is_composing: false,
is_submitting: false, is_submitting: false,
is_uploading: false, is_uploading: false,
media_attachments: ImmutableList<MediaAttachment>(), media_attachments: ImmutableList<MediaAttachment>(),
poll: null as Poll | null, poll: null,
privacy: 'public', privacy: 'public',
progress: 0, progress: 0,
quote: null as string | null, quote: null,
resetFileKey: null as number | null, resetFileKey: null,
schedule: null as Date | null, schedule: null,
sensitive: false, sensitive: false,
spoiler_text: '', spoiler_text: '',
spoilerTextMap: ImmutableMap<Language, string>(), spoilerTextMap: ImmutableMap<Language, string>(),
suggestions: ImmutableList<string>(), suggestions: ImmutableList<string>(),
suggestion_token: null as string | null, suggestion_token: null,
tagHistory: ImmutableList<string>(), tagHistory: ImmutableList<string>(),
text: '', text: '',
textMap: ImmutableMap<Language, string>(), textMap: ImmutableMap<Language, string>(),
to: ImmutableOrderedSet<string>(), to: ImmutableOrderedSet<string>(),
parent_reblogged_by: null as string | null, parent_reblogged_by: null,
dismissed_quotes: ImmutableOrderedSet<string>(), dismissed_quotes: ImmutableOrderedSet<string>(),
language: null as Language | null, language: null,
modified_language: null as Language | null, modified_language: null,
suggested_language: null as string | null, suggested_language: null,
federated: true, federated: true,
approvalRequired: false, approvalRequired: false,
...params,
}); });
type State = ImmutableMap<string, Compose>; type State = {
type Compose = ReturnType<typeof ReducerCompose>; default: Compose;
type Poll = ReturnType<typeof PollRecord>; [key: string]: Compose;
};
const statusToTextMentions = (status: Pick<Status, 'account' | 'mentions'>, account: Pick<Account, 'acct'>) => { const statusToTextMentions = (status: Pick<Status, 'account' | 'mentions'>, account: Pick<Account, 'acct'>) => {
const author = status.account.acct; const author = status.account.acct;
@ -160,53 +213,47 @@ const statusToMentionsAccountIdsArray = (status: Pick<Status, 'mentions' | 'acco
const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?: boolean) => { const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?: boolean) => {
const prevSize = compose.media_attachments.size; const prevSize = compose.media_attachments.size;
return compose.withMutations(map => { compose.media_attachments.push(media);
map.update('media_attachments', list => list.push(media)); compose.is_uploading = false;
map.set('is_uploading', false); compose.resetFileKey = Math.floor((Math.random() * 0x10000));
map.set('resetFileKey', Math.floor((Math.random() * 0x10000))); compose.idempotencyKey = crypto.randomUUID();
map.set('idempotencyKey', crypto.randomUUID());
if (prevSize === 0 && (defaultSensitive || compose.sensitive)) { if (prevSize === 0 && (defaultSensitive || compose.sensitive)) {
map.set('sensitive', true); compose.sensitive = true;
} }
});
}; };
const removeMedia = (compose: Compose, mediaId: string) => { const removeMedia = (compose: Compose, mediaId: string) => {
const prevSize = compose.media_attachments.size; const prevSize = compose.media_attachments.size;
return compose.withMutations(map => { compose.media_attachments = compose.media_attachments.filter(item => item.id !== mediaId);
map.update('media_attachments', list => list.filterNot(item => item.id === mediaId)); compose.idempotencyKey = crypto.randomUUID();
map.set('idempotencyKey', crypto.randomUUID());
if (prevSize === 1) { if (prevSize === 1) {
map.set('sensitive', false); compose.sensitive = false;
} }
});
}; };
const insertSuggestion = (compose: Compose, position: number, token: string | null, completion: string, path: Array<string | number>) => const insertSuggestion = (compose: Compose, position: number, token: string | null, completion: string, path: ComposeSuggestionSelectAction['path']) => {
compose.withMutations(map => { const updateText = (oldText?: string) => `${oldText?.slice(0, position)}${completion} ${oldText?.slice(position + (token?.length ?? 0))}`;
map.updateIn(path, oldText => `${(oldText as string).slice(0, position)}${completion} ${(oldText as string).slice(position + (token?.length ?? 0))}`); if (path[0] === 'spoiler_text') {
map.set('suggestion_token', null); compose.spoiler_text = updateText(compose.spoiler_text);
map.set('suggestions', ImmutableList()); } else if (compose.poll) {
if (path.length === 1 && path[0] === 'text') { compose.poll.options = compose.poll.options.update(path[2], updateText);
map.set('focusDate', new Date()); }
map.set('caretPosition', position + completion.length + 1); compose.suggestion_token = null;
} compose.suggestions = ImmutableList();
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
}); };
const updateSuggestionTags = (compose: Compose, token: string, tags: Tag[]) => { const updateSuggestionTags = (compose: Compose, token: string, tags: Tag[]) => {
const prefix = token.slice(1); const prefix = token.slice(1);
return compose.merge({ compose.suggestions = ImmutableList(tags
suggestions: ImmutableList(tags .filter((tag) => tag.name.toLowerCase().startsWith(prefix.toLowerCase()))
.filter((tag) => tag.name.toLowerCase().startsWith(prefix.toLowerCase())) .slice(0, 4)
.slice(0, 4) .map((tag) => '#' + tag.name));
.map((tag) => '#' + tag.name)), compose.suggestion_token = token;
suggestion_token: token,
});
}; };
const insertEmoji = (compose: Compose, position: number, emojiData: Emoji, needsSpace: boolean) => { const insertEmoji = (compose: Compose, position: number, emojiData: Emoji, needsSpace: boolean) => {
@ -214,12 +261,10 @@ const insertEmoji = (compose: Compose, position: number, emojiData: Emoji, needs
const emojiText = isNativeEmoji(emojiData) ? emojiData.native : emojiData.colons; const emojiText = isNativeEmoji(emojiData) ? emojiData.native : emojiData.colons;
const emoji = needsSpace ? ' ' + emojiText : emojiText; const emoji = needsSpace ? ' ' + emojiText : emojiText;
return compose.merge({ compose.text = `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`;
text: `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`, compose.focusDate = new Date();
focusDate: new Date(), compose.caretPosition = position + emoji.length + 1;
caretPosition: position + emoji.length + 1, compose.idempotencyKey = crypto.randomUUID();
idempotencyKey: crypto.randomUUID(),
});
}; };
const privacyPreference = (a: string, b: string) => { const privacyPreference = (a: string, b: string) => {
@ -257,13 +302,11 @@ const getExplicitMentions = (me: string, status: Pick<Status, 'content' | 'menti
const importAccount = (compose: Compose, account: CredentialAccount) => { const importAccount = (compose: Compose, account: CredentialAccount) => {
const settings = account.settings_store?.[FE_NAME]; const settings = account.settings_store?.[FE_NAME];
if (!settings) return compose; if (!settings) return;
return compose.withMutations(compose => { if (settings.defaultPrivacy) compose.privacy = settings.defaultPrivacy;
if (settings.defaultPrivacy) compose.set('privacy', settings.defaultPrivacy); if (settings.defaultContentType) compose.content_type = settings.defaultContentType;
if (settings.defaultContentType) compose.set('content_type', settings.defaultContentType); compose.tagHistory = ImmutableList(tagHistory.get(account.id));
compose.set('tagHistory', ImmutableList(tagHistory.get(account.id)));
});
}; };
// const updateSetting = (compose: Compose, path: string[], value: string) => { // const updateSetting = (compose: Compose, path: string[], value: string) => {
@ -281,323 +324,385 @@ const importAccount = (compose: Compose, account: CredentialAccount) => {
const updateDefaultContentType = (compose: Compose, instance: Instance) => { const updateDefaultContentType = (compose: Compose, instance: Instance) => {
const postFormats = instance.pleroma.metadata.post_formats; const postFormats = instance.pleroma.metadata.post_formats;
return compose.update('content_type', type => postFormats.includes(type) ? type : postFormats.includes('text/markdown') ? 'text/markdown' : postFormats[0]); compose.content_type = postFormats.includes(compose.content_type) ? compose.content_type : postFormats.includes('text/markdown') ? 'text/markdown' : postFormats[0];
}; };
const updateCompose = (state: State, key: string, updater: (compose: Compose) => Compose) => const updateCompose = (state: State, key: string, updater: (compose: Compose) => void) =>
state.update(key, state.get('default')!, updater); create(state, draft => {
draft[key] = draft[key] || create(draft.default, () => {});
updater(draft[key]);
});
// state.update(key, state.get('default')!, updater);
const initialState: State = ImmutableMap({ const initialState: State = {
default: ReducerCompose({ idempotencyKey: crypto.randomUUID(), resetFileKey: getResetFileKey() }), default: newCompose({ idempotencyKey: crypto.randomUUID(), resetFileKey: getResetFileKey() }),
}); };
const compose = (state = initialState, action: ComposeAction | EventsAction | InstanceAction | MeAction | TimelineAction) => { const compose = (state = initialState, action: ComposeAction | EventsAction | InstanceAction | MeAction | TimelineAction): State => {
switch (action.type) { switch (action.type) {
case COMPOSE_TYPE_CHANGE: case COMPOSE_TYPE_CHANGE:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('content_type', action.value); compose.content_type = action.value;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_SPOILERNESS_CHANGE: case COMPOSE_SPOILERNESS_CHANGE:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('sensitive', !compose.sensitive); compose.sensitive = !compose.sensitive;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_SPOILER_TEXT_CHANGE: case COMPOSE_SPOILER_TEXT_CHANGE:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {
return compose if (compose.modified_language === compose.language) {
.setIn(compose.modified_language === compose.language ? ['spoiler_text'] : ['spoilerTextMap', compose.modified_language], action.text) compose.spoiler_text = action.text;
.set('idempotencyKey', crypto.randomUUID()); } else {
compose.spoilerTextMap = compose.spoilerTextMap.set(compose.modified_language!, action.text);
}
}); });
case COMPOSE_VISIBILITY_CHANGE: case COMPOSE_VISIBILITY_CHANGE:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, compose => {
.set('privacy', action.value) compose.privacy = action.value;
.set('idempotencyKey', crypto.randomUUID())); compose.idempotencyKey = crypto.randomUUID();
});
case COMPOSE_LANGUAGE_CHANGE: case COMPOSE_LANGUAGE_CHANGE:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('language', action.value); compose.language = action.value;
map.set('modified_language', action.value); compose.modified_language = action.value;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_MODIFIED_LANGUAGE_CHANGE: case COMPOSE_MODIFIED_LANGUAGE_CHANGE:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('modified_language', action.value); compose.modified_language = action.value;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_CHANGE: case COMPOSE_CHANGE:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, compose => {
.set('text', action.text) compose.text = action.text;
.set('idempotencyKey', crypto.randomUUID())); compose.idempotencyKey = crypto.randomUUID();
});
case COMPOSE_REPLY: case COMPOSE_REPLY:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
const defaultCompose = state.get('default')!; const defaultCompose = state.default!;
const to = action.explicitAddressing const to = action.explicitAddressing
? statusToMentionsArray(action.status, action.account, action.rebloggedBy) ? statusToMentionsArray(action.status, action.account, action.rebloggedBy)
: ImmutableOrderedSet<string>(); : ImmutableOrderedSet<string>();
map.set('group_id', action.status.group_id); compose.group_id = action.status.group_id;
map.set('in_reply_to', action.status.id); compose.in_reply_to = action.status.id;
map.set('to', to); compose.to = to;
map.set('parent_reblogged_by', action.rebloggedBy?.id || null); compose.parent_reblogged_by = action.rebloggedBy?.id || null;
map.set('text', !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : ''); compose.text = !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : '';
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy)); compose.privacy = privacyPreference(action.status.visibility, defaultCompose.privacy);
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
map.set('content_type', defaultCompose.content_type); compose.content_type = defaultCompose.content_type;
map.set('approvalRequired', action.approvalRequired || false); compose.approvalRequired = action.approvalRequired || false;
if (action.preserveSpoilers && action.status.spoiler_text) { if (action.preserveSpoilers && action.status.spoiler_text) {
map.set('sensitive', true); compose.sensitive = true;
map.set('spoiler_text', action.status.spoiler_text); compose.spoiler_text = action.status.spoiler_text;
} }
})); });
case COMPOSE_EVENT_REPLY: case COMPOSE_EVENT_REPLY:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('in_reply_to', action.status.id); compose.in_reply_to = action.status.id;
map.set('to', statusToMentionsArray(action.status, action.account)); compose.to = statusToMentionsArray(action.status, action.account);
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_QUOTE: case COMPOSE_QUOTE:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => { return updateCompose(state, 'compose-modal', compose => {
const author = action.status.account.acct; const author = action.status.account.acct;
const defaultCompose = state.get('default')!; const defaultCompose = state.default;
map.set('quote', action.status.id); compose.quote = action.status.id;
map.set('to', ImmutableOrderedSet<string>([author])); compose.to = ImmutableOrderedSet<string>([author]);
map.set('parent_reblogged_by', null); compose.parent_reblogged_by = null;
map.set('text', ''); compose.text = '';
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy)); compose.privacy = privacyPreference(action.status.visibility, defaultCompose.privacy);
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
map.set('content_type', defaultCompose.content_type); compose.content_type = defaultCompose.content_type;
map.set('spoiler_text', ''); compose.spoiler_text = '';
if (action.status.visibility === 'group') { if (action.status.visibility === 'group') {
map.set('group_id', action.status.group_id); compose.group_id = action.status.group_id;
map.set('privacy', 'group'); compose.privacy = 'group';
} }
})); });
case COMPOSE_SUBMIT_REQUEST: case COMPOSE_SUBMIT_REQUEST:
return updateCompose(state, action.composeId, compose => compose.set('is_submitting', true)); return updateCompose(state, action.composeId, compose => {
compose.is_submitting = true;
});
case COMPOSE_UPLOAD_CHANGE_REQUEST: case COMPOSE_UPLOAD_CHANGE_REQUEST:
return updateCompose(state, action.composeId, compose => compose.set('is_changing_upload', true)); return updateCompose(state, action.composeId, compose => {
compose.is_changing_upload = true;
});
case COMPOSE_REPLY_CANCEL: case COMPOSE_REPLY_CANCEL:
case COMPOSE_RESET: case COMPOSE_RESET:
case COMPOSE_SUBMIT_SUCCESS: case COMPOSE_SUBMIT_SUCCESS:
return updateCompose(state, action.composeId, () => state.get('default')!.withMutations(map => { return create(state, (draft) => {
map.set('idempotencyKey', crypto.randomUUID()); draft[action.composeId] = newCompose({
map.set('in_reply_to', action.composeId.startsWith('reply:') ? action.composeId.slice(6) : null); idempotencyKey: crypto.randomUUID(),
if (action.composeId.startsWith('group:')) { in_reply_to: action.composeId.startsWith('reply:') ? action.composeId.slice(6) : null,
map.set('privacy', 'group'); ...(action.composeId.startsWith('group:') ? {
map.set('group_id', action.composeId.slice(6)); privacy: 'group',
} group_id: action.composeId.slice(6),
})); } : undefined),
});
});
case COMPOSE_SUBMIT_FAIL: case COMPOSE_SUBMIT_FAIL:
return updateCompose(state, action.composeId, compose => compose.set('is_submitting', false)); return updateCompose(state, action.composeId, compose => {
compose.is_submitting = false;
});
case COMPOSE_UPLOAD_CHANGE_FAIL: case COMPOSE_UPLOAD_CHANGE_FAIL:
return updateCompose(state, action.composeId, compose => compose.set('is_changing_upload', false)); return updateCompose(state, action.composeId, compose => {
compose.is_changing_upload = false;
});
case COMPOSE_UPLOAD_REQUEST: case COMPOSE_UPLOAD_REQUEST:
return updateCompose(state, action.composeId, compose => compose.set('is_uploading', true)); return updateCompose(state, action.composeId, compose => {
compose.is_uploading = true;
});
case COMPOSE_UPLOAD_SUCCESS: case COMPOSE_UPLOAD_SUCCESS:
return updateCompose(state, action.composeId, compose => appendMedia(compose, action.media, state.get('default')!.sensitive)); return updateCompose(state, action.composeId, compose => appendMedia(compose, action.media, state.default.sensitive));
case COMPOSE_UPLOAD_FAIL: case COMPOSE_UPLOAD_FAIL:
return updateCompose(state, action.composeId, compose => compose.set('is_uploading', false)); return updateCompose(state, action.composeId, compose => {
compose.is_uploading = false;
});
case COMPOSE_UPLOAD_UNDO: case COMPOSE_UPLOAD_UNDO:
return updateCompose(state, action.composeId, compose => removeMedia(compose, action.mediaId)); return updateCompose(state, action.composeId, compose => removeMedia(compose, action.mediaId));
case COMPOSE_UPLOAD_PROGRESS: case COMPOSE_UPLOAD_PROGRESS:
return updateCompose(state, action.composeId, compose => compose.set('progress', Math.round((action.loaded / action.total) * 100))); return updateCompose(state, action.composeId, compose => {
compose.progress = Math.round((action.loaded / action.total) * 100);
});
case COMPOSE_MENTION: case COMPOSE_MENTION:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => { return updateCompose(state, 'compose-modal', compose => {
map.update('text', text => [text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' ')); compose.text = [compose.text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' ');
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_DIRECT: case COMPOSE_DIRECT:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => { return updateCompose(state, 'compose-modal', compose => {
map.update('text', text => [text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' ')); compose.text = [compose.text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' ');
map.set('privacy', 'direct'); compose.privacy = 'direct';
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_GROUP_POST: case COMPOSE_GROUP_POST:
return updateCompose(state, action.composeId, compose => compose.withMutations(map => { return updateCompose(state, action.composeId, compose => {
map.set('privacy', 'group'); compose.privacy = 'group';
map.set('group_id', action.groupId); compose.group_id = action.groupId;
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
})); });
case COMPOSE_SUGGESTIONS_CLEAR: case COMPOSE_SUGGESTIONS_CLEAR:
return updateCompose(state, action.composeId, compose => compose.update('suggestions', list => list?.clear()).set('suggestion_token', null)); return updateCompose(state, action.composeId, compose => {
compose.suggestions = compose.suggestions.clear();
compose.suggestion_token = null;
});
case COMPOSE_SUGGESTIONS_READY: case COMPOSE_SUGGESTIONS_READY:
return updateCompose(state, action.composeId, compose => compose.set('suggestions', ImmutableList(action.accounts ? action.accounts.map((item: APIEntity) => item.id) : action.emojis)).set('suggestion_token', action.token)); return updateCompose(state, action.composeId, compose => {
compose.suggestions = ImmutableList(action.accounts ? action.accounts.map((item: APIEntity) => item.id) : action.emojis);
compose.suggestion_token = action.token;
});
case COMPOSE_SUGGESTION_SELECT: case COMPOSE_SUGGESTION_SELECT:
return updateCompose(state, action.composeId, compose => insertSuggestion(compose, action.position, action.token, action.completion, action.path)); return updateCompose(state, action.composeId, compose => insertSuggestion(compose, action.position, action.token, action.completion, action.path));
case COMPOSE_SUGGESTION_TAGS_UPDATE: case COMPOSE_SUGGESTION_TAGS_UPDATE:
return updateCompose(state, action.composeId, compose => updateSuggestionTags(compose, action.token, action.tags)); return updateCompose(state, action.composeId, compose => updateSuggestionTags(compose, action.token, action.tags));
case COMPOSE_TAG_HISTORY_UPDATE: case COMPOSE_TAG_HISTORY_UPDATE:
return updateCompose(state, action.composeId, compose => compose.set('tagHistory', ImmutableList(fromJS(action.tags)) as ImmutableList<string>)); return updateCompose(state, action.composeId, compose => {
compose.tagHistory = ImmutableList(fromJS(action.tags)) as ImmutableList<string>;
});
case TIMELINE_DELETE: case TIMELINE_DELETE:
return updateCompose(state, 'compose-modal', compose => { return updateCompose(state, 'compose-modal', compose => {
if (action.statusId === compose.in_reply_to) { if (action.statusId === compose.in_reply_to) {
return compose.set('in_reply_to', null); compose.in_reply_to = null;
} if (action.statusId === compose.quote) { } if (action.statusId === compose.quote) {
return compose.set('quote', null); compose.quote = null;
} else {
return compose;
} }
}); });
case COMPOSE_EMOJI_INSERT: case COMPOSE_EMOJI_INSERT:
return updateCompose(state, action.composeId, compose => insertEmoji(compose, action.position, action.emoji, action.needsSpace)); return updateCompose(state, action.composeId, compose => insertEmoji(compose, action.position, action.emoji, action.needsSpace));
case COMPOSE_UPLOAD_CHANGE_SUCCESS: case COMPOSE_UPLOAD_CHANGE_SUCCESS:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, compose => {
.set('is_changing_upload', false) compose.is_changing_upload = false;
.update('media_attachments', list => list.map(item => {
compose.media_attachments = compose.media_attachments.map(item => {
if (item.id === action.media.id) { if (item.id === action.media.id) {
return action.media; return action.media;
} }
return item; return item;
}))); });
});
case COMPOSE_SET_STATUS: case COMPOSE_SET_STATUS:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => { return updateCompose(state, 'compose-modal', compose => {
const to = action.explicitAddressing ? getExplicitMentions(action.status.account.id, action.status) : ImmutableOrderedSet<string>(); const to = action.explicitAddressing ? getExplicitMentions(action.status.account.id, action.status) : ImmutableOrderedSet<string>();
if (!action.withRedraft && !action.draftId) { if (!action.withRedraft && !action.draftId) {
map.set('id', action.status.id); compose.id = action.status.id;
} }
map.set('text', action.rawText || unescapeHTML(expandMentions(action.status))); compose.text = action.rawText || unescapeHTML(expandMentions(action.status));
map.set('to', to); compose.to = to;
map.set('parent_reblogged_by', null); compose.parent_reblogged_by = null;
map.set('in_reply_to', action.status.in_reply_to_id); compose.in_reply_to = action.status.in_reply_to_id;
map.set('privacy', action.status.visibility); compose.privacy = action.status.visibility;
map.set('focusDate', new Date()); compose.focusDate = new Date();
map.set('caretPosition', null); compose.caretPosition = null;
map.set('idempotencyKey', crypto.randomUUID()); compose.idempotencyKey = crypto.randomUUID();
map.set('content_type', action.contentType || 'text/plain'); compose.content_type = action.contentType || 'text/plain';
map.set('quote', action.status.quote_id); compose.quote = action.status.quote_id;
map.set('group_id', action.status.group_id); compose.group_id = action.status.group_id;
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) { if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) {
map.set('media_attachments', ImmutableList()); compose.media_attachments = ImmutableList();
} else { } else {
map.set('media_attachments', ImmutableList(action.status.media_attachments)); compose.media_attachments = ImmutableList(action.status.media_attachments);
} }
if (action.status.spoiler_text.length > 0) { if (action.status.spoiler_text.length > 0) {
map.set('spoiler_text', action.status.spoiler_text); compose.spoiler_text = action.status.spoiler_text;
} else { } else {
map.set('spoiler_text', ''); compose.spoiler_text = '';
} }
if (action.poll) { if (action.poll) {
map.set('poll', PollRecord({ compose.poll = newPoll({
options: ImmutableList(action.poll.options.map(({ title }) => title)), options: ImmutableList(action.poll.options.map(({ title }) => title)),
multiple: action.poll.multiple, multiple: action.poll.multiple,
expires_in: 24 * 3600, expires_in: 24 * 3600,
})); });
} }
if (action.draftId) { if (action.draftId) {
map.set('draft_id', action.draftId); compose.draft_id = action.draftId;
} }
if (action.editorState) { if (action.editorState) {
map.set('editorState', action.editorState); compose.editorState = action.editorState;
} }
})); });
case COMPOSE_POLL_ADD: case COMPOSE_POLL_ADD:
return updateCompose(state, action.composeId, compose => compose.set('poll', PollRecord())); return updateCompose(state, action.composeId, compose => {
compose.poll = newPoll();
});
case COMPOSE_POLL_REMOVE: case COMPOSE_POLL_REMOVE:
return updateCompose(state, action.composeId, compose => compose.set('poll', null)); return updateCompose(state, action.composeId, compose => {
compose.poll = null;
});
case COMPOSE_SCHEDULE_ADD: case COMPOSE_SCHEDULE_ADD:
return updateCompose(state, action.composeId, compose => compose.set('schedule', new Date(Date.now() + 10 * 60 * 1000))); return updateCompose(state, action.composeId, compose => {
compose.schedule = new Date(Date.now() + 10 * 60 * 1000);
});
case COMPOSE_SCHEDULE_SET: case COMPOSE_SCHEDULE_SET:
return updateCompose(state, action.composeId, compose => compose.set('schedule', action.date)); return updateCompose(state, action.composeId, compose => {
compose.schedule = action.date;
});
case COMPOSE_SCHEDULE_REMOVE: case COMPOSE_SCHEDULE_REMOVE:
return updateCompose(state, action.composeId, compose => compose.set('schedule', null)); return updateCompose(state, action.composeId, compose => {
compose.schedule = null;
});
case COMPOSE_POLL_OPTION_ADD: case COMPOSE_POLL_OPTION_ADD:
return updateCompose(state, action.composeId, compose => return updateCompose(state, action.composeId, compose => {
compose if (!compose.poll) return;
.updateIn(['poll', 'options'], options => (options as ImmutableList<string>).push(action.title)) compose.poll.options = compose.poll.options.push(action.title);
.updateIn(['poll', 'options_map'], options_map => (options_map as ImmutableList<ImmutableMap<Language, string>>).push(ImmutableMap(compose.textMap.map(_ => action.title)))), compose.poll.options_map = compose.poll.options_map.push(ImmutableMap(compose.textMap.map(_ => action.title)));
); });
case COMPOSE_POLL_OPTION_CHANGE: case COMPOSE_POLL_OPTION_CHANGE:
return updateCompose(state, action.composeId, compose => return updateCompose(state, action.composeId, compose => {
compose.setIn(!compose.modified_language || compose.modified_language === compose.language ? ['poll', 'options', action.index] : ['poll', 'options_map', action.index, compose.modified_language], action.title), if (!compose.poll) return;
); if (!compose.modified_language || compose.modified_language === compose.language) {
compose.poll.options = compose.poll.options.set(action.index, action.title);
compose.poll.options_map = compose.poll.options_map.setIn([action.index, compose.modified_language], action.title);
}
});
case COMPOSE_POLL_OPTION_REMOVE: case COMPOSE_POLL_OPTION_REMOVE:
return updateCompose(state, action.composeId, compose => return updateCompose(state, action.composeId, compose => {
compose if (!compose.poll) return;
.updateIn(['poll', 'options'], options => (options as ImmutableList<string>).delete(action.index)) compose.poll.options = compose.poll.options.delete(action.index);
.updateIn(['poll', 'options_map'], options_map => (options_map as ImmutableList<ImmutableMap<Language, string>>).delete(action.index)), compose.poll.options_map = compose.poll.options_map.delete(action.index);
); });
case COMPOSE_POLL_SETTINGS_CHANGE: case COMPOSE_POLL_SETTINGS_CHANGE:
return updateCompose(state, action.composeId, compose => compose.update('poll', poll => { return updateCompose(state, action.composeId, compose => {
if (!poll) return null; if (!compose.poll) return null;
return poll.withMutations((poll) => { if (action.expiresIn) {
if (action.expiresIn) { compose.poll.expires_in = action.expiresIn;
poll.set('expires_in', action.expiresIn); }
} if (typeof action.isMultiple === 'boolean') {
if (typeof action.isMultiple === 'boolean') { compose.poll.multiple = action.isMultiple;
poll.set('multiple', action.isMultiple); }
} });
});
}));
case COMPOSE_ADD_TO_MENTIONS: case COMPOSE_ADD_TO_MENTIONS:
return updateCompose(state, action.composeId, compose => compose.update('to', mentions => mentions!.add(action.account))); return updateCompose(state, action.composeId, compose => {
compose.to = compose.to.add(action.account);
});
case COMPOSE_REMOVE_FROM_MENTIONS: case COMPOSE_REMOVE_FROM_MENTIONS:
return updateCompose(state, action.composeId, compose => compose.update('to', mentions => mentions!.delete(action.account))); return updateCompose(state, action.composeId, compose => {
compose.to = compose.to.delete(action.account);
});
case ME_FETCH_SUCCESS: case ME_FETCH_SUCCESS:
case ME_PATCH_SUCCESS: case ME_PATCH_SUCCESS:
return updateCompose(state, 'default', compose => importAccount(compose, action.me)); return updateCompose(state, 'default', compose => importAccount(compose, action.me));
// case SETTING_CHANGE: // case SETTING_CHANGE:
// return updateCompose(state, 'default', compose => updateSetting(compose, action.path, action.value)); // return updateCompose(state, 'default', compose => updateSetting(compose, action.path, action.value));
case COMPOSE_EDITOR_STATE_SET: case COMPOSE_EDITOR_STATE_SET:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, compose => {
.setIn(!compose.modified_language || compose.modified_language === compose.language ? ['editorState'] : ['editorStateMap', compose.modified_language], action.editorState as string) if (!compose.modified_language || compose.modified_language) {
.setIn(!compose.modified_language || compose.modified_language === compose.language ? ['text'] : ['textMap', compose.modified_language], action.text as string)); compose.editorState = action.editorState as string;
compose.text = action.text as string;
} else {
compose.editorStateMap = compose.editorStateMap.set(compose.modified_language, action.editorState as string);
compose.textMap = compose.textMap.set(compose.modified_language, action.text as string);
}
});
case EVENT_COMPOSE_CANCEL: case EVENT_COMPOSE_CANCEL:
return updateCompose(state, 'event-compose-modal', compose => compose.set('text', '')); return updateCompose(state, 'event-compose-modal', compose => {
compose.text = '';
});
case EVENT_FORM_SET: case EVENT_FORM_SET:
return updateCompose(state, action.composeId, compose => compose.set('text', action.text)); return updateCompose(state, action.composeId, compose => {
compose.text = action.text;
});
case COMPOSE_CHANGE_MEDIA_ORDER: case COMPOSE_CHANGE_MEDIA_ORDER:
return updateCompose(state, action.composeId, compose => compose.update('media_attachments', list => { return updateCompose(state, action.composeId, compose => {
const indexA = list.findIndex(x => x.id === action.a); const indexA = compose.media_attachments.findIndex(x => x.id === action.a);
const moveItem = list.get(indexA)!; const moveItem = compose.media_attachments.get(indexA)!;
const indexB = list.findIndex(x => x.id === action.b); const indexB = compose.media_attachments.findIndex(x => x.id === action.b);
return list.splice(indexA, 1).splice(indexB, 0, moveItem); return compose.media_attachments.splice(indexA, 1).splice(indexB, 0, moveItem);
})); });
case COMPOSE_ADD_SUGGESTED_QUOTE: case COMPOSE_ADD_SUGGESTED_QUOTE:
return updateCompose(state, action.composeId, compose => compose.set('quote', action.quoteId)); return updateCompose(state, action.composeId, compose => {
compose.quote = action.quoteId;
});
case COMPOSE_ADD_SUGGESTED_LANGUAGE: case COMPOSE_ADD_SUGGESTED_LANGUAGE:
return updateCompose(state, action.composeId, compose => compose.set('suggested_language', action.language)); return updateCompose(state, action.composeId, compose => {
compose.suggested_language = action.language;
});
case COMPOSE_LANGUAGE_ADD: case COMPOSE_LANGUAGE_ADD:
return updateCompose(state, action.composeId, compose => return updateCompose(state, action.composeId, compose => {
compose compose.editorStateMap = compose.editorStateMap.set(action.value, compose.editorState);
.setIn(['editorStateMap', action.value], compose.editorState) compose.textMap = compose.textMap.set(action.value, compose.text);
.setIn(['textMap', action.value], compose.text) compose.spoilerTextMap = compose.spoilerTextMap.set(action.value, compose.spoiler_text);
.setIn(['spoilerTextMap', action.value], compose.spoiler_text) if (compose.poll) compose.poll.options_map = compose.poll.options_map.map((option, key) => option.set(action.value, compose.poll!.options.get(key)!));
.update('poll', poll => { });
if (!poll) return poll;
return poll.update('options_map', optionsMap => optionsMap.map((option, key) => option.set(action.value, poll.options.get(key)!)));
}),
);
case COMPOSE_LANGUAGE_DELETE: case COMPOSE_LANGUAGE_DELETE:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, compose => {
.removeIn(['editorStateMap', action.value]) compose.editorStateMap = compose.editorStateMap.delete(action.value);
.removeIn(['textMap', action.value]) compose.textMap = compose.textMap.delete(action.value);
.removeIn(['spoilerTextMap', action.value])); compose.spoilerTextMap = compose.spoilerTextMap.delete(action.value);
});
case COMPOSE_QUOTE_CANCEL: case COMPOSE_QUOTE_CANCEL:
return updateCompose(state, action.composeId, compose => compose return updateCompose(state, action.composeId, (compose) => {
.update('dismissed_quotes', quotes => compose.quote ? quotes.add(compose.quote) : quotes) if (compose.quote) compose.dismissed_quotes = compose.dismissed_quotes.add(compose.quote);
.set('quote', null)); compose.quote = null;
});
case COMPOSE_FEDERATED_CHANGE: case COMPOSE_FEDERATED_CHANGE:
return updateCompose(state, action.composeId, compose => compose.update('federated', value => !value)); return updateCompose(state, action.composeId, compose => {
compose.federated = !compose.federated;
});
case INSTANCE_FETCH_SUCCESS: case INSTANCE_FETCH_SUCCESS:
return updateCompose(state, 'default', (compose) => updateDefaultContentType(compose, action.instance)); return updateCompose(state, 'default', (compose) => updateDefaultContentType(compose, action.instance));
default: default:
@ -606,7 +711,7 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
}; };
export { export {
ReducerCompose, type Compose,
statusToMentionsAccountIdsArray, statusToMentionsAccountIdsArray,
initialState, initialState,
compose as default, compose as default,