Make Compose reducer type-safe

environments/review-compose-sa-0lryjs/deployments/3552
Alex Gleason 1 year ago
parent 118cbd5994
commit de0b05d691
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -31,61 +31,60 @@ const { CancelToken, isCancel } = axios;
let cancelFetchComposeSuggestionsAccounts: Canceler;
const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
const COMPOSE_REPLY = 'COMPOSE_REPLY';
const COMPOSE_EVENT_REPLY = 'COMPOSE_EVENT_REPLY';
const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
const COMPOSE_QUOTE = 'COMPOSE_QUOTE';
const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL';
const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
const COMPOSE_MENTION = 'COMPOSE_MENTION';
const COMPOSE_RESET = 'COMPOSE_RESET';
const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
const COMPOSE_GROUP_POST = 'COMPOSE_GROUP_POST';
const COMPOSE_SET_GROUP_TIMELINE_VISIBLE = 'COMPOSE_SET_GROUP_TIMELINE_VISIBLE';
const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE';
const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
const COMPOSE_SCHEDULE_ADD = 'COMPOSE_SCHEDULE_ADD';
const COMPOSE_SCHEDULE_SET = 'COMPOSE_SCHEDULE_SET';
const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE';
const COMPOSE_ADD_TO_MENTIONS = 'COMPOSE_ADD_TO_MENTIONS';
const COMPOSE_REMOVE_FROM_MENTIONS = 'COMPOSE_REMOVE_FROM_MENTIONS';
const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
const COMPOSE_CHANGE = 'COMPOSE_CHANGE' as const;
const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST' as const;
const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS' as const;
const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL' as const;
const COMPOSE_REPLY = 'COMPOSE_REPLY' as const;
const COMPOSE_EVENT_REPLY = 'COMPOSE_EVENT_REPLY' as const;
const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL' as const;
const COMPOSE_QUOTE = 'COMPOSE_QUOTE' as const;
const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL' as const;
const COMPOSE_DIRECT = 'COMPOSE_DIRECT' as const;
const COMPOSE_MENTION = 'COMPOSE_MENTION' as const;
const COMPOSE_RESET = 'COMPOSE_RESET' as const;
const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST' as const;
const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS' as const;
const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL' as const;
const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS' as const;
const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO' as const;
const COMPOSE_GROUP_POST = 'COMPOSE_GROUP_POST' as const;
const COMPOSE_SET_GROUP_TIMELINE_VISIBLE = 'COMPOSE_SET_GROUP_TIMELINE_VISIBLE' as const;
const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR' as const;
const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY' as const;
const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT' as const;
const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE' as const;
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE' as const;
const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE' as const;
const COMPOSE_TYPE_CHANGE = 'COMPOSE_TYPE_CHANGE' as const;
const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE' as const;
const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE' as const;
const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE' as const;
const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT' as const;
const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST' as const;
const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS' as const;
const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL' as const;
const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD' as const;
const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE' as const;
const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD' as const;
const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE' as const;
const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE' as const;
const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE' as const;
const COMPOSE_SCHEDULE_ADD = 'COMPOSE_SCHEDULE_ADD' as const;
const COMPOSE_SCHEDULE_SET = 'COMPOSE_SCHEDULE_SET' as const;
const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE' as const;
const COMPOSE_ADD_TO_MENTIONS = 'COMPOSE_ADD_TO_MENTIONS' as const;
const COMPOSE_REMOVE_FROM_MENTIONS = 'COMPOSE_REMOVE_FROM_MENTIONS' as const;
const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS' as const;
const messages = defineMessages({
exceededImageSizeLimit: { id: 'upload_error.image_size_limit', defaultMessage: 'Image exceeds the current file size limit ({limit})' },
@ -101,12 +100,24 @@ const messages = defineMessages({
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
});
interface ComposeSetStatusAction {
type: typeof COMPOSE_SET_STATUS
id: string
status: Status
rawText: string
explicitAddressing: boolean
spoilerText?: string
contentType?: string | false
v: ReturnType<typeof parseVersion>
withRedraft?: boolean
}
const setComposeToStatus = (status: Status, rawText: string, spoilerText?: string, contentType?: string | false, withRedraft?: boolean) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const { instance } = getState();
const { explicitAddressing } = getFeatures(instance);
dispatch({
const action: ComposeSetStatusAction = {
type: COMPOSE_SET_STATUS,
id: 'compose-modal',
status,
@ -116,7 +127,9 @@ const setComposeToStatus = (status: Status, rawText: string, spoilerText?: strin
contentType,
v: parseVersion(instance.version),
withRedraft,
});
};
dispatch(action);
};
const changeCompose = (composeId: string, text: string) => ({
@ -125,20 +138,29 @@ const changeCompose = (composeId: string, text: string) => ({
text: text,
});
interface ComposeReplyAction {
type: typeof COMPOSE_REPLY
id: string
status: Status
account: Account
explicitAddressing: boolean
}
const replyCompose = (status: Status) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const instance = state.instance;
const { explicitAddressing } = getFeatures(instance);
dispatch({
const action: ComposeReplyAction = {
type: COMPOSE_REPLY,
id: 'compose-modal',
status: status,
account: state.accounts.get(state.me),
account: state.accounts.get(state.me)!,
explicitAddressing,
});
};
dispatch(action);
dispatch(openModal('COMPOSE'));
};
@ -147,20 +169,29 @@ const cancelReplyCompose = () => ({
id: 'compose-modal',
});
interface ComposeQuoteAction {
type: typeof COMPOSE_QUOTE
id: string
status: Status
account: Account | undefined
explicitAddressing: boolean
}
const quoteCompose = (status: Status) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const instance = state.instance;
const { explicitAddressing } = getFeatures(instance);
dispatch({
const action: ComposeQuoteAction = {
type: COMPOSE_QUOTE,
id: 'compose-modal',
status: status,
account: state.accounts.get(state.me),
explicitAddressing,
});
};
dispatch(action);
dispatch(openModal('COMPOSE'));
};
@ -182,38 +213,54 @@ const resetCompose = (composeId = 'compose-modal') => ({
id: composeId,
});
interface ComposeMentionAction {
type: typeof COMPOSE_MENTION
id: string
account: Account
}
const mentionCompose = (account: Account) =>
(dispatch: AppDispatch) => {
dispatch({
const action: ComposeMentionAction = {
type: COMPOSE_MENTION,
id: 'compose-modal',
account: account,
});
};
dispatch(action);
dispatch(openModal('COMPOSE'));
};
interface ComposeDirectAction {
type: typeof COMPOSE_DIRECT
id: string
account: Account
}
const directCompose = (account: Account) =>
(dispatch: AppDispatch) => {
dispatch({
const action: ComposeDirectAction = {
type: COMPOSE_DIRECT,
id: 'compose-modal',
account: account,
});
account,
};
dispatch(action);
dispatch(openModal('COMPOSE'));
};
const directComposeById = (accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const account = getState().accounts.get(accountId);
if (!account) return;
dispatch({
const action: ComposeDirectAction = {
type: COMPOSE_DIRECT,
id: 'compose-modal',
account: account,
});
account,
};
dispatch(action);
dispatch(openModal('COMPOSE'));
};
@ -487,14 +534,11 @@ const undoUploadCompose = (composeId: string, media_id: string) => ({
media_id: media_id,
});
const groupCompose = (composeId: string, groupId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({
type: COMPOSE_GROUP_POST,
id: composeId,
group_id: groupId,
});
};
const groupCompose = (composeId: string, groupId: string) => ({
type: COMPOSE_GROUP_POST,
id: composeId,
group_id: groupId,
});
const setGroupTimelineVisible = (composeId: string, groupTimelineVisible: boolean) => ({
type: COMPOSE_SET_GROUP_TIMELINE_VISIBLE,
@ -564,6 +608,14 @@ const fetchComposeSuggestions = (composeId: string, token: string) =>
}
};
interface ComposeSuggestionsReadyAction {
type: typeof COMPOSE_SUGGESTIONS_READY
id: string
token: string
emojis?: Emoji[]
accounts?: APIEntity[]
}
const readyComposeSuggestionsEmojis = (composeId: string, token: string, emojis: Emoji[]) => ({
type: COMPOSE_SUGGESTIONS_READY,
id: composeId,
@ -578,6 +630,15 @@ const readyComposeSuggestionsAccounts = (composeId: string, token: string, accou
accounts,
});
interface ComposeSuggestionSelectAction {
type: typeof COMPOSE_SUGGESTION_SELECT
id: string
position: number
token: string | null
completion: string
path: Array<string | number>
}
const selectComposeSuggestion = (composeId: string, position: number, token: string | null, suggestion: AutoSuggestion, path: Array<string | number>) =>
(dispatch: AppDispatch, getState: () => RootState) => {
let completion, startPosition;
@ -595,14 +656,16 @@ const selectComposeSuggestion = (composeId: string, position: number, token: str
startPosition = position;
}
dispatch({
const action: ComposeSuggestionSelectAction = {
type: COMPOSE_SUGGESTION_SELECT,
id: composeId,
position: startPosition,
token,
completion,
path,
});
};
dispatch(action);
};
const updateSuggestionTags = (composeId: string, token: string, currentTrends: ImmutableList<Tag>) => ({
@ -712,7 +775,7 @@ const removePollOption = (composeId: string, index: number) => ({
index,
});
const changePollSettings = (composeId: string, expiresIn?: string | number, isMultiple?: boolean) => ({
const changePollSettings = (composeId: string, expiresIn?: number, isMultiple?: boolean) => ({
type: COMPOSE_POLL_SETTINGS_CHANGE,
id: composeId,
expiresIn,
@ -726,30 +789,54 @@ const openComposeWithText = (composeId: string, text = '') =>
dispatch(changeCompose(composeId, text));
};
interface ComposeAddToMentionsAction {
type: typeof COMPOSE_ADD_TO_MENTIONS
id: string
account: string
}
const addToMentions = (composeId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const acct = state.accounts.get(accountId)!.acct;
return dispatch({
const action: ComposeAddToMentionsAction = {
type: COMPOSE_ADD_TO_MENTIONS,
id: composeId,
account: acct,
});
};
return dispatch(action);
};
interface ComposeRemoveFromMentionsAction {
type: typeof COMPOSE_REMOVE_FROM_MENTIONS
id: string
account: string
}
const removeFromMentions = (composeId: string, accountId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
const acct = state.accounts.get(accountId)!.acct;
return dispatch({
const action: ComposeRemoveFromMentionsAction = {
type: COMPOSE_REMOVE_FROM_MENTIONS,
id: composeId,
account: acct,
});
};
return dispatch(action);
};
interface ComposeEventReplyAction {
type: typeof COMPOSE_EVENT_REPLY
id: string
status: Status
account: Account
explicitAddressing: boolean
}
const eventDiscussionCompose = (composeId: string, status: Status) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const state = getState();
@ -765,6 +852,52 @@ const eventDiscussionCompose = (composeId: string, status: Status) =>
});
};
type ComposeAction =
ComposeSetStatusAction
| ReturnType<typeof changeCompose>
| ComposeReplyAction
| ReturnType<typeof cancelReplyCompose>
| ComposeQuoteAction
| ReturnType<typeof cancelQuoteCompose>
| ReturnType<typeof resetCompose>
| ComposeMentionAction
| ComposeDirectAction
| ReturnType<typeof submitComposeRequest>
| ReturnType<typeof submitComposeSuccess>
| ReturnType<typeof submitComposeFail>
| ReturnType<typeof changeUploadComposeRequest>
| ReturnType<typeof changeUploadComposeSuccess>
| ReturnType<typeof changeUploadComposeFail>
| ReturnType<typeof uploadComposeRequest>
| ReturnType<typeof uploadComposeProgress>
| ReturnType<typeof uploadComposeSuccess>
| ReturnType<typeof uploadComposeFail>
| ReturnType<typeof undoUploadCompose>
| ReturnType<typeof groupCompose>
| ReturnType<typeof setGroupTimelineVisible>
| ReturnType<typeof clearComposeSuggestions>
| ComposeSuggestionsReadyAction
| ComposeSuggestionSelectAction
| ReturnType<typeof updateSuggestionTags>
| ReturnType<typeof updateTagHistory>
| ReturnType<typeof changeComposeSpoilerness>
| ReturnType<typeof changeComposeContentType>
| ReturnType<typeof changeComposeSpoilerText>
| ReturnType<typeof changeComposeVisibility>
| ReturnType<typeof insertEmojiCompose>
| ReturnType<typeof addPoll>
| ReturnType<typeof removePoll>
| ReturnType<typeof addSchedule>
| ReturnType<typeof setSchedule>
| ReturnType<typeof removeSchedule>
| ReturnType<typeof addPollOption>
| ReturnType<typeof changePollOption>
| ReturnType<typeof removePollOption>
| ReturnType<typeof changePollSettings>
| ComposeAddToMentionsAction
| ComposeRemoveFromMentionsAction
| ComposeEventReplyAction
export {
COMPOSE_CHANGE,
COMPOSE_SUBMIT_REQUEST,
@ -794,7 +927,6 @@ export {
COMPOSE_SPOILER_TEXT_CHANGE,
COMPOSE_VISIBILITY_CHANGE,
COMPOSE_LISTABILITY_CHANGE,
COMPOSE_COMPOSING_CHANGE,
COMPOSE_EMOJI_INSERT,
COMPOSE_UPLOAD_CHANGE_REQUEST,
COMPOSE_UPLOAD_CHANGE_SUCCESS,
@ -865,4 +997,5 @@ export {
addToMentions,
removeFromMentions,
eventDiscussionCompose,
type ComposeAction,
};

@ -10,14 +10,14 @@ import type { AxiosError, RawAxiosRequestHeaders } from 'axios';
import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity } from 'soapbox/types/entities';
const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST';
const ME_FETCH_SUCCESS = 'ME_FETCH_SUCCESS';
const ME_FETCH_FAIL = 'ME_FETCH_FAIL';
const ME_FETCH_SKIP = 'ME_FETCH_SKIP';
const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST' as const;
const ME_FETCH_SUCCESS = 'ME_FETCH_SUCCESS' as const;
const ME_FETCH_FAIL = 'ME_FETCH_FAIL' as const;
const ME_FETCH_SKIP = 'ME_FETCH_SKIP' as const;
const ME_PATCH_REQUEST = 'ME_PATCH_REQUEST';
const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS';
const ME_PATCH_FAIL = 'ME_PATCH_FAIL';
const ME_PATCH_REQUEST = 'ME_PATCH_REQUEST' as const;
const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS' as const;
const ME_PATCH_FAIL = 'ME_PATCH_FAIL' as const;
const noOp = () => new Promise(f => f(undefined));
@ -85,13 +85,10 @@ const fetchMeRequest = () => ({
type: ME_FETCH_REQUEST,
});
const fetchMeSuccess = (me: APIEntity) =>
(dispatch: AppDispatch) => {
dispatch({
type: ME_FETCH_SUCCESS,
me,
});
};
const fetchMeSuccess = (me: APIEntity) => ({
type: ME_FETCH_SUCCESS,
me,
});
const fetchMeFail = (error: APIEntity) => ({
type: ME_FETCH_FAIL,
@ -103,13 +100,20 @@ const patchMeRequest = () => ({
type: ME_PATCH_REQUEST,
});
interface MePatchSuccessAction {
type: typeof ME_PATCH_SUCCESS
me: APIEntity
}
const patchMeSuccess = (me: APIEntity) =>
(dispatch: AppDispatch) => {
dispatch(importFetchedAccount(me));
dispatch({
const action: MePatchSuccessAction = {
type: ME_PATCH_SUCCESS,
me,
});
};
dispatch(importFetchedAccount(me));
dispatch(action);
};
const patchMeFail = (error: AxiosError) => ({
@ -118,6 +122,14 @@ const patchMeFail = (error: AxiosError) => ({
skipAlert: true,
});
type MeAction =
| ReturnType<typeof fetchMeRequest>
| ReturnType<typeof fetchMeSuccess>
| ReturnType<typeof fetchMeFail>
| ReturnType<typeof patchMeRequest>
| MePatchSuccessAction
| ReturnType<typeof patchMeFail>;
export {
ME_FETCH_REQUEST,
ME_FETCH_SUCCESS,
@ -134,4 +146,5 @@ export {
patchMeRequest,
patchMeSuccess,
patchMeFail,
type MeAction,
};

@ -10,9 +10,9 @@ import { isLoggedIn } from 'soapbox/utils/auth';
import type { AppDispatch, RootState } from 'soapbox/store';
const SETTING_CHANGE = 'SETTING_CHANGE';
const SETTING_SAVE = 'SETTING_SAVE';
const SETTINGS_UPDATE = 'SETTINGS_UPDATE';
const SETTING_CHANGE = 'SETTING_CHANGE' as const;
const SETTING_SAVE = 'SETTING_SAVE' as const;
const SETTINGS_UPDATE = 'SETTINGS_UPDATE' as const;
const FE_NAME = 'soapbox_fe';
@ -181,25 +181,33 @@ const getSettings = createSelector([
.mergeDeep(settings);
});
interface SettingChangeAction {
type: typeof SETTING_CHANGE
path: string[]
value: any
}
const changeSettingImmediate = (path: string[], value: any, opts?: SettingOpts) =>
(dispatch: AppDispatch) => {
dispatch({
const action: SettingChangeAction = {
type: SETTING_CHANGE,
path,
value,
});
};
dispatch(action);
dispatch(saveSettingsImmediate(opts));
};
const changeSetting = (path: string[], value: any, opts?: SettingOpts) =>
(dispatch: AppDispatch) => {
dispatch({
const action: SettingChangeAction = {
type: SETTING_CHANGE,
path,
value,
});
};
dispatch(action);
return dispatch(saveSettings(opts));
};
@ -236,6 +244,10 @@ const getLocale = (state: RootState, fallback = 'en') => {
return Object.keys(messages).includes(localeWithVariant) ? localeWithVariant : Object.keys(messages).includes(locale) ? locale : fallback;
};
type SettingsAction =
| SettingChangeAction
| { type: typeof SETTING_SAVE }
export {
SETTING_CHANGE,
SETTING_SAVE,
@ -248,4 +260,5 @@ export {
saveSettingsImmediate,
saveSettings,
getLocale,
type SettingsAction,
};

@ -13,23 +13,23 @@ import type { AxiosError } from 'axios';
import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity, Status } from 'soapbox/types/entities';
const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
const TIMELINE_DELETE = 'TIMELINE_DELETE';
const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
const TIMELINE_UPDATE = 'TIMELINE_UPDATE' as const;
const TIMELINE_DELETE = 'TIMELINE_DELETE' as const;
const TIMELINE_CLEAR = 'TIMELINE_CLEAR' as const;
const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE' as const;
const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE' as const;
const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP' as const;
const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST' as const;
const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS' as const;
const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL' as const;
const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
const TIMELINE_CONNECT = 'TIMELINE_CONNECT' as const;
const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT' as const;
const TIMELINE_REPLACE = 'TIMELINE_REPLACE';
const TIMELINE_INSERT = 'TIMELINE_INSERT';
const TIMELINE_CLEAR_FEED_ACCOUNT_ID = 'TIMELINE_CLEAR_FEED_ACCOUNT_ID';
const TIMELINE_REPLACE = 'TIMELINE_REPLACE' as const;
const TIMELINE_INSERT = 'TIMELINE_INSERT' as const;
const TIMELINE_CLEAR_FEED_ACCOUNT_ID = 'TIMELINE_CLEAR_FEED_ACCOUNT_ID' as const;
const MAX_QUEUED_ITEMS = 40;
@ -111,19 +111,29 @@ const dequeueTimeline = (timelineId: string, expandFunc?: (lastStatusId: string)
}
};
interface TimelineDeleteAction {
type: typeof TIMELINE_DELETE
id: string
accountId: string
references: ImmutableMap<string, readonly [statusId: string, accountId: string]>
reblogOf: unknown
}
const deleteFromTimelines = (id: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const accountId = getState().statuses.get(id)?.account;
const references = getState().statuses.filter(status => status.reblog === id).map(status => [status.id, status.account]);
const accountId = getState().statuses.get(id)?.account?.id!;
const references = getState().statuses.filter(status => status.reblog === id).map(status => [status.id, status.account.id] as const);
const reblogOf = getState().statuses.getIn([id, 'reblog'], null);
dispatch({
const action: TimelineDeleteAction = {
type: TIMELINE_DELETE,
id,
accountId,
references,
reblogOf,
});
};
dispatch(action);
};
const clearTimeline = (timeline: string) =>
@ -327,6 +337,9 @@ const clearFeedAccountId = () => (dispatch: AppDispatch, _getState: () => RootSt
dispatch({ type: TIMELINE_CLEAR_FEED_ACCOUNT_ID });
};
// TODO: other actions
type TimelineAction = TimelineDeleteAction;
export {
TIMELINE_UPDATE,
TIMELINE_DELETE,
@ -373,4 +386,5 @@ export {
scrollTopTimeline,
insertSuggestionsIntoTimeline,
clearFeedAccountId,
type TimelineAction,
};

@ -126,10 +126,10 @@ const PollForm: React.FC<IPollForm> = ({ composeId }) => {
const onRemoveOption = (index: number) => dispatch(removePollOption(composeId, index));
const onChangeOption = (index: number, title: string) => dispatch(changePollOption(composeId, index, title));
const handleAddOption = () => dispatch(addPollOption(composeId, ''));
const onChangeSettings = (expiresIn: string | number | undefined, isMultiple?: boolean) =>
const onChangeSettings = (expiresIn: number, isMultiple?: boolean) =>
dispatch(changePollSettings(composeId, expiresIn, isMultiple));
const handleSelectDuration = (value: number) => onChangeSettings(value, isMultiple);
const handleToggleMultiple = () => onChangeSettings(expiresIn, !isMultiple);
const handleToggleMultiple = () => onChangeSettings(Number(expiresIn), !isMultiple);
const onRemovePoll = () => dispatch(removePoll(composeId));
if (!options) {

@ -48,7 +48,7 @@ describe('compose reducer', () => {
withRedraft: true,
};
const result = reducer(undefined, action);
const result = reducer(undefined, action as any);
expect(result.get('compose-modal')!.media_attachments.isEmpty()).toBe(true);
});
@ -59,7 +59,7 @@ describe('compose reducer', () => {
status: normalizeStatus(fromJS(require('soapbox/__fixtures__/pleroma-status-deleted.json'))),
};
const result = reducer(undefined, action);
const result = reducer(undefined, action as any);
expect(result.get('compose-modal')!.media_attachments.getIn([0, 'id'])).toEqual('508107650');
});
@ -71,7 +71,7 @@ describe('compose reducer', () => {
status: normalizeStatus(fromJS(require('soapbox/__fixtures__/pleroma-status-deleted.json'))),
};
const result = reducer(undefined, action);
const result = reducer(undefined, action as any);
expect(result.get('compose-modal')!.id).toEqual('AHU2RrX0wdcwzCYjFQ');
});
@ -83,7 +83,7 @@ describe('compose reducer', () => {
status: normalizeStatus(fromJS(require('soapbox/__fixtures__/pleroma-status-deleted.json'))),
};
const result = reducer(undefined, action);
const result = reducer(undefined, action as any);
expect(result.get('compose-modal')!.id).toEqual(null);
});
});
@ -95,7 +95,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({})(),
account: ImmutableRecord({})(),
};
expect(reducer(undefined, action).toJS()['compose-modal']).toMatchObject({ privacy: 'public' });
expect(reducer(undefined, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'public' });
});
it('uses \'direct\' scope when replying to a DM', () => {
@ -106,7 +106,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({ visibility: 'direct' })(),
account: ImmutableRecord({})(),
};
expect(reducer(state as any, action).toJS()['compose-modal']).toMatchObject({ privacy: 'direct' });
expect(reducer(state as any, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'direct' });
});
it('uses \'private\' scope when replying to a private post', () => {
@ -117,7 +117,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({ visibility: 'private' })(),
account: ImmutableRecord({})(),
};
expect(reducer(state as any, action).toJS()['compose-modal']).toMatchObject({ privacy: 'private' });
expect(reducer(state as any, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'private' });
});
it('uses \'unlisted\' scope when replying to an unlisted post', () => {
@ -128,7 +128,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({ visibility: 'unlisted' })(),
account: ImmutableRecord({})(),
};
expect(reducer(state, action).toJS()['compose-modal']).toMatchObject({ privacy: 'unlisted' });
expect(reducer(state, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'unlisted' });
});
it('uses \'private\' scope when set as preference and replying to a public post', () => {
@ -139,7 +139,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({ visibility: 'public' })(),
account: ImmutableRecord({})(),
};
expect(reducer(state, action).toJS()['compose-modal']).toMatchObject({ privacy: 'private' });
expect(reducer(state, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'private' });
});
it('uses \'unlisted\' scope when set as preference and replying to a public post', () => {
@ -150,7 +150,7 @@ describe('compose reducer', () => {
status: ImmutableRecord({ visibility: 'public' })(),
account: ImmutableRecord({})(),
};
expect(reducer(state, action).toJS()['compose-modal']).toMatchObject({ privacy: 'unlisted' });
expect(reducer(state, action as any).toJS()['compose-modal']).toMatchObject({ privacy: 'unlisted' });
});
it('sets preferred scope on user login', () => {
@ -238,18 +238,6 @@ describe('compose reducer', () => {
});
});
it('should handle COMPOSE_COMPOSING_CHANGE', () => {
const state = initialState.set('home', ReducerCompose({ is_composing: true }));
const action = {
type: actions.COMPOSE_COMPOSING_CHANGE,
id: 'home',
value: false,
};
expect(reducer(state, action).toJS().home).toMatchObject({
is_composing: false,
});
});
it('should handle COMPOSE_SUBMIT_REQUEST', () => {
const state = initialState.set('home', ReducerCompose({ is_submitting: false }));
const action = {
@ -267,7 +255,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_UPLOAD_CHANGE_REQUEST,
id: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_changing_upload: true,
});
});
@ -278,7 +266,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_SUBMIT_SUCCESS,
id: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
privacy: 'public',
});
});
@ -289,7 +277,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_SUBMIT_FAIL,
id: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_submitting: false,
});
});
@ -300,7 +288,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_UPLOAD_CHANGE_FAIL,
composeId: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_changing_upload: false,
});
});
@ -311,7 +299,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_UPLOAD_REQUEST,
id: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_uploading: true,
});
});
@ -338,7 +326,7 @@ describe('compose reducer', () => {
media: media,
skipLoading: true,
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_uploading: false,
});
});
@ -349,7 +337,7 @@ describe('compose reducer', () => {
type: actions.COMPOSE_UPLOAD_FAIL,
id: 'home',
};
expect(reducer(state, action).toJS().home).toMatchObject({
expect(reducer(state, action as any).toJS().home).toMatchObject({
is_uploading: false,
});
});
@ -414,7 +402,7 @@ describe('compose reducer', () => {
type: TIMELINE_DELETE,
id: '9wk6pmImMrZjgrK7iC',
};
expect(reducer(state, action).toJS()['compose-modal']).toMatchObject({
expect(reducer(state, action as any).toJS()['compose-modal']).toMatchObject({
in_reply_to: null,
});
});

@ -2,6 +2,7 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde
import { v4 as uuid } from 'uuid';
import { isNativeEmoji } from 'soapbox/features/emoji';
import { Account } from 'soapbox/schemas';
import { tagHistory } from 'soapbox/settings';
import { PLEROMA } from 'soapbox/utils/features';
import { hasIntegerMediaIds } from 'soapbox/utils/status';
@ -32,7 +33,6 @@ import {
COMPOSE_TYPE_CHANGE,
COMPOSE_SPOILER_TEXT_CHANGE,
COMPOSE_VISIBILITY_CHANGE,
COMPOSE_COMPOSING_CHANGE,
COMPOSE_EMOJI_INSERT,
COMPOSE_UPLOAD_CHANGE_REQUEST,
COMPOSE_UPLOAD_CHANGE_SUCCESS,
@ -52,19 +52,19 @@ import {
COMPOSE_SET_STATUS,
COMPOSE_EVENT_REPLY,
COMPOSE_SET_GROUP_TIMELINE_VISIBLE,
ComposeAction,
} from '../actions/compose';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from '../actions/me';
import { SETTING_CHANGE, FE_NAME } from '../actions/settings';
import { TIMELINE_DELETE } from '../actions/timelines';
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me';
import { SETTING_CHANGE, FE_NAME, SettingsAction } from '../actions/settings';
import { TIMELINE_DELETE, TimelineAction } from '../actions/timelines';
import { normalizeAttachment } from '../normalizers/attachment';
import { unescapeHTML } from '../utils/html';
import type { AnyAction } from 'redux';
import type { Emoji } from 'soapbox/features/emoji';
import type {
Account as AccountEntity,
APIEntity,
Attachment as AttachmentEntity,
Status,
Status as StatusEntity,
Tag,
} from 'soapbox/types/entities';
@ -111,9 +111,9 @@ type State = ImmutableMap<string, Compose>;
type Compose = ReturnType<typeof ReducerCompose>;
type Poll = ReturnType<typeof PollRecord>;
const statusToTextMentions = (status: ImmutableMap<string, any>, account: AccountEntity) => {
const statusToTextMentions = (status: Status, account: Account) => {
const author = status.getIn(['account', 'acct']);
const mentions = status.get('mentions')?.map((m: ImmutableMap<string, any>) => m.get('acct')) || [];
const mentions = status.get('mentions')?.map((m) => m.acct) || [];
return ImmutableOrderedSet([author])
.concat(mentions)
@ -122,22 +122,21 @@ const statusToTextMentions = (status: ImmutableMap<string, any>, account: Accoun
.join('');
};
export const statusToMentionsArray = (status: ImmutableMap<string, any>, account: AccountEntity) => {
export const statusToMentionsArray = (status: Status, account: Account) => {
const author = status.getIn(['account', 'acct']) as string;
const mentions = status.get('mentions')?.map((m: ImmutableMap<string, any>) => m.get('acct')) || [];
const mentions = status.get('mentions')?.map((m) => m.acct) || [];
return ImmutableOrderedSet<string>([author])
.concat(mentions)
.delete(account.acct) as ImmutableOrderedSet<string>;
};
export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: AccountEntity) => {
const author = (status.account as AccountEntity).id;
export const statusToMentionsAccountIdsArray = (status: StatusEntity, account: Account) => {
const mentions = status.mentions.map((m) => m.id);
return ImmutableOrderedSet([author])
return ImmutableOrderedSet<string>([account.id])
.concat(mentions)
.delete(account.id) as ImmutableOrderedSet<string>;
.delete(account.id);
};
const appendMedia = (compose: Compose, media: APIEntity, defaultSensitive?: boolean) => {
@ -168,9 +167,9 @@ const removeMedia = (compose: Compose, mediaId: string) => {
});
};
const insertSuggestion = (compose: Compose, position: number, token: string, completion: string, path: Array<string | number>) => {
const insertSuggestion = (compose: Compose, position: number, token: string | null, completion: string, path: Array<string | number>) => {
return compose.withMutations(map => {
map.updateIn(path, oldText => `${(oldText as string).slice(0, position)}${completion} ${(oldText as string).slice(position + token.length)}`);
map.updateIn(path, oldText => `${(oldText as string).slice(0, position)}${completion} ${(oldText as string).slice(position + (token?.length ?? 0))}`);
map.set('suggestion_token', null);
map.set('suggestions', ImmutableList());
if (path.length === 1 && path[0] === 'text') {
@ -216,10 +215,10 @@ const privacyPreference = (a: string, b: string) => {
const domParser = new DOMParser();
const expandMentions = (status: ImmutableMap<string, any>) => {
const expandMentions = (status: Status) => {
const fragment = domParser.parseFromString(status.get('content'), 'text/html').documentElement;
status.get('mentions').forEach((mention: ImmutableMap<string, any>) => {
status.get('mentions').forEach((mention) => {
const node = fragment.querySelector(`a[href="${mention.get('url')}"]`);
if (node) node.textContent = `@${mention.get('acct')}`;
});
@ -227,13 +226,13 @@ const expandMentions = (status: ImmutableMap<string, any>) => {
return fragment.innerHTML;
};
const getExplicitMentions = (me: string, status: ImmutableMap<string, any>) => {
const fragment = domParser.parseFromString(status.get('content'), 'text/html').documentElement;
const getExplicitMentions = (me: string, status: Status) => {
const fragment = domParser.parseFromString(status.content, 'text/html').documentElement;
const mentions = status
.get('mentions')
.filter((mention: ImmutableMap<string, any>) => !(fragment.querySelector(`a[href="${mention.get('url')}"]`) || mention.get('id') === me))
.map((m: ImmutableMap<string, any>) => m.get('acct'));
.filter((mention) => !(fragment.querySelector(`a[href="${mention.url}"]`) || mention.id === me))
.map((m) => m.acct);
return ImmutableOrderedSet<string>(mentions);
};
@ -274,7 +273,7 @@ export const initialState: State = ImmutableMap({
default: ReducerCompose({ idempotencyKey: uuid(), resetFileKey: getResetFileKey() }),
});
export default function compose(state = initialState, action: AnyAction) {
export default function compose(state = initialState, action: ComposeAction | MeAction | SettingsAction | TimelineAction) {
switch (action.type) {
case COMPOSE_TYPE_CHANGE:
return updateCompose(state, action.id, compose => compose.withMutations(map => {
@ -300,13 +299,11 @@ export default function compose(state = initialState, action: AnyAction) {
return updateCompose(state, action.id, compose => compose
.set('text', action.text)
.set('idempotencyKey', uuid()));
case COMPOSE_COMPOSING_CHANGE:
return updateCompose(state, action.id, compose => compose.set('is_composing', action.value));
case COMPOSE_REPLY:
return updateCompose(state, action.id, compose => compose.withMutations(map => {
const defaultCompose = state.get('default')!;
map.set('group_id', action.status.getIn(['group', 'id']) || action.status.get('group'));
map.set('group_id', action.status.getIn(['group', 'id']) as string);
map.set('in_reply_to', action.status.get('id'));
map.set('to', action.explicitAddressing ? statusToMentionsArray(action.status, action.account) : ImmutableOrderedSet<string>());
map.set('text', !action.explicitAddressing ? statusToTextMentions(action.status, action.account) : '');
@ -324,11 +321,11 @@ export default function compose(state = initialState, action: AnyAction) {
}));
case COMPOSE_QUOTE:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
const author = action.status.getIn(['account', 'acct']);
const author = action.status.getIn(['account', 'acct']) as string;
const defaultCompose = state.get('default')!;
map.set('quote', action.status.get('id'));
map.set('to', ImmutableOrderedSet([author]));
map.set('to', ImmutableOrderedSet<string>([author]));
map.set('text', '');
map.set('privacy', privacyPreference(action.status.visibility, defaultCompose.privacy));
map.set('focusDate', new Date());
@ -342,7 +339,7 @@ export default function compose(state = initialState, action: AnyAction) {
if (action.status.group?.group_visibility === 'everyone') {
map.set('privacy', privacyPreference('public', defaultCompose.privacy));
} else if (action.status.group?.group_visibility === 'members_only') {
map.set('group_id', action.status.getIn(['group', 'id']) || action.status.get('group'));
map.set('group_id', action.status.getIn(['group', 'id']) as string);
map.set('privacy', 'group');
}
}
@ -379,14 +376,14 @@ export default function compose(state = initialState, action: AnyAction) {
return updateCompose(state, action.id, compose => compose.set('progress', Math.round((action.loaded / action.total) * 100)));
case COMPOSE_MENTION:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
map.update('text', text => [text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' '));
map.set('focusDate', new Date());
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
}));
case COMPOSE_DIRECT:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' '));
map.update('text', text => [text.trim(), `@${action.account.acct} `].filter((str) => str.length !== 0).join(' '));
map.set('privacy', 'direct');
map.set('focusDate', new Date());
map.set('caretPosition', null);
@ -435,7 +432,7 @@ export default function compose(state = initialState, action: AnyAction) {
case COMPOSE_SET_STATUS:
return updateCompose(state, 'compose-modal', compose => compose.withMutations(map => {
if (!action.withRedraft) {
map.set('id', action.status.get('id'));
map.set('id', action.status.id);
}
map.set('text', action.rawText || unescapeHTML(expandMentions(action.status)));
map.set('to', action.explicitAddressing ? getExplicitMentions(action.status.account.id, action.status) : ImmutableOrderedSet<string>());
@ -445,10 +442,10 @@ export default function compose(state = initialState, action: AnyAction) {
map.set('caretPosition', null);
map.set('idempotencyKey', uuid());
map.set('content_type', action.contentType || 'text/plain');
map.set('quote', action.status.get('quote'));
map.set('group_id', action.status.get('group'));
map.set('quote', action.status.getIn(['quote', 'id']) as string);
map.set('group_id', action.status.getIn(['group', 'id']) as string);
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) {
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status.toJS() as any)) {
map.set('media_attachments', ImmutableList());
} else {
map.set('media_attachments', action.status.media_attachments);
@ -462,9 +459,9 @@ export default function compose(state = initialState, action: AnyAction) {
map.set('spoiler_text', '');
}
if (action.status.get('poll')) {
if (action.status.poll && typeof action.status.poll === 'object') {
map.set('poll', PollRecord({
options: action.status.poll.options.map((x: APIEntity) => x.get('title')),
options: ImmutableList(action.status.poll.options.map(({ title }) => title)),
multiple: action.status.poll.multiple,
expires_in: 24 * 3600,
}));
@ -487,7 +484,17 @@ export default function compose(state = initialState, action: AnyAction) {
case COMPOSE_POLL_OPTION_REMOVE:
return updateCompose(state, action.id, compose => compose.updateIn(['poll', 'options'], options => (options as ImmutableList<string>).delete(action.index)));
case COMPOSE_POLL_SETTINGS_CHANGE:
return updateCompose(state, action.id, compose => compose.update('poll', poll => poll!.set('expires_in', action.expiresIn).set('multiple', action.isMultiple)));
return updateCompose(state, action.id, compose => compose.update('poll', poll => {
if (!poll) return null;
return poll.withMutations((poll) => {
if (action.expiresIn) {
poll.set('expires_in', action.expiresIn);
}
if (typeof action.isMultiple === 'boolean') {
poll.set('multiple', action.isMultiple);
}
});
}));
case COMPOSE_ADD_TO_MENTIONS:
return updateCompose(state, action.id, compose => compose.update('to', mentions => mentions!.add(action.account)));
case COMPOSE_REMOVE_FROM_MENTIONS:

Loading…
Cancel
Save