pl-fe: types

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-26 14:08:26 +01:00
parent 32bd753064
commit 8529bf64ad
8 changed files with 64 additions and 63 deletions

View file

@ -179,7 +179,7 @@ const joinEventSuccess = (statusId: string) => ({
statusId,
});
const joinEventFail = (error: unknown, statusId: string, previousState: string | null) => ({
const joinEventFail = (error: unknown, statusId: string, previousState: Exclude<Status['event'], null>['join_state'] | null) => ({
type: EVENT_JOIN_FAIL,
error,
statusId,
@ -214,7 +214,7 @@ const leaveEventSuccess = (statusId: string) => ({
statusId,
});
const leaveEventFail = (error: unknown, statusId: string, previousState: string | null) => ({
const leaveEventFail = (error: unknown, statusId: string, previousState: Exclude<Status['event'], null>['join_state'] | null) => ({
type: EVENT_LEAVE_FAIL,
statusId,
error,

View file

@ -99,20 +99,20 @@ const editStatus = (statusId: string) => (dispatch: AppDispatch, getState: () =>
const status = state.statuses[statusId]!;
const poll = status.poll_id ? state.polls[status.poll_id] : undefined;
dispatch({ type: STATUS_FETCH_SOURCE_REQUEST });
dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_REQUEST });
return getClient(state).statuses.getStatusSource(statusId).then(response => {
dispatch({ type: STATUS_FETCH_SOURCE_SUCCESS });
dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_SUCCESS });
dispatch(setComposeToStatus(status, poll, response.text, response.spoiler_text, response.content_type, false));
useModalsStore.getState().openModal('COMPOSE');
}).catch(error => {
dispatch({ type: STATUS_FETCH_SOURCE_FAIL, error });
dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_FAIL, error });
});
};
const fetchStatus = (statusId: string, intl?: IntlShape) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: STATUS_FETCH_REQUEST, statusId });
dispatch<StatusesAction>({ type: STATUS_FETCH_REQUEST, statusId });
const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
language: intl.locale,
@ -120,10 +120,10 @@ const fetchStatus = (statusId: string, intl?: IntlShape) =>
return getClient(getState()).statuses.getStatus(statusId, params).then(status => {
dispatch(importEntities({ statuses: [status] }));
dispatch({ type: STATUS_FETCH_SUCCESS, status });
dispatch<StatusesAction>({ type: STATUS_FETCH_SUCCESS, status });
return status;
}).catch(error => {
dispatch({ type: STATUS_FETCH_FAIL, statusId, error, skipAlert: true });
dispatch<StatusesAction>({ type: STATUS_FETCH_FAIL, statusId, error, skipAlert: true });
});
};
@ -136,10 +136,10 @@ const deleteStatus = (statusId: string, withRedraft = false) =>
const status = state.statuses[statusId]!;
const poll = status.poll_id ? state.polls[status.poll_id] : undefined;
dispatch({ type: STATUS_DELETE_REQUEST, params: status });
dispatch<StatusesAction>({ type: STATUS_DELETE_REQUEST, params: status });
return getClient(state).statuses.deleteStatus(statusId).then(response => {
dispatch({ type: STATUS_DELETE_SUCCESS, statusId });
dispatch<StatusesAction>({ type: STATUS_DELETE_SUCCESS, statusId });
dispatch(deleteFromTimelines(statusId));
if (withRedraft) {
@ -148,7 +148,7 @@ const deleteStatus = (statusId: string, withRedraft = false) =>
}
})
.catch(error => {
dispatch({ type: STATUS_DELETE_FAIL, params: status, error });
dispatch<StatusesAction>({ type: STATUS_DELETE_FAIL, params: status, error });
});
};
@ -157,7 +157,7 @@ const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) =>
const fetchContext = (statusId: string, intl?: IntlShape) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: CONTEXT_FETCH_REQUEST, statusId });
dispatch<StatusesAction>({ type: CONTEXT_FETCH_REQUEST, statusId });
const params = intl && useSettingsStore.getState().settings.autoTranslate ? {
language: intl.locale,
@ -167,14 +167,14 @@ const fetchContext = (statusId: string, intl?: IntlShape) =>
const { ancestors, descendants } = context;
const statuses = ancestors.concat(descendants);
dispatch(importEntities({ statuses }));
dispatch({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
dispatch<StatusesAction>({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
return context;
}).catch(error => {
if (error.response?.status === 404) {
dispatch(deleteFromTimelines(statusId));
}
dispatch({ type: CONTEXT_FETCH_FAIL, statusId, error, skipAlert: true });
dispatch<StatusesAction>({ type: CONTEXT_FETCH_FAIL, statusId, error, skipAlert: true });
});
};
@ -319,13 +319,13 @@ const translateStatus = (statusId: string, targetLanguage: string, lazy?: boolea
handleTranslateMany();
} else {
return client.statuses.translateStatus(statusId, targetLanguage).then(response => {
dispatch({
dispatch<StatusesAction>({
type: STATUS_TRANSLATE_SUCCESS,
statusId,
translation: response,
});
}).catch(error => {
dispatch({
dispatch<StatusesAction>({
type: STATUS_TRANSLATE_FAIL,
statusId,
error,
@ -354,11 +354,18 @@ type StatusesAction =
| { type: typeof STATUS_CREATE_REQUEST; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
| { type: typeof STATUS_CREATE_SUCCESS; status: BaseStatus | ScheduledStatus; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
| { type: typeof STATUS_CREATE_FAIL; error: unknown; params: CreateStatusParams; idempotencyKey: string; editing: boolean }
// editStatus,
// fetchStatus,
// deleteStatus,
// updateStatus,
// fetchContext,
| { type: typeof STATUS_FETCH_SOURCE_REQUEST }
| { type: typeof STATUS_FETCH_SOURCE_SUCCESS }
| { type: typeof STATUS_FETCH_SOURCE_FAIL; error: unknown }
| { type: typeof STATUS_FETCH_REQUEST; statusId: string }
| { type: typeof STATUS_FETCH_SUCCESS; status: BaseStatus }
| { type: typeof STATUS_FETCH_FAIL; statusId: string; error: unknown; skipAlert: true }
| { type: typeof STATUS_DELETE_REQUEST; params: Pick<Status, 'in_reply_to_id' | 'quote_id'> }
| { type: typeof STATUS_DELETE_SUCCESS; statusId: string }
| { type: typeof STATUS_DELETE_FAIL; params: Pick<Status, 'in_reply_to_id' | 'quote_id'>; error: unknown }
| { type: typeof CONTEXT_FETCH_REQUEST; statusId: string }
| { type: typeof CONTEXT_FETCH_SUCCESS; statusId: string; ancestors: Array<BaseStatus>; descendants: Array<BaseStatus> }
| { type: typeof CONTEXT_FETCH_FAIL; statusId: string; error: unknown; skipAlert: true }
| { type: typeof STATUS_MUTE_REQUEST; statusId: string }
| { type: typeof STATUS_MUTE_SUCCESS; statusId: string }
| { type: typeof STATUS_MUTE_FAIL; statusId: string; error: unknown }

View file

@ -114,16 +114,16 @@ interface TimelineDeleteAction {
type: typeof TIMELINE_DELETE;
statusId: string;
accountId: string;
references: Record<string, readonly [statusId: string, accountId: string]>;
references: Array<[string, string]>;
reblogOf: string | null;
}
const deleteFromTimelines = (statusId: string) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const accountId = getState().statuses[statusId]?.account?.id!;
const references = Object.fromEntries(Object.entries(getState().statuses)
const references: Array<[string, string]> = Object.entries(getState().statuses)
.filter(([key, status]) => [key, status.reblog_id === statusId])
.map(([key, status]) => [key, [status.id, status.account_id] as const]));
.map(([key, status]) => [key, status.account_id]);
const reblogOf = getState().statuses[statusId]?.reblog_id || null;
dispatch<TimelineDeleteAction>({

View file

@ -12,7 +12,6 @@ import {
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
import type { Status } from 'pl-api';
import type { AnyAction } from 'redux';
interface State {
inReplyTos: Record<string, string>;
@ -36,7 +35,7 @@ const importStatus = (state: State, status: Pick<Status, 'id' | 'in_reply_to_id'
state.inReplyTos[id] = inReplyToId;
if (idempotencyKey) {
deletePendingStatus(state, status, idempotencyKey);
deletePendingStatus(state, status.in_reply_to_id, idempotencyKey);
}
};
@ -150,16 +149,14 @@ const filterContexts = (
};
/** Add a fake status ID for a pending status. */
const importPendingStatus = (state: State, params: Pick<Status, 'id' | 'in_reply_to_id'>, idempotencyKey: string) => {
const importPendingStatus = (state: State, inReplyToId: string | null | undefined, idempotencyKey: string) => {
const id = `末pending-${idempotencyKey}`;
const { in_reply_to_id } = params;
return importStatus(state, { id, in_reply_to_id });
return importStatus(state, { id, in_reply_to_id: inReplyToId || null });
};
/** Delete a pending status from the reducer. */
const deletePendingStatus = (state: State, params: Pick<Status, 'id' | 'in_reply_to_id'>, idempotencyKey: string) => {
const deletePendingStatus = (state: State, inReplyToId: string | null | undefined, idempotencyKey: string) => {
const id = `末pending-${idempotencyKey}`;
const { in_reply_to_id: inReplyToId } = params;
delete state.inReplyTos[id];
@ -171,7 +168,7 @@ const deletePendingStatus = (state: State, params: Pick<Status, 'id' | 'in_reply
};
/** Contexts reducer. Used for building a nested tree structure for threads. */
const replies = (state = initialState, action: AccountsAction | AnyAction | ImporterAction | StatusesAction | TimelineAction): State => {
const replies = (state = initialState, action: AccountsAction | ImporterAction | StatusesAction | TimelineAction): State => {
switch (action.type) {
case ACCOUNT_BLOCK_SUCCESS:
case ACCOUNT_MUTE_SUCCESS:
@ -181,9 +178,9 @@ const replies = (state = initialState, action: AccountsAction | AnyAction | Impo
case TIMELINE_DELETE:
return create(state, (draft) => deleteStatuses(draft, [action.statusId]));
case STATUS_CREATE_REQUEST:
return create(state, (draft) => importPendingStatus(draft, action.params, action.idempotencyKey));
return create(state, (draft) => importPendingStatus(draft, action.params.in_reply_to_id, action.idempotencyKey));
case STATUS_CREATE_SUCCESS:
return create(state, (draft) => deletePendingStatus(draft, action.status, action.idempotencyKey));
return create(state, (draft) => deletePendingStatus(draft, 'in_reply_to_id' in action.status ? action.status.in_reply_to_id : null, action.idempotencyKey));
case STATUS_IMPORT:
return create(state, (draft) => importStatus(draft, action.status, action.idempotencyKey));
case STATUSES_IMPORT:

View file

@ -5,10 +5,10 @@ import {
STATUS_CREATE_FAIL,
STATUS_CREATE_REQUEST,
STATUS_CREATE_SUCCESS,
type StatusesAction,
} from 'pl-fe/actions/statuses';
import type { StatusVisibility } from 'pl-fe/normalizers/status';
import type { AnyAction } from 'redux';
interface PendingStatus {
content_type: string;
@ -49,7 +49,7 @@ const deleteStatus = (state: State, idempotencyKey: string) => {
delete state[idempotencyKey];
};
const pending_statuses = (state = initialState, action: AnyAction): State => {
const pending_statuses = (state = initialState, action: StatusesAction): State => {
switch (action.type) {
case STATUS_CREATE_REQUEST:
if (action.editing) return state;

View file

@ -7,10 +7,9 @@ import {
SCHEDULED_STATUS_CANCEL_SUCCESS,
type ScheduledStatusesAction,
} from 'pl-fe/actions/scheduled-statuses';
import { STATUS_CREATE_SUCCESS } from 'pl-fe/actions/statuses';
import { STATUS_CREATE_SUCCESS, type StatusesAction } from 'pl-fe/actions/statuses';
import type { Status, ScheduledStatus } from 'pl-api';
import type { AnyAction } from 'redux';
type State = Record<string, ScheduledStatus>;
@ -29,7 +28,7 @@ const deleteStatus = (state: State, statusId: string) => {
delete state[statusId];
};
const scheduled_statuses = (state: State = initialState, action: AnyAction | ImporterAction | ScheduledStatusesAction) => {
const scheduled_statuses = (state: State = initialState, action: ImporterAction | ScheduledStatusesAction | StatusesAction) => {
switch (action.type) {
case STATUS_IMPORT:
case STATUS_CREATE_SUCCESS:

View file

@ -52,8 +52,7 @@ import {
} from '../actions/statuses';
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
import type { Status as BaseStatus, Translation } from 'pl-api';
import type { AnyAction } from 'redux';
import type { Status as BaseStatus, CreateStatusParams, Translation } from 'pl-api';
type State = Record<string, MinifiedStatus>;
@ -91,7 +90,7 @@ const importStatuses = (state: State, statuses: Array<BaseStatus>) =>{
statuses.forEach(status => importStatus(state, status));
};
const deleteStatus = (state: State, statusId: string, references: Array<string>) => {
const deleteStatus = (state: State, statusId: string, references: Array<[string, string]>) => {
references.forEach(ref => {
deleteStatus(state, ref[0], []);
});
@ -99,28 +98,28 @@ const deleteStatus = (state: State, statusId: string, references: Array<string>)
delete state[statusId];
};
const incrementReplyCount = (state: State, { in_reply_to_id, quote }: BaseStatus) => {
const incrementReplyCount = (state: State, { in_reply_to_id, quote_id }: Pick<BaseStatus | CreateStatusParams, 'in_reply_to_id' | 'quote_id'>) => {
if (in_reply_to_id && state[in_reply_to_id]) {
const parent = state[in_reply_to_id];
parent.replies_count = (typeof parent.replies_count === 'number' ? parent.replies_count : 0) + 1;
}
if (quote?.id && state[quote.id]) {
const parent = state[quote.id];
if (quote_id && state[quote_id]) {
const parent = state[quote_id];
parent.quotes_count = (typeof parent.quotes_count === 'number' ? parent.quotes_count : 0) + 1;
}
return state;
};
const decrementReplyCount = (state: State, { in_reply_to_id, quote }: BaseStatus) => {
const decrementReplyCount = (state: State, { in_reply_to_id, quote_id }: Pick<BaseStatus | CreateStatusParams, 'in_reply_to_id' | 'quote_id'>) => {
if (in_reply_to_id && state[in_reply_to_id]) {
const parent = state[in_reply_to_id];
parent.replies_count = Math.max(0, parent.replies_count - 1);
}
if (quote?.id) {
const parent = state[quote.id];
if (quote_id) {
const parent = state[quote_id];
parent.quotes_count = Math.max(0, parent.quotes_count - 1);
}
@ -177,7 +176,7 @@ const deleteTranslation = (state: State, statusId: string) => {
const initialState: State = {};
const statuses = (state = initialState, action: AnyAction | EmojiReactsAction | EventsAction | ImporterAction | InteractionsAction | StatusesAction | TimelineAction): State => {
const statuses = (state = initialState, action: EmojiReactsAction | EventsAction | ImporterAction | InteractionsAction | StatusesAction | TimelineAction): State => {
switch (action.type) {
case STATUS_IMPORT:
return create(state, (draft) => importStatus(draft, action.status));
@ -317,7 +316,7 @@ const statuses = (state = initialState, action: AnyAction | EmojiReactsAction |
}
});
case STATUS_TRANSLATE_SUCCESS:
return create(state, (draft) => importTranslation(draft, action.statusId, action.translation));
return action.statusId !== null ? create(state, (draft) => importTranslation(draft, action.statusId!, action.translation)) : state;
case STATUS_TRANSLATE_FAIL:
return create(state, (draft) => {
const status = draft[action.statusId];

View file

@ -19,10 +19,9 @@ import {
type TimelineAction,
} from '../actions/timelines';
import type { PaginatedResponse, Status as BaseStatus, Relationship } from 'pl-api';
import type { PaginatedResponse, Status as BaseStatus, Relationship, CreateStatusParams } from 'pl-api';
import type { ImportPosition } from 'pl-fe/entity-store/types';
import type { Status } from 'pl-fe/normalizers/status';
import type { AnyAction } from 'redux';
const TRUNCATE_LIMIT = 40;
const TRUNCATE_SIZE = 20;
@ -159,14 +158,14 @@ const updateTimelineQueue = (state: State, timelineId: string, statusId: string)
});
};
const shouldDelete = (timelineId: string, excludeAccount?: string) => {
const shouldDelete = (timelineId: string, excludeAccount: string | null) => {
if (!excludeAccount) return true;
if (timelineId === `account:${excludeAccount}`) return false;
if (timelineId.startsWith(`account:${excludeAccount}:`)) return false;
return true;
};
const deleteStatus = (state: State, statusId: string, references: Array<string>, excludeAccount?: string) => {
const deleteStatus = (state: State, statusId: string, references: Array<[string]> | Array<[string, string]>, excludeAccount: string | null) => {
for (const timelineId in state) {
if (shouldDelete(timelineId, excludeAccount)) {
state[timelineId].items = state[timelineId].items.filter(id => id !== statusId);
@ -176,7 +175,7 @@ const deleteStatus = (state: State, statusId: string, references: Array<string>,
// Remove reblogs of deleted status
references.forEach(ref => {
deleteStatus(state, ref, [], excludeAccount);
deleteStatus(state, ref[0], [], excludeAccount);
});
};
@ -195,10 +194,10 @@ const isReblogOf = (reblog: Pick<Status, 'reblog_id'>, status: Pick<Status, 'id'
const buildReferencesTo = (
statuses: Record<string, Pick<Status, 'id' | 'account' | 'reblog_id'>>,
status: Pick<Status, 'id'>,
) => (
): Array<[string]> => (
Object.values(statuses)
.filter(reblog => isReblogOf(reblog, status))
.map(status => status.id)
.map(status => [status.id])
);
// const filterTimeline = (state: State, timelineId: string, relationship: APIEntity, statuses: ImmutableList<ImmutableMap<string, any>>) =>
@ -238,10 +237,10 @@ const timelineDequeue = (state: State, timelineId: string) => {
// timeline.set('items', addStatusId(items, null));
// }));
const getTimelinesForStatus = (status: Pick<BaseStatus, 'visibility' | 'group'>) => {
const getTimelinesForStatus = (status: Pick<BaseStatus, 'visibility' | 'group'> | Pick<CreateStatusParams, 'visibility'>) => {
switch (status.visibility) {
case 'group':
return [`group:${status.group?.id}`];
return [`group:${'group' in status && status.group?.id}`];
case 'direct':
return ['direct'];
case 'public':
@ -260,7 +259,7 @@ const replaceId = (ids: Array<string>, oldId: string, newId: string) => {
}
};
const importPendingStatus = (state: State, params: BaseStatus, idempotencyKey: string) => {
const importPendingStatus = (state: State, params: CreateStatusParams, idempotencyKey: string) => {
const statusId = `末pending-${idempotencyKey}`;
const timelineIds = getTimelinesForStatus(params);
@ -295,14 +294,14 @@ const handleExpandFail = (state: State, timelineId: string) => {
setFailed(state, timelineId, true);
};
const timelines = (state: State = initialState, action: AccountsAction | AnyAction | InteractionsAction | StatusesAction | TimelineAction): State => {
const timelines = (state: State = initialState, action: AccountsAction | InteractionsAction | StatusesAction | TimelineAction): State => {
switch (action.type) {
case STATUS_CREATE_REQUEST:
if (action.params.scheduled_at) return state;
return create(state, (draft) => importPendingStatus(draft, action.params, action.idempotencyKey));
case STATUS_CREATE_SUCCESS:
if (action.status.scheduled_at || action.editing) return state;
return create(state, (draft) => importStatus(draft, action.status, action.idempotencyKey));
if ('params' in action.status || action.editing) return state;
return create(state, (draft) => importStatus(draft, action.status as BaseStatus, action.idempotencyKey));
case TIMELINE_EXPAND_REQUEST:
return create(state, (draft) => setLoading(draft, action.timeline, true));
case TIMELINE_EXPAND_FAIL: