WIP pl-api migration
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
6f2b12c7ca
commit
178faef829
13 changed files with 78 additions and 94 deletions
|
@ -19,7 +19,7 @@ import { getSettings } from './settings';
|
||||||
import { createStatus } from './statuses';
|
import { createStatus } from './statuses';
|
||||||
|
|
||||||
import type { EditorState } from 'lexical';
|
import type { EditorState } from 'lexical';
|
||||||
import type { Account as BaseAccount, BackendVersion, CreateStatusParams, Group, MediaAttachment, Status as BaseStatus, Tag, Poll } from 'pl-api';
|
import type { Account as BaseAccount, BackendVersion, CreateStatusParams, Group, MediaAttachment, Status as BaseStatus, Tag, Poll, ScheduledStatus } from 'pl-api';
|
||||||
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
|
import type { AutoSuggestion } from 'soapbox/components/autosuggest-input';
|
||||||
import type { Emoji } from 'soapbox/features/emoji';
|
import type { Emoji } from 'soapbox/features/emoji';
|
||||||
import type { Account, Status } from 'soapbox/normalizers';
|
import type { Account, Status } from 'soapbox/normalizers';
|
||||||
|
@ -99,6 +99,7 @@ const messages = defineMessages({
|
||||||
scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' },
|
scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' },
|
||||||
success: { id: 'compose.submit_success', defaultMessage: 'Your post was sent!' },
|
success: { id: 'compose.submit_success', defaultMessage: 'Your post was sent!' },
|
||||||
editSuccess: { id: 'compose.edit_success', defaultMessage: 'Your post was edited' },
|
editSuccess: { id: 'compose.edit_success', defaultMessage: 'Your post was edited' },
|
||||||
|
scheduledSuccess: { id: 'compose.scheduled_success', defaultMessage: 'Your post was scheduled' },
|
||||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||||
view: { id: 'toast.view', defaultMessage: 'View' },
|
view: { id: 'toast.view', defaultMessage: 'View' },
|
||||||
|
@ -176,11 +177,11 @@ const replyCompose = (
|
||||||
const action: ComposeReplyAction = {
|
const action: ComposeReplyAction = {
|
||||||
type: COMPOSE_REPLY,
|
type: COMPOSE_REPLY,
|
||||||
composeId: 'compose-modal',
|
composeId: 'compose-modal',
|
||||||
status: status,
|
status,
|
||||||
account,
|
account,
|
||||||
explicitAddressing,
|
explicitAddressing,
|
||||||
preserveSpoilers,
|
preserveSpoilers,
|
||||||
rebloggedBy: rebloggedBy,
|
rebloggedBy,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
|
@ -208,7 +209,7 @@ const quoteCompose = (status: ComposeQuoteAction['status']) =>
|
||||||
const action: ComposeQuoteAction = {
|
const action: ComposeQuoteAction = {
|
||||||
type: COMPOSE_QUOTE,
|
type: COMPOSE_QUOTE,
|
||||||
composeId: 'compose-modal',
|
composeId: 'compose-modal',
|
||||||
status: status,
|
status,
|
||||||
account: selectOwnAccount(state),
|
account: selectOwnAccount(state),
|
||||||
explicitAddressing,
|
explicitAddressing,
|
||||||
};
|
};
|
||||||
|
@ -286,7 +287,7 @@ const directComposeById = (accountId: string) =>
|
||||||
dispatch(openModal('COMPOSE'));
|
dispatch(openModal('COMPOSE'));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, composeId: string, data: BaseStatus, status: string, edit?: boolean) => {
|
const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, composeId: string, data: BaseStatus | ScheduledStatus, status: string, edit?: boolean) => {
|
||||||
if (!dispatch || !getState) return;
|
if (!dispatch || !getState) return;
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -294,12 +295,19 @@ const handleComposeSubmit = (dispatch: AppDispatch, getState: () => RootState, c
|
||||||
const accountUrl = getAccount(state, state.me as string)!.url;
|
const accountUrl = getAccount(state, state.me as string)!.url;
|
||||||
const draftId = getState().compose.get(composeId)!.draft_id;
|
const draftId = getState().compose.get(composeId)!.draft_id;
|
||||||
|
|
||||||
dispatch(insertIntoTagHistory(composeId, data.tags || [], status));
|
dispatch(submitComposeSuccess(composeId, data, accountUrl, draftId));
|
||||||
dispatch(submitComposeSuccess(composeId, { ...data }, accountUrl, draftId));
|
if (data.scheduled_at === null) {
|
||||||
toast.success(edit ? messages.editSuccess : messages.success, {
|
dispatch(insertIntoTagHistory(composeId, data.tags || [], status));
|
||||||
actionLabel: messages.view,
|
toast.success(edit ? messages.editSuccess : messages.success, {
|
||||||
actionLink: `/@${data.account.acct}/posts/${data.id}`,
|
actionLabel: messages.view,
|
||||||
});
|
actionLink: `/@${data.account.acct}/posts/${data.id}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.success(messages.scheduledSuccess, {
|
||||||
|
actionLabel: messages.view,
|
||||||
|
actionLink: '/scheduled_statuses',
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const needsDescriptions = (state: RootState, composeId: string) => {
|
const needsDescriptions = (state: RootState, composeId: string) => {
|
||||||
|
@ -420,7 +428,8 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
return dispatch(createStatus(params, idempotencyKey, statusId)).then((data) => {
|
return dispatch(createStatus(params, idempotencyKey, statusId)).then((data) => {
|
||||||
if (!statusId && data.visibility === 'direct' && getState().conversations.mounted <= 0 && history) {
|
console.log(data);
|
||||||
|
if (!statusId && data.scheduled_at === null && data.visibility === 'direct' && getState().conversations.mounted <= 0 && history) {
|
||||||
history.push('/conversations');
|
history.push('/conversations');
|
||||||
}
|
}
|
||||||
handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId);
|
handleComposeSubmit(dispatch, getState, composeId, data, status, !!statusId);
|
||||||
|
@ -434,10 +443,10 @@ const submitComposeRequest = (composeId: string) => ({
|
||||||
composeId,
|
composeId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitComposeSuccess = (composeId: string, status: BaseStatus, accountUrl: string, draftId?: string | null) => ({
|
const submitComposeSuccess = (composeId: string, status: BaseStatus | ScheduledStatus, accountUrl: string, draftId?: string | null) => ({
|
||||||
type: COMPOSE_SUBMIT_SUCCESS,
|
type: COMPOSE_SUBMIT_SUCCESS,
|
||||||
composeId,
|
composeId,
|
||||||
status: status,
|
status,
|
||||||
accountUrl,
|
accountUrl,
|
||||||
draftId,
|
draftId,
|
||||||
});
|
});
|
||||||
|
@ -894,7 +903,7 @@ const eventDiscussionCompose = (composeId: string, status: ComposeEventReplyActi
|
||||||
return dispatch({
|
return dispatch({
|
||||||
type: COMPOSE_EVENT_REPLY,
|
type: COMPOSE_EVENT_REPLY,
|
||||||
composeId,
|
composeId,
|
||||||
status: status,
|
status,
|
||||||
account: selectOwnAccount(state),
|
account: selectOwnAccount(state),
|
||||||
explicitAddressing,
|
explicitAddressing,
|
||||||
});
|
});
|
||||||
|
|
|
@ -100,6 +100,7 @@ const importFetchedStatus = (status: BaseStatus, idempotencyKey?: string) =>
|
||||||
// or a repost can appear of a deleted account. Skip these statuses.
|
// or a repost can appear of a deleted account. Skip these statuses.
|
||||||
const isBroken = (status: BaseStatus) => {
|
const isBroken = (status: BaseStatus) => {
|
||||||
try {
|
try {
|
||||||
|
if (status.scheduled_at !== null) return true;
|
||||||
// Skip empty accounts
|
// Skip empty accounts
|
||||||
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
|
// https://gitlab.com/soapbox-pub/soapbox/-/issues/424
|
||||||
if (!status.account.id) return true;
|
if (!status.account.id) return true;
|
||||||
|
|
|
@ -62,7 +62,7 @@ const createStatus = (params: CreateStatusParams, idempotencyKey: string, status
|
||||||
return (statusId === null ? getClient(getState()).statuses.createStatus(params) : getClient(getState()).statuses.editStatus(statusId, params))
|
return (statusId === null ? getClient(getState()).statuses.createStatus(params) : getClient(getState()).statuses.editStatus(statusId, params))
|
||||||
.then((status) => {
|
.then((status) => {
|
||||||
// The backend might still be processing the rich media attachment
|
// The backend might still be processing the rich media attachment
|
||||||
const expectsCard = !status.card && shouldHaveCard(status);
|
const expectsCard = status.scheduled_at === null && !status.card && shouldHaveCard(status);
|
||||||
|
|
||||||
dispatch(importFetchedStatus({ ...status, expectsCard } as BaseStatus, idempotencyKey));
|
dispatch(importFetchedStatus({ ...status, expectsCard } as BaseStatus, idempotencyKey));
|
||||||
dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId });
|
dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId });
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { IconButton, Portal } from '../ui';
|
||||||
|
|
||||||
import DropdownMenuItem, { MenuItem } from './dropdown-menu-item';
|
import DropdownMenuItem, { MenuItem } from './dropdown-menu-item';
|
||||||
|
|
||||||
import type { Status } from 'soapbox/types/entities';
|
import type { Status } from 'soapbox/normalizers';
|
||||||
|
|
||||||
type Menu = Array<MenuItem | null>;
|
type Menu = Array<MenuItem | null>;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import EventActionButton from '../components/event-action-button';
|
||||||
import EventDate from '../components/event-date';
|
import EventDate from '../components/event-date';
|
||||||
|
|
||||||
import type { Menu as MenuType } from 'soapbox/components/dropdown-menu';
|
import type { Menu as MenuType } from 'soapbox/components/dropdown-menu';
|
||||||
import type { Status as StatusEntity } from 'soapbox/normalizers';
|
import type { Status } from 'soapbox/normalizers';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
bannerHeader: { id: 'event.banner', defaultMessage: 'Event banner' },
|
bannerHeader: { id: 'event.banner', defaultMessage: 'Event banner' },
|
||||||
|
@ -61,7 +61,7 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IEventHeader {
|
interface IEventHeader {
|
||||||
status?: StatusEntity;
|
status?: Pick<Status, 'id' | 'account' | 'bookmarked' | 'event' | 'group' | 'pinned' | 'reblog' | 'reblogged' | 'sensitive' | 'uri' | 'url' | 'visibility'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { statusSchema } from 'pl-api';
|
import { statusSchema, type ScheduledStatus } from 'pl-api';
|
||||||
|
|
||||||
import { Entities } from 'soapbox/entity-store/entities';
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
|
|
||||||
import type { ScheduledStatus } from 'soapbox/reducers/scheduled-statuses';
|
|
||||||
import type { RootState } from 'soapbox/store';
|
import type { RootState } from 'soapbox/store';
|
||||||
|
|
||||||
const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => {
|
const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => {
|
||||||
|
@ -12,17 +11,18 @@ const buildStatus = (state: RootState, scheduledStatus: ScheduledStatus) => {
|
||||||
|
|
||||||
const status = statusSchema.parse({
|
const status = statusSchema.parse({
|
||||||
account,
|
account,
|
||||||
content: scheduledStatus.text.replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
content: scheduledStatus.params.text?.replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||||
created_at: scheduledStatus.scheduled_at,
|
created_at: scheduledStatus.params.scheduled_at,
|
||||||
id: scheduledStatus.id,
|
id: scheduledStatus.id,
|
||||||
in_reply_to_id: scheduledStatus.in_reply_to_id,
|
in_reply_to_id: scheduledStatus.params.in_reply_to_id,
|
||||||
media_attachments: scheduledStatus.media_attachments,
|
media_attachments: scheduledStatus.media_attachments,
|
||||||
poll: scheduledStatus.poll,
|
poll: scheduledStatus.params.poll,
|
||||||
sensitive: scheduledStatus.sensitive,
|
sensitive: scheduledStatus.params.sensitive,
|
||||||
uri: `/scheduled_statuses/${scheduledStatus.id}`,
|
uri: `/scheduled_statuses/${scheduledStatus.id}`,
|
||||||
url: `/scheduled_statuses/${scheduledStatus.id}`,
|
url: `/scheduled_statuses/${scheduledStatus.id}`,
|
||||||
visibility: scheduledStatus.visibility,
|
visibility: scheduledStatus.params.visibility,
|
||||||
});
|
});
|
||||||
|
console.log(scheduledStatus.params);
|
||||||
|
|
||||||
return normalizeStatus(status);
|
return normalizeStatus(status);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,8 +13,6 @@ import { buildStatus } from '../builder';
|
||||||
|
|
||||||
import ScheduledStatusActionBar from './scheduled-status-action-bar';
|
import ScheduledStatusActionBar from './scheduled-status-action-bar';
|
||||||
|
|
||||||
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
interface IScheduledStatus {
|
interface IScheduledStatus {
|
||||||
statusId: string;
|
statusId: string;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +22,7 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
|
||||||
const scheduledStatus = state.scheduled_statuses.get(statusId);
|
const scheduledStatus = state.scheduled_statuses.get(statusId);
|
||||||
if (!scheduledStatus) return null;
|
if (!scheduledStatus) return null;
|
||||||
return buildStatus(state, scheduledStatus);
|
return buildStatus(state, scheduledStatus);
|
||||||
}) as StatusEntity | null;
|
});
|
||||||
|
|
||||||
if (!status) return null;
|
if (!status) return null;
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,8 @@ import { unescapeHTML } from '../utils/html';
|
||||||
|
|
||||||
import type { Emoji } from 'soapbox/features/emoji';
|
import type { Emoji } from 'soapbox/features/emoji';
|
||||||
import type { Language } from 'soapbox/features/preferences';
|
import type { Language } from 'soapbox/features/preferences';
|
||||||
import type { Account } from 'soapbox/normalizers';
|
import type { Account, Status } from 'soapbox/normalizers';
|
||||||
import type { APIEntity, Status, Status as StatusEntity } from 'soapbox/types/entities';
|
import type { APIEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
|
const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ const statusToMentionsArray = (status: Pick<Status, 'account' | 'mentions'>, acc
|
||||||
.delete(account.acct);
|
.delete(account.acct);
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusToMentionsAccountIdsArray = (status: Pick<StatusEntity, 'mentions' | 'account'>, account: Pick<Account, 'id'>, parentRebloggedBy?: string | null) => {
|
const statusToMentionsAccountIdsArray = (status: Pick<Status, 'mentions' | 'account'>, account: Pick<Account, 'id'>, parentRebloggedBy?: string | null) => {
|
||||||
const mentions = status.mentions.map((m) => m.id);
|
const mentions = status.mentions.map((m) => m.id);
|
||||||
|
|
||||||
return ImmutableOrderedSet<string>([status.account.id])
|
return ImmutableOrderedSet<string>([status.account.id])
|
||||||
|
|
|
@ -17,8 +17,8 @@ import {
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
|
|
||||||
|
import type { Status } from 'pl-api';
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { Status } from 'soapbox/normalizers';
|
|
||||||
|
|
||||||
const ReducerRecord = ImmutableRecord({
|
const ReducerRecord = ImmutableRecord({
|
||||||
inReplyTos: ImmutableMap<string, string>(),
|
inReplyTos: ImmutableMap<string, string>(),
|
||||||
|
@ -27,14 +27,8 @@ const ReducerRecord = ImmutableRecord({
|
||||||
|
|
||||||
type State = ReturnType<typeof ReducerRecord>;
|
type State = ReturnType<typeof ReducerRecord>;
|
||||||
|
|
||||||
/** Minimal status fields needed to process context. */
|
|
||||||
type ContextStatus = {
|
|
||||||
id: string;
|
|
||||||
in_reply_to_id: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Import a single status into the reducer, setting replies and replyTos. */
|
/** Import a single status into the reducer, setting replies and replyTos. */
|
||||||
const importStatus = (state: State, status: ContextStatus, idempotencyKey?: string): State => {
|
const importStatus = (state: State, status: Pick<Status, 'id' | 'in_reply_to_id'>, idempotencyKey?: string): State => {
|
||||||
const { id, in_reply_to_id: inReplyToId } = status;
|
const { id, in_reply_to_id: inReplyToId } = status;
|
||||||
if (!inReplyToId) return state;
|
if (!inReplyToId) return state;
|
||||||
|
|
||||||
|
@ -52,7 +46,7 @@ const importStatus = (state: State, status: ContextStatus, idempotencyKey?: stri
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Import multiple statuses into the state. */
|
/** Import multiple statuses into the state. */
|
||||||
const importStatuses = (state: State, statuses: ContextStatus[]): State =>
|
const importStatuses = (state: State, statuses: Array<Pick<Status, 'id' | 'in_reply_to_id'>>): State =>
|
||||||
state.withMutations(state => {
|
state.withMutations(state => {
|
||||||
statuses.forEach(status => importStatus(state, status));
|
statuses.forEach(status => importStatus(state, status));
|
||||||
});
|
});
|
||||||
|
@ -93,7 +87,7 @@ const connectNodes = (state: State, fromId: string, toId: string): State => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Import a branch of ancestors or descendants, in relation to statusId. */
|
/** Import a branch of ancestors or descendants, in relation to statusId. */
|
||||||
const importBranch = (state: State, statuses: ContextStatus[], statusId?: string): State =>
|
const importBranch = (state: State, statuses: Array<Pick<Status, 'id' | 'in_reply_to_id'>>, statusId?: string): State =>
|
||||||
state.withMutations(state => {
|
state.withMutations(state => {
|
||||||
statuses.forEach((status, i) => {
|
statuses.forEach((status, i) => {
|
||||||
const prevId = statusId && i === 0 ? statusId : (statuses[i - 1] || {}).id;
|
const prevId = statusId && i === 0 ? statusId : (statuses[i - 1] || {}).id;
|
||||||
|
@ -118,8 +112,8 @@ const importBranch = (state: State, statuses: ContextStatus[], statusId?: string
|
||||||
const normalizeContext = (
|
const normalizeContext = (
|
||||||
state: State,
|
state: State,
|
||||||
id: string,
|
id: string,
|
||||||
ancestors: ContextStatus[],
|
ancestors: Array<Pick<Status, 'id' | 'in_reply_to_id'>>,
|
||||||
descendants: ContextStatus[],
|
descendants: Array<Pick<Status, 'id' | 'in_reply_to_id'>>,
|
||||||
) => state.withMutations(state => {
|
) => state.withMutations(state => {
|
||||||
importBranch(state, ancestors);
|
importBranch(state, ancestors);
|
||||||
importBranch(state, descendants, id);
|
importBranch(state, descendants, id);
|
||||||
|
@ -171,14 +165,14 @@ const filterContexts = (
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Add a fake status ID for a pending status. */
|
/** Add a fake status ID for a pending status. */
|
||||||
const importPendingStatus = (state: State, params: ContextStatus, idempotencyKey: string): State => {
|
const importPendingStatus = (state: State, params: Pick<Status, 'id' | 'in_reply_to_id'>, idempotencyKey: string): State => {
|
||||||
const id = `末pending-${idempotencyKey}`;
|
const id = `末pending-${idempotencyKey}`;
|
||||||
const { in_reply_to_id } = params;
|
const { in_reply_to_id } = params;
|
||||||
return importStatus(state, { id, in_reply_to_id });
|
return importStatus(state, { id, in_reply_to_id });
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Delete a pending status from the reducer. */
|
/** Delete a pending status from the reducer. */
|
||||||
const deletePendingStatus = (state: State, params: ContextStatus, idempotencyKey: string): State => {
|
const deletePendingStatus = (state: State, params: Pick<Status, 'id' | 'in_reply_to_id'>, idempotencyKey: string): State => {
|
||||||
const id = `末pending-${idempotencyKey}`;
|
const id = `末pending-${idempotencyKey}`;
|
||||||
const { in_reply_to_id: inReplyToId } = params;
|
const { in_reply_to_id: inReplyToId } = params;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
import { STATUS_IMPORT, STATUSES_IMPORT } from 'soapbox/actions/importer';
|
import { STATUS_IMPORT, STATUSES_IMPORT } from 'soapbox/actions/importer';
|
||||||
import {
|
import {
|
||||||
|
@ -8,34 +8,19 @@ import {
|
||||||
} from 'soapbox/actions/scheduled-statuses';
|
} from 'soapbox/actions/scheduled-statuses';
|
||||||
import { STATUS_CREATE_SUCCESS } from 'soapbox/actions/statuses';
|
import { STATUS_CREATE_SUCCESS } from 'soapbox/actions/statuses';
|
||||||
|
|
||||||
|
import type { Status, ScheduledStatus } from 'pl-api';
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { StatusVisibility } from 'soapbox/normalizers/status';
|
|
||||||
import type { APIEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
const ScheduledStatusRecord = ImmutableRecord({
|
|
||||||
id: '',
|
|
||||||
scheduled_at: new Date(),
|
|
||||||
media_attachments: null as ImmutableList<ImmutableMap<string, any>> | null,
|
|
||||||
text: '',
|
|
||||||
in_reply_to_id: null as string | null,
|
|
||||||
media_ids: null as ImmutableList<string> | null,
|
|
||||||
sensitive: false,
|
|
||||||
spoiler_text: '',
|
|
||||||
visibility: 'public' as StatusVisibility,
|
|
||||||
poll: null as ImmutableMap<string, any> | null,
|
|
||||||
});
|
|
||||||
|
|
||||||
type ScheduledStatus = ReturnType<typeof ScheduledStatusRecord>;
|
|
||||||
type State = ImmutableMap<string, ScheduledStatus>;
|
type State = ImmutableMap<string, ScheduledStatus>;
|
||||||
|
|
||||||
const initialState: State = ImmutableMap();
|
const initialState: State = ImmutableMap();
|
||||||
|
|
||||||
const importStatus = (state: State, { params, ...status }: APIEntity) => {
|
const importStatus = (state: State, status: Status | ScheduledStatus) => {
|
||||||
if (!status.scheduled_at) return state;
|
if (!status.scheduled_at) return state;
|
||||||
return state.set(status.id, ScheduledStatusRecord(ImmutableMap(fromJS({ ...status, ...params }))));
|
return state.set(status.id, status);
|
||||||
};
|
};
|
||||||
|
|
||||||
const importStatuses = (state: State, statuses: APIEntity[]) =>
|
const importStatuses = (state: State, statuses: Array<Status | ScheduledStatus>) =>
|
||||||
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));
|
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));
|
||||||
|
|
||||||
const deleteStatus = (state: State, statusId: string) => state.delete(statusId);
|
const deleteStatus = (state: State, statusId: string) => state.delete(statusId);
|
||||||
|
@ -56,7 +41,4 @@ const scheduled_statuses = (state: State = initialState, action: AnyAction) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export { scheduled_statuses as default };
|
||||||
ScheduledStatus,
|
|
||||||
scheduled_statuses as default,
|
|
||||||
};
|
|
||||||
|
|
|
@ -69,9 +69,8 @@ import {
|
||||||
SCHEDULED_STATUS_CANCEL_SUCCESS,
|
SCHEDULED_STATUS_CANCEL_SUCCESS,
|
||||||
} from '../actions/scheduled-statuses';
|
} from '../actions/scheduled-statuses';
|
||||||
|
|
||||||
import type { PaginatedResponse, Status } from 'pl-api';
|
import type { PaginatedResponse, ScheduledStatus, Status } from 'pl-api';
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { APIEntity, Status as StatusEntity } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
const StatusListRecord = ImmutableRecord({
|
const StatusListRecord = ImmutableRecord({
|
||||||
next: null as (() => Promise<PaginatedResponse<Status>>) | null,
|
next: null as (() => Promise<PaginatedResponse<Status>>) | null,
|
||||||
|
@ -92,16 +91,16 @@ const initialState: State = ImmutableMap({
|
||||||
joined_events: StatusListRecord(),
|
joined_events: StatusListRecord(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const getStatusId = (status: string | APIEntity) => typeof status === 'string' ? status : status.id;
|
const getStatusId = (status: string | Pick<Status, 'id'>) => typeof status === 'string' ? status : status.id;
|
||||||
|
|
||||||
const getStatusIds = (statuses: APIEntity[] = []) => (
|
const getStatusIds = (statuses: Array<string | Pick<Status, 'id'>> = []) => (
|
||||||
ImmutableOrderedSet(statuses.map(getStatusId))
|
ImmutableOrderedSet(statuses.map(getStatusId))
|
||||||
);
|
);
|
||||||
|
|
||||||
const setLoading = (state: State, listType: string, loading: boolean) =>
|
const setLoading = (state: State, listType: string, loading: boolean) =>
|
||||||
state.update(listType, StatusListRecord(), listMap => listMap.set('isLoading', loading));
|
state.update(listType, StatusListRecord(), listMap => listMap.set('isLoading', loading));
|
||||||
|
|
||||||
const normalizeList = (state: State, listType: string, statuses: APIEntity[], next: (() => Promise<PaginatedResponse<Status>>) | null) =>
|
const normalizeList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) =>
|
||||||
state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||||
map.set('next', next);
|
map.set('next', next);
|
||||||
map.set('loaded', true);
|
map.set('loaded', true);
|
||||||
|
@ -109,7 +108,7 @@ const normalizeList = (state: State, listType: string, statuses: APIEntity[], ne
|
||||||
map.set('items', getStatusIds(statuses));
|
map.set('items', getStatusIds(statuses));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const appendToList = (state: State, listType: string, statuses: APIEntity[], next: (() => Promise<PaginatedResponse<Status>>) | null) => {
|
const appendToList = (state: State, listType: string, statuses: Array<string | Pick<Status, 'id'>>, next: (() => Promise<PaginatedResponse<Status>>) | null) => {
|
||||||
const newIds = getStatusIds(statuses);
|
const newIds = getStatusIds(statuses);
|
||||||
|
|
||||||
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||||
|
@ -119,24 +118,24 @@ const appendToList = (state: State, listType: string, statuses: APIEntity[], nex
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const prependOneToList = (state: State, listType: string, status: APIEntity) => {
|
const prependOneToList = (state: State, listType: string, status: string | Pick<Status, 'id'>) => {
|
||||||
const statusId = getStatusId(status);
|
const statusId = getStatusId(status);
|
||||||
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items =>
|
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items =>
|
||||||
ImmutableOrderedSet([statusId]).union(items as ImmutableOrderedSet<string>),
|
ImmutableOrderedSet([statusId]).union(items as ImmutableOrderedSet<string>),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeOneFromList = (state: State, listType: string, status: APIEntity) => {
|
const removeOneFromList = (state: State, listType: string, status: string | Pick<Status, 'id'>) => {
|
||||||
const statusId = getStatusId(status);
|
const statusId = getStatusId(status);
|
||||||
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items => items.delete(statusId)));
|
return state.update(listType, StatusListRecord(), listMap => listMap.update('items', items => items.delete(statusId)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const maybeAppendScheduledStatus = (state: State, status: APIEntity) => {
|
const maybeAppendScheduledStatus = (state: State, status: Pick<ScheduledStatus | Status, 'id' | 'scheduled_at'>) => {
|
||||||
if (!status.scheduled_at) return state;
|
if (!status.scheduled_at) return state;
|
||||||
return prependOneToList(state, 'scheduled_statuses', getStatusId(status));
|
return prependOneToList(state, 'scheduled_statuses', getStatusId(status));
|
||||||
};
|
};
|
||||||
|
|
||||||
const addBookmarkToLists = (state: State, status: APIEntity) => {
|
const addBookmarkToLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
|
||||||
state = prependOneToList(state, 'bookmarks', status);
|
state = prependOneToList(state, 'bookmarks', status);
|
||||||
const folderId = status.bookmark_folder;
|
const folderId = status.bookmark_folder;
|
||||||
if (folderId) {
|
if (folderId) {
|
||||||
|
@ -145,7 +144,7 @@ const addBookmarkToLists = (state: State, status: APIEntity) => {
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeBookmarkFromLists = (state: State, status: StatusEntity) => {
|
const removeBookmarkFromLists = (state: State, status: Pick<Status, 'id' | 'bookmark_folder'>) => {
|
||||||
state = removeOneFromList(state, 'bookmarks', status);
|
state = removeOneFromList(state, 'bookmarks', status);
|
||||||
const folderId = status.bookmark_folder;
|
const folderId = status.bookmark_folder;
|
||||||
if (folderId) {
|
if (folderId) {
|
||||||
|
|
|
@ -30,10 +30,9 @@ import {
|
||||||
} from '../actions/timelines';
|
} from '../actions/timelines';
|
||||||
|
|
||||||
import type { ReducerStatus } from './statuses';
|
import type { ReducerStatus } from './statuses';
|
||||||
import type { PaginatedResponse, Status as BaseStatus } from 'pl-api';
|
import type { PaginatedResponse, Status as BaseStatus, Relationship } from 'pl-api';
|
||||||
import type { AnyAction } from 'redux';
|
import type { AnyAction } from 'redux';
|
||||||
import type { ImportPosition } from 'soapbox/entity-store/types';
|
import type { ImportPosition } from 'soapbox/entity-store/types';
|
||||||
import type { APIEntity, Status } from 'soapbox/types/entities';
|
|
||||||
|
|
||||||
const TRUNCATE_LIMIT = 40;
|
const TRUNCATE_LIMIT = 40;
|
||||||
const TRUNCATE_SIZE = 20;
|
const TRUNCATE_SIZE = 20;
|
||||||
|
@ -187,10 +186,10 @@ const updateTop = (state: State, timelineId: string, top: boolean) =>
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const isReblogOf = (reblog: Pick<ReducerStatus, 'reblog'>, status: Pick<ReducerStatus, 'id'>) => reblog.reblog === status.id;
|
const isReblogOf = (reblog: Pick<ReducerStatus, 'reblog'>, status: Pick<ReducerStatus, 'id'>) => reblog.reblog === status.id;
|
||||||
const statusToReference = (status: Pick<Status, 'id' | 'account'>) => [status.id, status.account];
|
const statusToReference = (status: Pick<ReducerStatus, 'id' | 'account'>) => [status.id, status.account];
|
||||||
|
|
||||||
const buildReferencesTo = (
|
const buildReferencesTo = (
|
||||||
statuses: ImmutableMap<string, Pick<ReducerStatus, 'id' | 'account' | 'reblog'>>,
|
statuses: ImmutableMap<string, ReducerStatus>,
|
||||||
status: Pick<ReducerStatus, 'id'>,
|
status: Pick<ReducerStatus, 'id'>,
|
||||||
) => (
|
) => (
|
||||||
statuses
|
statuses
|
||||||
|
@ -204,12 +203,12 @@ const buildReferencesTo = (
|
||||||
// statuses.getIn([statusId, 'account']) === relationship.id,
|
// statuses.getIn([statusId, 'account']) === relationship.id,
|
||||||
// ));
|
// ));
|
||||||
|
|
||||||
const filterTimelines = (state: State, relationship: APIEntity, statuses: ImmutableMap<string, ReducerStatus>) =>
|
const filterTimelines = (state: State, relationship: Relationship, statuses: ImmutableMap<string, ReducerStatus>) =>
|
||||||
state.withMutations(state => {
|
state.withMutations(state => {
|
||||||
statuses.forEach(status => {
|
statuses.forEach(status => {
|
||||||
if (status.account !== relationship.id) return;
|
if (status.account.id !== relationship.id) return;
|
||||||
const references = buildReferencesTo(statuses, status);
|
const references = buildReferencesTo(statuses, status);
|
||||||
deleteStatus(state, status.id, status.account!.id, references, relationship.id);
|
deleteStatus(state, status.id, status.account.id, references, relationship.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -236,10 +235,10 @@ const timelineDequeue = (state: State, timelineId: string) => {
|
||||||
// timeline.set('items', addStatusId(items, null));
|
// timeline.set('items', addStatusId(items, null));
|
||||||
// }));
|
// }));
|
||||||
|
|
||||||
const getTimelinesForStatus = (status: APIEntity) => {
|
const getTimelinesForStatus = (status: Pick<BaseStatus, 'visibility' | 'group'>) => {
|
||||||
switch (status.visibility) {
|
switch (status.visibility) {
|
||||||
case 'group':
|
case 'group':
|
||||||
return [`group:${status.group?.id || status.group_id}`];
|
return [`group:${status.group?.id}`];
|
||||||
case 'direct':
|
case 'direct':
|
||||||
return ['direct'];
|
return ['direct'];
|
||||||
case 'public':
|
case 'public':
|
||||||
|
@ -261,7 +260,7 @@ const replaceId = (ids: ImmutableOrderedSet<string>, oldId: string, newId: strin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const importPendingStatus = (state: State, params: APIEntity, idempotencyKey: string) => {
|
const importPendingStatus = (state: State, params: BaseStatus, idempotencyKey: string) => {
|
||||||
const statusId = `末pending-${idempotencyKey}`;
|
const statusId = `末pending-${idempotencyKey}`;
|
||||||
|
|
||||||
return state.withMutations(state => {
|
return state.withMutations(state => {
|
||||||
|
@ -285,7 +284,7 @@ const replacePendingStatus = (state: State, idempotencyKey: string, newId: strin
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const importStatus = (state: State, status: APIEntity, idempotencyKey: string) =>
|
const importStatus = (state: State, status: BaseStatus, idempotencyKey: string) =>
|
||||||
state.withMutations(state => {
|
state.withMutations(state => {
|
||||||
replacePendingStatus(state, idempotencyKey, status.id);
|
replacePendingStatus(state, idempotencyKey, status.id);
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,7 @@ const makeGetStatus = () => createSelector(
|
||||||
if (group) return state.entities[Entities.GROUPS]?.store[group] as Group;
|
if (group) return state.entities[Entities.GROUPS]?.store[group] as Group;
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
(state: RootState, { id }: APIStatus) => state.polls.get(id) || null,
|
||||||
(_state: RootState, { username }: APIStatus) => username,
|
(_state: RootState, { username }: APIStatus) => username,
|
||||||
getFilters,
|
getFilters,
|
||||||
(state: RootState) => state.me,
|
(state: RootState) => state.me,
|
||||||
|
@ -143,7 +144,7 @@ const makeGetStatus = () => createSelector(
|
||||||
(state: RootState) => getLocale(state, 'en'),
|
(state: RootState) => getLocale(state, 'en'),
|
||||||
],
|
],
|
||||||
|
|
||||||
(statusBase, statusReblog, statusGroup, username, filters, me, features, locale) => {
|
(statusBase, statusReblog, statusGroup, poll, username, filters, me, features, locale) => {
|
||||||
if (!statusBase) return null;
|
if (!statusBase) return null;
|
||||||
const { account } = statusBase;
|
const { account } = statusBase;
|
||||||
const accountUsername = account.acct;
|
const accountUsername = account.acct;
|
||||||
|
@ -161,6 +162,7 @@ const makeGetStatus = () => createSelector(
|
||||||
...statusBase,
|
...statusBase,
|
||||||
reblog: statusReblog || null,
|
reblog: statusReblog || null,
|
||||||
group: statusGroup || null,
|
group: statusGroup || null,
|
||||||
|
poll,
|
||||||
filtered,
|
filtered,
|
||||||
};
|
};
|
||||||
// if (map.currentLanguage === null && map.content_map?.size) {
|
// if (map.currentLanguage === null && map.content_map?.size) {
|
||||||
|
|
Loading…
Reference in a new issue