Merge branch 'compose-safe' into 'develop'
Make Compose reducer type-safe See merge request soapbox-pub/soapbox!2581
This commit is contained in:
commit
5ffaaa4c3a
7 changed files with 357 additions and 189 deletions
|
@ -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_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';
|
||||
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_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';
|
||||
const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE' as const;
|
||||
|
||||
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_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';
|
||||
const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT' as const;
|
||||
|
||||
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_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';
|
||||
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_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';
|
||||
const COMPOSE_SCHEDULE_SET = 'COMPOSE_SCHEDULE_SET';
|
||||
const COMPOSE_SCHEDULE_REMOVE = 'COMPOSE_SCHEDULE_REMOVE';
|
||||
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';
|
||||
const COMPOSE_REMOVE_FROM_MENTIONS = 'COMPOSE_REMOVE_FROM_MENTIONS';
|
||||
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';
|
||||
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…
Reference in a new issue