Reducers: TypeScript
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
af695e3812
commit
419ab93077
24 changed files with 219 additions and 61 deletions
|
@ -1,5 +1,4 @@
|
|||
import axios, { AxiosError, Canceler } from 'axios';
|
||||
import { List as ImmutableList, Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { defineMessages, IntlShape } from 'react-intl';
|
||||
|
||||
|
@ -100,7 +99,7 @@ const messages = defineMessages({
|
|||
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
||||
|
||||
const ensureComposeIsVisible = (getState: () => RootState, routerHistory: History) => {
|
||||
if (!getState().compose.get('mounted') && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
|
||||
if (!getState().compose.mounted && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
|
||||
routerHistory.push('/posts/new');
|
||||
}
|
||||
};
|
||||
|
@ -212,16 +211,16 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, d
|
|||
};
|
||||
|
||||
const needsDescriptions = (state: RootState) => {
|
||||
const media = state.compose.get('media_attachments') as ImmutableList<ImmutableMap<string, any>>;
|
||||
const media = state.compose.media_attachments;
|
||||
const missingDescriptionModal = getSettings(state).get('missingDescriptionModal');
|
||||
|
||||
const hasMissing = media.filter(item => !item.get('description')).size > 0;
|
||||
const hasMissing = media.filter(item => !item.description).size > 0;
|
||||
|
||||
return missingDescriptionModal && hasMissing;
|
||||
};
|
||||
|
||||
const validateSchedule = (state: RootState) => {
|
||||
const schedule = state.compose.get('schedule');
|
||||
const schedule = state.compose.schedule;
|
||||
if (!schedule) return true;
|
||||
|
||||
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000);
|
||||
|
@ -234,10 +233,10 @@ const submitCompose = (routerHistory: History, force = false) =>
|
|||
if (!isLoggedIn(getState)) return;
|
||||
const state = getState();
|
||||
|
||||
const status = state.compose.get('text') || '';
|
||||
const media = state.compose.get('media_attachments') as ImmutableList<ImmutableMap<string, any>>;
|
||||
const statusId = state.compose.get('id') || null;
|
||||
let to = state.compose.get('to') || ImmutableOrderedSet();
|
||||
const status = state.compose.text;
|
||||
const media = state.compose.media_attachments;
|
||||
const statusId = state.compose.id;
|
||||
let to = state.compose.to;
|
||||
|
||||
if (!validateSchedule(state)) {
|
||||
dispatch(snackbar.error(messages.scheduleError));
|
||||
|
@ -259,7 +258,7 @@ const submitCompose = (routerHistory: History, force = false) =>
|
|||
}
|
||||
|
||||
if (to && status) {
|
||||
const mentions: string[] = status.match(/(?:^|\s|\.)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/gi); // not a perfect regex
|
||||
const mentions: string[] | null = status.match(/(?:^|\s|\.)@([a-z0-9_]+(?:@[a-z0-9\.\-]+)?)/gi); // not a perfect regex
|
||||
|
||||
if (mentions)
|
||||
to = to.union(mentions.map(mention => mention.trim().slice(1)));
|
||||
|
@ -268,19 +267,19 @@ const submitCompose = (routerHistory: History, force = false) =>
|
|||
dispatch(submitComposeRequest());
|
||||
dispatch(closeModal());
|
||||
|
||||
const idempotencyKey = state.compose.get('idempotencyKey');
|
||||
const idempotencyKey = state.compose.idempotencyKey;
|
||||
|
||||
const params = {
|
||||
status,
|
||||
in_reply_to_id: state.compose.get('in_reply_to') || null,
|
||||
quote_id: state.compose.get('quote') || null,
|
||||
media_ids: media.map(item => item.get('id')),
|
||||
sensitive: state.compose.get('sensitive'),
|
||||
spoiler_text: state.compose.get('spoiler_text') || '',
|
||||
visibility: state.compose.get('privacy'),
|
||||
content_type: state.compose.get('content_type'),
|
||||
poll: state.compose.get('poll') || null,
|
||||
scheduled_at: state.compose.get('schedule') || null,
|
||||
in_reply_to_id: state.compose.in_reply_to,
|
||||
quote_id: state.compose.quote,
|
||||
media_ids: media.map(item => item.id),
|
||||
sensitive: state.compose.sensitive,
|
||||
spoiler_text: state.compose.spoiler_text,
|
||||
visibility: state.compose.privacy,
|
||||
content_type: state.compose.content_type,
|
||||
poll: state.compose.poll,
|
||||
scheduled_at: state.compose.schedule,
|
||||
to,
|
||||
};
|
||||
|
||||
|
@ -315,7 +314,7 @@ const uploadCompose = (files: FileList, intl: IntlShape) =>
|
|||
const maxImageSize = getState().instance.configuration.getIn(['media_attachments', 'image_size_limit']) as number | undefined;
|
||||
const maxVideoSize = getState().instance.configuration.getIn(['media_attachments', 'video_size_limit']) as number | undefined;
|
||||
|
||||
const media = getState().compose.get('media_attachments');
|
||||
const media = getState().compose.media_attachments;
|
||||
const progress = new Array(files.length).fill(0);
|
||||
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
||||
|
||||
|
@ -550,7 +549,7 @@ const updateTagHistory = (tags: string[]) => ({
|
|||
const insertIntoTagHistory = (recognizedTags: APIEntity[], text: string) =>
|
||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const oldHistory = state.compose.get('tagHistory') as ImmutableList<string>;
|
||||
const oldHistory = state.compose.tagHistory;
|
||||
const me = state.me;
|
||||
const names = recognizedTags
|
||||
.filter(tag => text.match(new RegExp(`#${tag.name}`, 'i')))
|
||||
|
|
|
@ -25,7 +25,7 @@ const getMeId = (state: RootState) => state.me || getAuthUserId(state);
|
|||
|
||||
const getMeUrl = (state: RootState) => {
|
||||
const accountId = getMeId(state);
|
||||
return state.accounts.get(accountId)!.url || getAuthUserUrl(state);
|
||||
return state.accounts.get(accountId)?.url || getAuthUserUrl(state);
|
||||
};
|
||||
|
||||
const getMeToken = (state: RootState) => {
|
||||
|
|
|
@ -47,7 +47,7 @@ const statusExists = (getState: () => RootState, statusId: string) => {
|
|||
return (getState().statuses.get(statusId) || null) !== null;
|
||||
};
|
||||
|
||||
const createStatus = (params: Record<string, any>, idempotencyKey: string, statusId: string) => {
|
||||
const createStatus = (params: Record<string, any>, idempotencyKey: string, statusId: string | null) => {
|
||||
return (dispatch: AppDispatch, getState: () => RootState) => {
|
||||
dispatch({ type: STATUS_CREATE_REQUEST, params, idempotencyKey });
|
||||
|
||||
|
|
Binary file not shown.
|
@ -22,7 +22,7 @@ const Backups = () => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const backups = useAppSelector<ImmutableList<ImmutableMap<string, any>>>((state) => state.backups.toList().sortBy((backup: ImmutableMap<string, any>) => backup.get('inserted_at')));
|
||||
const backups = useAppSelector((state) => state.backups.toList().sortBy((backup) => backup.inserted_at));
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
|
@ -63,12 +63,12 @@ const Backups = () => {
|
|||
>
|
||||
{backups.map((backup) => (
|
||||
<div
|
||||
className={classNames('backup', { 'backup--pending': !backup.get('processed') })}
|
||||
key={backup.get('id')}
|
||||
className={classNames('backup', { 'backup--pending': !backup.processed })}
|
||||
key={backup.id}
|
||||
>
|
||||
{backup.get('processed')
|
||||
? <a href={backup.get('url')} target='_blank'>{backup.get('inserted_at')}</a>
|
||||
: <div>{intl.formatMessage(messages.pending)}: {backup.get('inserted_at')}</div>
|
||||
{backup.processed
|
||||
? <a href={backup.url} target='_blank'>{backup.inserted_at}</a>
|
||||
: <div>{intl.formatMessage(messages.pending)}: {backup.inserted_at}</div>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -49,7 +49,7 @@ const Option = (props: IOption) => {
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const suggestions = useAppSelector((state) => state.compose.get('suggestions'));
|
||||
const suggestions = useAppSelector((state) => state.compose.suggestions);
|
||||
|
||||
const handleOptionTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => onChange(index, event.target.value);
|
||||
|
||||
|
@ -107,9 +107,9 @@ const PollForm = () => {
|
|||
const intl = useIntl();
|
||||
|
||||
const pollLimits = useAppSelector((state) => state.instance.getIn(['configuration', 'polls']) as any);
|
||||
const options = useAppSelector((state) => state.compose.getIn(['poll', 'options']));
|
||||
const expiresIn = useAppSelector((state) => state.compose.getIn(['poll', 'expires_in']));
|
||||
const isMultiple = useAppSelector((state) => state.compose.getIn(['poll', 'multiple']));
|
||||
const options = useAppSelector((state) => state.compose.poll?.options);
|
||||
const expiresIn = useAppSelector((state) => state.compose.poll?.expires_in);
|
||||
const isMultiple = useAppSelector((state) => state.compose.poll?.multiple);
|
||||
|
||||
const maxOptions = pollLimits.get('max_options');
|
||||
const maxOptionChars = pollLimits.get('max_characters_per_option');
|
||||
|
|
|
@ -13,9 +13,9 @@ import type { Status as StatusEntity } from 'soapbox/types/entities';
|
|||
const ReplyMentions: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const instance = useAppSelector((state) => state.instance);
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.get('in_reply_to') }));
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.in_reply_to! }));
|
||||
|
||||
const to = useAppSelector((state) => state.compose.get('to'));
|
||||
const to = useAppSelector((state) => state.compose.to);
|
||||
const account = useAppSelector((state) => state.accounts.get(state.me));
|
||||
|
||||
const { explicitAddressing } = getFeatures(instance);
|
||||
|
@ -24,7 +24,7 @@ const ReplyMentions: React.FC = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const parentTo = status && statusToMentionsAccountIdsArray(status, account);
|
||||
const parentTo = status && statusToMentionsAccountIdsArray(status, account!);
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -31,7 +31,7 @@ const ScheduleForm: React.FC = () => {
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const scheduledAt = useAppSelector((state) => state.compose.get('schedule'));
|
||||
const scheduledAt = useAppSelector((state) => state.compose.schedule);
|
||||
const active = !!scheduledAt;
|
||||
|
||||
const onSchedule = (date: Date) => {
|
||||
|
|
|
@ -15,8 +15,8 @@ const SensitiveButton: React.FC = () => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const active = useAppSelector(state => state.compose.get('sensitive') === true);
|
||||
const disabled = useAppSelector(state => state.compose.get('spoiler') === true);
|
||||
const active = useAppSelector(state => state.compose.sensitive === true);
|
||||
const disabled = useAppSelector(state => state.compose.spoiler === true);
|
||||
|
||||
const onClick = () => {
|
||||
dispatch(changeComposeSensitivity());
|
||||
|
|
|
@ -5,8 +5,8 @@ import { useAppSelector } from 'soapbox/hooks';
|
|||
|
||||
/** File upload progress bar for post composer. */
|
||||
const ComposeUploadProgress = () => {
|
||||
const active = useAppSelector((state) => state.compose.get('is_uploading'));
|
||||
const progress = useAppSelector((state) => state.compose.get('progress'));
|
||||
const active = useAppSelector((state) => state.compose.is_uploading);
|
||||
const progress = useAppSelector((state) => state.compose.progress);
|
||||
|
||||
if (!active) {
|
||||
return null;
|
||||
|
|
|
@ -10,7 +10,7 @@ import UploadContainer from '../containers/upload_container';
|
|||
import type { Attachment as AttachmentEntity } from 'soapbox/types/entities';
|
||||
|
||||
const UploadForm = () => {
|
||||
const mediaIds = useAppSelector((state) => state.compose.get('media_attachments').map((item: AttachmentEntity) => item.get('id')));
|
||||
const mediaIds = useAppSelector((state) => state.compose.media_attachments.map((item: AttachmentEntity) => item.id));
|
||||
const classes = classNames('compose-form__uploads-wrapper', {
|
||||
'contains-media': mediaIds.size !== 0,
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ const getStatus = makeGetStatus();
|
|||
/** QuotedStatus shown in post composer. */
|
||||
const QuotedStatusContainer: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const status = useAppSelector(state => getStatus(state, { id: state.compose.get('quote') }));
|
||||
const status = useAppSelector(state => getStatus(state, { id: state.compose.quote! }));
|
||||
|
||||
const onCancel = () => {
|
||||
dispatch(cancelQuoteCompose());
|
||||
|
|
|
@ -26,7 +26,7 @@ const Account: React.FC<IAccount> = ({ accountId, author }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
|
||||
const account = useAppSelector((state) => getAccount(state, accountId));
|
||||
const added = useAppSelector((state) => !!account && state.compose.get('to').includes(account.acct));
|
||||
const added = useAppSelector((state) => !!account && state.compose.to?.includes(account.acct));
|
||||
|
||||
const onRemove = () => dispatch(removeFromMentions(accountId));
|
||||
const onAdd = () => dispatch(addToMentions(accountId));
|
||||
|
|
|
@ -165,7 +165,7 @@ const makeMapStateToProps = () => {
|
|||
status,
|
||||
ancestorsIds,
|
||||
descendantsIds,
|
||||
askReplyConfirmation: state.compose.get('text', '').trim().length !== 0,
|
||||
askReplyConfirmation: state.compose.text.trim().length !== 0,
|
||||
me: state.me,
|
||||
displayMedia: getSettings(state).get('displayMedia'),
|
||||
allowedEmoji: soapbox.allowedEmoji,
|
||||
|
|
Binary file not shown.
71
app/soapbox/features/ui/components/compose_modal.tsx
Normal file
71
app/soapbox/features/ui/components/compose_modal.tsx
Normal file
|
@ -0,0 +1,71 @@
|
|||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { cancelReplyCompose } from 'soapbox/actions/compose';
|
||||
import { openModal, closeModal } from 'soapbox/actions/modals';
|
||||
import { Modal } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import ComposeFormContainer from '../../compose/containers/compose_form_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
});
|
||||
|
||||
interface IComposeModal {
|
||||
onClose: (type?: string) => void,
|
||||
}
|
||||
|
||||
const ComposeModal: React.FC<IComposeModal> = ({ onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const statusId = useAppSelector((state) => state.compose.id);
|
||||
const composeText = useAppSelector((state) => state.compose.text);
|
||||
const privacy = useAppSelector((state) => state.compose.privacy);
|
||||
const inReplyTo = useAppSelector((state) => state.compose.in_reply_to);
|
||||
const quote = useAppSelector((state) => state.compose.quote);
|
||||
|
||||
const onClickClose = () => {
|
||||
if (composeText) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
icon: require('@tabler/icons/icons/trash.svg'),
|
||||
heading: <FormattedMessage id='confirmations.delete.heading' defaultMessage='Delete post' />,
|
||||
message: <FormattedMessage id='confirmations.delete.message' defaultMessage='Are you sure you want to delete this post?' />,
|
||||
confirm: intl.formatMessage(messages.confirm),
|
||||
onConfirm: () => {
|
||||
dispatch(closeModal('COMPOSE'));
|
||||
dispatch(cancelReplyCompose());
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
onClose('COMPOSE');
|
||||
}
|
||||
};
|
||||
|
||||
const renderTitle = () => {
|
||||
if (statusId) {
|
||||
return <FormattedMessage id='navigation_bar.compose_edit' defaultMessage='Edit post' />;
|
||||
} else if (privacy === 'direct') {
|
||||
return <FormattedMessage id='navigation_bar.compose_direct' defaultMessage='Direct message' />;
|
||||
} else if (inReplyTo) {
|
||||
return <FormattedMessage id='navigation_bar.compose_reply' defaultMessage='Reply to post' />;
|
||||
} else if (quote) {
|
||||
return <FormattedMessage id='navigation_bar.compose_quote' defaultMessage='Quote post' />;
|
||||
} else {
|
||||
return <FormattedMessage id='navigation_bar.compose' defaultMessage='Compose new post' />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={renderTitle()}
|
||||
onClose={onClickClose}
|
||||
>
|
||||
<ComposeFormContainer />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComposeModal;
|
|
@ -15,10 +15,10 @@ interface IReplyMentionsModal {
|
|||
}
|
||||
|
||||
const ReplyMentionsModal: React.FC<IReplyMentionsModal> = ({ onClose }) => {
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.get('in_reply_to') }));
|
||||
const status = useAppSelector<StatusEntity | null>(state => makeGetStatus()(state, { id: state.compose.in_reply_to! }));
|
||||
const account = useAppSelector((state) => state.accounts.get(state.me));
|
||||
|
||||
const mentions = statusToMentionsAccountIdsArray(status, account);
|
||||
const mentions = statusToMentionsAccountIdsArray(status!, account!);
|
||||
const author = (status?.account as AccountEntity).id;
|
||||
|
||||
const onClickClose = () => {
|
||||
|
|
|
@ -4,21 +4,22 @@ import { normalizeStatus } from 'soapbox/normalizers/status';
|
|||
import { calculateStatus } from 'soapbox/reducers/statuses';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
import type { PendingStatus } from 'soapbox/reducers/pending_statuses';
|
||||
import type { RootState } from 'soapbox/store';
|
||||
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const buildMentions = (pendingStatus: ImmutableMap<string, any>) => {
|
||||
if (pendingStatus.get('in_reply_to_id')) {
|
||||
return ImmutableList(pendingStatus.get('to') || []).map(acct => ImmutableMap({ acct }));
|
||||
const buildMentions = (pendingStatus: PendingStatus) => {
|
||||
if (pendingStatus.in_reply_to_id) {
|
||||
return ImmutableList(pendingStatus.to || []).map(acct => ImmutableMap({ acct }));
|
||||
} else {
|
||||
return ImmutableList();
|
||||
}
|
||||
};
|
||||
|
||||
const buildPoll = (pendingStatus: ImmutableMap<string, any>) => {
|
||||
const buildPoll = (pendingStatus: PendingStatus) => {
|
||||
if (pendingStatus.hasIn(['poll', 'options'])) {
|
||||
return pendingStatus.get('poll').update('options', (options: ImmutableMap<string, any>) => {
|
||||
return pendingStatus.poll!.update('options', (options: ImmutableMap<string, any>) => {
|
||||
return options.map((title: string) => ImmutableMap({ title }));
|
||||
});
|
||||
} else {
|
||||
|
@ -26,23 +27,23 @@ const buildPoll = (pendingStatus: ImmutableMap<string, any>) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const buildStatus = (state: RootState, pendingStatus: ImmutableMap<string, any>, idempotencyKey: string) => {
|
||||
export const buildStatus = (state: RootState, pendingStatus: PendingStatus, idempotencyKey: string) => {
|
||||
const me = state.me as string;
|
||||
const account = getAccount(state, me);
|
||||
const inReplyToId = pendingStatus.get('in_reply_to_id');
|
||||
const inReplyToId = pendingStatus.in_reply_to_id;
|
||||
|
||||
const status = ImmutableMap({
|
||||
account,
|
||||
content: pendingStatus.get('status', '').replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||
content: pendingStatus.status.replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||
id: `末pending-${idempotencyKey}`,
|
||||
in_reply_to_account_id: state.statuses.getIn([inReplyToId, 'account'], null),
|
||||
in_reply_to_id: inReplyToId,
|
||||
media_attachments: pendingStatus.get('media_ids', ImmutableList()).map((id: string) => ImmutableMap({ id })),
|
||||
media_attachments: (pendingStatus.media_ids || ImmutableList()).map((id: string) => ImmutableMap({ id })),
|
||||
mentions: buildMentions(pendingStatus),
|
||||
poll: buildPoll(pendingStatus),
|
||||
quote: pendingStatus.get('quote_id', null),
|
||||
sensitive: pendingStatus.get('sensitive', false),
|
||||
visibility: pendingStatus.get('visibility', 'public'),
|
||||
quote: pendingStatus.quote_id,
|
||||
sensitive: pendingStatus.sensitive,
|
||||
visibility: pendingStatus.visibility,
|
||||
});
|
||||
|
||||
return calculateStatus(normalizeStatus(status));
|
||||
|
|
|
@ -19,7 +19,7 @@ import { normalizePoll } from 'soapbox/normalizers/poll';
|
|||
import type { ReducerAccount } from 'soapbox/reducers/accounts';
|
||||
import type { Account, Attachment, Card, Emoji, Mention, Poll, EmbeddedEntity } from 'soapbox/types/entities';
|
||||
|
||||
type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct';
|
||||
export type StatusVisibility = 'public' | 'unlisted' | 'private' | 'direct';
|
||||
|
||||
// https://docs.joinmastodon.org/entities/status/
|
||||
export const StatusRecord = ImmutableRecord({
|
||||
|
|
Binary file not shown.
43
app/soapbox/reducers/backups.tsx
Normal file
43
app/soapbox/reducers/backups.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||
|
||||
import {
|
||||
BACKUPS_FETCH_SUCCESS,
|
||||
BACKUPS_CREATE_SUCCESS,
|
||||
} from '../actions/backups';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
const BackupRecord = ImmutableRecord({
|
||||
id: null as number | null,
|
||||
content_type: '',
|
||||
url: '',
|
||||
file_size: null as number | null,
|
||||
processed: false,
|
||||
inserted_at: '',
|
||||
});
|
||||
|
||||
type Backup = ReturnType<typeof BackupRecord>;
|
||||
type State = ImmutableMap<string, Backup>;
|
||||
|
||||
const initialState: State = ImmutableMap();
|
||||
|
||||
const importBackup = (state: State, backup: APIEntity) => {
|
||||
return state.set(backup.inserted_at, BackupRecord(backup));
|
||||
};
|
||||
|
||||
const importBackups = (state: State, backups: APIEntity[]) => {
|
||||
return state.withMutations(mutable => {
|
||||
backups.forEach(backup => importBackup(mutable, backup));
|
||||
});
|
||||
};
|
||||
|
||||
export default function backups(state = initialState, action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case BACKUPS_FETCH_SUCCESS:
|
||||
case BACKUPS_CREATE_SUCCESS:
|
||||
return importBackups(state, action.backups);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
44
app/soapbox/reducers/pending_statuses.ts
Normal file
44
app/soapbox/reducers/pending_statuses.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import {
|
||||
STATUS_CREATE_REQUEST,
|
||||
STATUS_CREATE_SUCCESS,
|
||||
} from 'soapbox/actions/statuses';
|
||||
|
||||
import type { StatusVisibility } from 'soapbox/normalizers/status';
|
||||
|
||||
const PendingStatusRecord = ImmutableRecord({
|
||||
content_type: '',
|
||||
in_reply_to_id: null as string | null,
|
||||
media_ids: null as ImmutableList<string> | null,
|
||||
quote_id: null as string | null,
|
||||
poll: null as ImmutableMap<string, any> | null,
|
||||
sensitive: false,
|
||||
spoiler_text: '',
|
||||
status: '',
|
||||
to: null as ImmutableList<string> | null,
|
||||
visibility: 'public' as StatusVisibility,
|
||||
});
|
||||
|
||||
export type PendingStatus = ReturnType<typeof PendingStatusRecord>;
|
||||
type State = ImmutableMap<string, PendingStatus>;
|
||||
|
||||
const initialState: State = ImmutableMap();
|
||||
|
||||
const importStatus = (state: State, params: ImmutableMap<string, any>, idempotencyKey: string) => {
|
||||
return state.set(idempotencyKey, PendingStatusRecord(params));
|
||||
};
|
||||
|
||||
const deleteStatus = (state: State, idempotencyKey: string) => state.delete(idempotencyKey);
|
||||
|
||||
export default function pending_statuses(state = initialState, action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case STATUS_CREATE_REQUEST:
|
||||
return importStatus(state, ImmutableMap(fromJS(action.params)), action.idempotencyKey);
|
||||
case STATUS_CREATE_SUCCESS:
|
||||
return deleteStatus(state, action.idempotencyKey);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue