[WiP] Front-end changes for the new API shape

This commit is contained in:
Claire 2024-07-30 16:59:05 +02:00
parent 4be16a3e9e
commit e7933543ef
14 changed files with 254 additions and 52 deletions

View File

@ -1,6 +1,9 @@
import { createAction } from '@reduxjs/toolkit'; import { createAction } from '@reduxjs/toolkit';
import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; import type {
ApiAccountJSON,
ShallowApiAccountJSON,
} from 'mastodon/api_types/accounts';
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships';
export const revealAccount = createAction<{ export const revealAccount = createAction<{
@ -11,6 +14,10 @@ export const importAccounts = createAction<{ accounts: ApiAccountJSON[] }>(
'accounts/importAccounts', 'accounts/importAccounts',
); );
export const importShallowAccounts = createAction<{
accounts: ShallowApiAccountJSON[];
}>('accounts/importShallowAccounts');
function actionWithSkipLoadingTrue<Args extends object>(args: Args) { function actionWithSkipLoadingTrue<Args extends object>(args: Args) {
return { return {
payload: { payload: {

View File

@ -1,6 +1,6 @@
import { importAccounts } from '../accounts_typed'; import { importAccounts } from '../accounts_typed';
import { normalizeStatus, normalizePoll } from './normalizer'; import { normalizeStatus, normalizeShallowStatus, normalizePoll } from './normalizer';
export const STATUS_IMPORT = 'STATUS_IMPORT'; export const STATUS_IMPORT = 'STATUS_IMPORT';
export const STATUSES_IMPORT = 'STATUSES_IMPORT'; export const STATUSES_IMPORT = 'STATUSES_IMPORT';
@ -49,6 +49,10 @@ export function importFetchedAccounts(accounts) {
return importAccounts({ accounts: normalAccounts }); return importAccounts({ accounts: normalAccounts });
} }
export function importShallowFetchedAccounts(accounts) {
return importAccounts({ accounts });
}
export function importFetchedStatus(status) { export function importFetchedStatus(status) {
return importFetchedStatuses([status]); return importFetchedStatuses([status]);
} }
@ -90,6 +94,32 @@ export function importFetchedStatuses(statuses) {
}; };
} }
export function importShallowStatuses(statuses) {
return (dispatch, getState) => {
const normalStatuses = [];
const polls = [];
const filters = [];
function processStatus(status) {
pushUnique(normalStatuses, normalizeShallowStatus(status, getState().getIn(['statuses', status.id])));
if (status.filtered) {
status.filtered.forEach(result => pushUnique(filters, result.filter));
}
if (status.poll?.id) {
pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id])));
}
}
statuses.forEach(processStatus);
dispatch(importPolls(polls));
dispatch(importStatuses(normalStatuses));
dispatch(importFilters(filters));
};
}
export function importFetchedPoll(poll) { export function importFetchedPoll(poll) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(importPolls([normalizePoll(poll, getState().getIn(['polls', poll.id]))])); dispatch(importPolls([normalizePoll(poll, getState().getIn(['polls', poll.id]))]));

View File

@ -25,13 +25,34 @@ export function normalizeFilterResult(result) {
} }
export function normalizeStatus(status, normalOldStatus) { export function normalizeStatus(status, normalOldStatus) {
const normalStatus = { ...status }; const { account, reblog, ...normalStatus } = status;
normalStatus.account = status.account.id;
if (status.reblog && status.reblog.id) { normalStatus.account_id = account.id;
normalStatus.reblog = status.reblog.id;
if (reblog && reblog.id) {
normalStatus.reblog_id = reblog.id;
} }
if (status.card) {
normalStatus.card = {
...status.card,
authors: status.card.authors.map(author => ({
...author,
account_id: author.account?.id,
account: undefined,
})),
};
}
return normalizeShallowStatus(normalStatus, normalOldStatus);
}
export function normalizeShallowStatus(status, normalOldStatus) {
const { account_id, reblog_id, ...normalStatus } = status;
normalStatus.account = account_id;
normalStatus.reblog = reblog_id;
if (status.poll && status.poll.id) { if (status.poll && status.poll.id) {
normalStatus.poll = status.poll.id; normalStatus.poll = status.poll.id;
} }
@ -41,8 +62,8 @@ export function normalizeStatus(status, normalOldStatus) {
...status.card, ...status.card,
authors: status.card.authors.map(author => ({ authors: status.card.authors.map(author => ({
...author, ...author,
accountId: author.account?.id, accountId: author.account_id,
account: undefined, account_id: undefined,
})), })),
}; };
} }

View File

@ -5,6 +5,7 @@ import {
apiFetchNotifications, apiFetchNotifications,
} from 'mastodon/api/notifications'; } from 'mastodon/api/notifications';
import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
import type { ApiGenericJSON } from 'mastodon/api_types/generic';
import type { import type {
ApiNotificationGroupJSON, ApiNotificationGroupJSON,
ApiNotificationJSON, ApiNotificationJSON,
@ -22,7 +23,12 @@ import {
createDataLoadingThunk, createDataLoadingThunk,
} from 'mastodon/store/typed_functions'; } from 'mastodon/store/typed_functions';
import { importFetchedAccounts, importFetchedStatuses } from './importer'; import { importShallowAccounts } from './accounts_typed';
import {
importFetchedAccounts,
importFetchedStatuses,
importShallowStatuses,
} from './importer';
import { NOTIFICATIONS_FILTER_SET } from './notifications'; import { NOTIFICATIONS_FILTER_SET } from './notifications';
import { saveSettings } from './settings'; import { saveSettings } from './settings';
@ -32,16 +38,12 @@ function excludeAllTypesExcept(filter: string) {
function dispatchAssociatedRecords( function dispatchAssociatedRecords(
dispatch: AppDispatch, dispatch: AppDispatch,
notifications: ApiNotificationGroupJSON[] | ApiNotificationJSON[], notifications: ApiNotificationJSON[],
) { ) {
const fetchedAccounts: ApiAccountJSON[] = []; const fetchedAccounts: ApiAccountJSON[] = [];
const fetchedStatuses: ApiStatusJSON[] = []; const fetchedStatuses: ApiStatusJSON[] = [];
notifications.forEach((notification) => { notifications.forEach((notification) => {
if ('sample_accounts' in notification) {
fetchedAccounts.push(...notification.sample_accounts);
}
if (notification.type === 'admin.report') { if (notification.type === 'admin.report') {
fetchedAccounts.push(notification.report.target_account); fetchedAccounts.push(notification.report.target_account);
} }
@ -62,6 +64,17 @@ function dispatchAssociatedRecords(
dispatch(importFetchedStatuses(fetchedStatuses)); dispatch(importFetchedStatuses(fetchedStatuses));
} }
function dispatchGenericAssociatedRecords(
dispatch: AppDispatch,
apiResult: ApiGenericJSON,
) {
if (apiResult.accounts.length > 0)
dispatch(importShallowAccounts({ accounts: apiResult.accounts }));
if (apiResult.statuses.length > 0)
dispatch(importShallowStatuses(apiResult.statuses));
}
export const fetchNotifications = createDataLoadingThunk( export const fetchNotifications = createDataLoadingThunk(
'notificationGroups/fetch', 'notificationGroups/fetch',
async (_params, { getState }) => { async (_params, { getState }) => {
@ -75,15 +88,18 @@ export const fetchNotifications = createDataLoadingThunk(
: excludeAllTypesExcept(activeFilter), : excludeAllTypesExcept(activeFilter),
}); });
}, },
({ notifications }, { dispatch }) => { ({ apiResult }, { dispatch }) => {
dispatchAssociatedRecords(dispatch, notifications); dispatchGenericAssociatedRecords(dispatch, apiResult);
const payload: (ApiNotificationGroupJSON | NotificationGap)[] = const payload: (ApiNotificationGroupJSON | NotificationGap)[] =
notifications; apiResult.notification_groups;
// TODO: might be worth not using gaps for that… // TODO: might be worth not using gaps for that…
// if (nextLink) payload.push({ type: 'gap', loadUrl: nextLink.uri }); // if (nextLink) payload.push({ type: 'gap', loadUrl: nextLink.uri });
if (notifications.length > 1) if (apiResult.notification_groups.length > 1)
payload.push({ type: 'gap', maxId: notifications.at(-1)?.page_min_id }); payload.push({
type: 'gap',
maxId: apiResult.notification_groups.at(-1)?.page_min_id,
});
return payload; return payload;
// dispatch(submitMarkers()); // dispatch(submitMarkers());
@ -95,10 +111,10 @@ export const fetchNotificationsGap = createDataLoadingThunk(
async (params: { gap: NotificationGap }) => async (params: { gap: NotificationGap }) =>
apiFetchNotifications({ max_id: params.gap.maxId }), apiFetchNotifications({ max_id: params.gap.maxId }),
({ notifications }, { dispatch }) => { ({ apiResult }, { dispatch }) => {
dispatchAssociatedRecords(dispatch, notifications); dispatchGenericAssociatedRecords(dispatch, apiResult);
return { notifications }; return { apiResult };
}, },
); );

View File

@ -1,17 +1,17 @@
import api, { apiRequest, getLinks } from 'mastodon/api'; import api, { apiRequest, getLinks } from 'mastodon/api';
import type { ApiNotificationGroupJSON } from 'mastodon/api_types/notifications'; import type { ApiGenericJSON } from 'mastodon/api_types/generic';
export const apiFetchNotifications = async (params?: { export const apiFetchNotifications = async (params?: {
exclude_types?: string[]; exclude_types?: string[];
max_id?: string; max_id?: string;
}) => { }) => {
const response = await api().request<ApiNotificationGroupJSON[]>({ const response = await api().request<ApiGenericJSON>({
method: 'GET', method: 'GET',
url: '/api/v2_alpha/notifications', url: '/api/v2_alpha/notifications',
params, params,
}); });
return { notifications: response.data, links: getLinks(response) }; return { apiResult: response.data, links: getLinks(response) };
}; };
export const apiClearNotifications = () => export const apiClearNotifications = () =>

View File

@ -12,8 +12,8 @@ export interface ApiAccountRoleJSON {
name: string; name: string;
} }
// See app/serializers/rest/account_serializer.rb // See app/serializers/rest/base_account_serializer.rb
export interface ApiAccountJSON { export interface BaseApiAccountJSON {
acct: string; acct: string;
avatar: string; avatar: string;
avatar_static: string; avatar_static: string;
@ -34,14 +34,21 @@ export interface ApiAccountJSON {
locked: boolean; locked: boolean;
noindex?: boolean; noindex?: boolean;
note: string; note: string;
roles?: ApiAccountJSON[]; roles?: ApiAccountRoleJSON[];
statuses_count: number; statuses_count: number;
uri: string; uri: string;
url: string; url: string;
username: string; username: string;
moved?: ApiAccountJSON;
suspended?: boolean; suspended?: boolean;
limited?: boolean; limited?: boolean;
memorial?: boolean; memorial?: boolean;
hide_collections: boolean; hide_collections: boolean;
} }
export interface ApiAccountJSON extends BaseApiAccountJSON {
moved?: ApiAccountJSON;
}
export interface ShallowApiAccountJSON extends BaseApiAccountJSON {
moved_to_account_id?: string;
}

View File

@ -0,0 +1,9 @@
import type { ShallowApiAccountJSON } from 'mastodon/api_types/accounts';
import type { ApiNotificationGroupJSON } from 'mastodon/api_types/notifications';
import type { ShallowApiStatusJSON } from 'mastodon/api_types/statuses';
export interface ApiGenericJSON {
statuses: ShallowApiStatusJSON[];
accounts: ShallowApiAccountJSON[];
notification_groups: ApiNotificationGroupJSON[];
}

View File

@ -3,7 +3,7 @@
import type { AccountWarningAction } from 'mastodon/models/notification_group'; import type { AccountWarningAction } from 'mastodon/models/notification_group';
import type { ApiAccountJSON } from './accounts'; import type { ApiAccountJSON } from './accounts';
import type { ApiReportJSON } from './reports'; import type { ApiReportJSON, ShallowApiReportJSON } from './reports';
import type { ApiStatusJSON } from './statuses'; import type { ApiStatusJSON } from './statuses';
// See app/model/notification.rb // See app/model/notification.rb
@ -51,7 +51,7 @@ export interface BaseNotificationGroupJSON {
group_key: string; group_key: string;
notifications_count: number; notifications_count: number;
type: NotificationType; type: NotificationType;
sample_accounts: ApiAccountJSON[]; sample_account_ids: string[];
latest_page_notification_at: string; // FIXME: This will only be present if the notification group is returned in a paginated list, not requested directly latest_page_notification_at: string; // FIXME: This will only be present if the notification group is returned in a paginated list, not requested directly
most_recent_notification_id: string; most_recent_notification_id: string;
page_min_id?: string; page_min_id?: string;
@ -60,7 +60,7 @@ export interface BaseNotificationGroupJSON {
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
type: NotificationWithStatusType; type: NotificationWithStatusType;
status: ApiStatusJSON; status_id: string;
} }
interface NotificationWithStatusJSON extends BaseNotificationJSON { interface NotificationWithStatusJSON extends BaseNotificationJSON {
@ -70,7 +70,7 @@ interface NotificationWithStatusJSON extends BaseNotificationJSON {
interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON { interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON {
type: 'admin.report'; type: 'admin.report';
report: ApiReportJSON; report: ShallowApiReportJSON;
} }
interface ReportNotificationJSON extends BaseNotificationJSON { interface ReportNotificationJSON extends BaseNotificationJSON {
@ -87,20 +87,28 @@ interface SimpleNotificationJSON extends BaseNotificationJSON {
type: SimpleNotificationTypes; type: SimpleNotificationTypes;
} }
export interface ApiAccountWarningJSON { export interface BaseApiAccountWarningJSON {
id: string; id: string;
action: AccountWarningAction; action: AccountWarningAction;
text: string; text: string;
status_ids: string[]; status_ids: string[];
created_at: string; created_at: string;
target_account: ApiAccountJSON;
appeal: unknown; appeal: unknown;
} }
export interface ApiAccountWarningJSON extends BaseApiAccountWarningJSON {
target_account: ApiAccountJSON;
}
export interface ShallowApiAccountWarningJSON
extends BaseApiAccountWarningJSON {
target_account_id: string;
}
interface ModerationWarningNotificationGroupJSON interface ModerationWarningNotificationGroupJSON
extends BaseNotificationGroupJSON { extends BaseNotificationGroupJSON {
type: 'moderation_warning'; type: 'moderation_warning';
moderation_warning: ApiAccountWarningJSON; moderation_warning: ShallowApiAccountWarningJSON;
} }
interface ModerationWarningNotificationJSON extends BaseNotificationJSON { interface ModerationWarningNotificationJSON extends BaseNotificationJSON {

View File

@ -2,7 +2,7 @@ import type { ApiAccountJSON } from './accounts';
export type ReportCategory = 'other' | 'spam' | 'legal' | 'violation'; export type ReportCategory = 'other' | 'spam' | 'legal' | 'violation';
export interface ApiReportJSON { export interface BaseApiReportJSON {
id: string; id: string;
action_taken: unknown; action_taken: unknown;
action_taken_at: unknown; action_taken_at: unknown;
@ -12,5 +12,12 @@ export interface ApiReportJSON {
created_at: string; created_at: string;
status_ids: string[]; status_ids: string[];
rule_ids: string[]; rule_ids: string[];
}
export interface ApiReportJSON extends BaseApiReportJSON {
target_account: ApiAccountJSON; target_account: ApiAccountJSON;
} }
export interface ShallowApiReportJSON extends BaseApiReportJSON {
target_account_id: string;
}

View File

@ -1,4 +1,4 @@
// See app/serializers/rest/status_serializer.rb // See app/serializers/rest/base_status_serializer.rb
import type { ApiAccountJSON } from './accounts'; import type { ApiAccountJSON } from './accounts';
import type { ApiCustomEmojiJSON } from './custom_emoji'; import type { ApiCustomEmojiJSON } from './custom_emoji';
@ -58,7 +58,7 @@ export interface ApiPreviewCardJSON {
authors: ApiPreviewCardAuthorJSON[]; authors: ApiPreviewCardAuthorJSON[];
} }
export interface ApiStatusJSON { export interface BaseApiStatusJSON {
id: string; id: string;
created_at: string; created_at: string;
in_reply_to_id?: string; in_reply_to_id?: string;
@ -85,9 +85,7 @@ export interface ApiStatusJSON {
content?: string; content?: string;
text?: string; text?: string;
reblog?: ApiStatusJSON;
application?: ApiStatusApplicationJSON; application?: ApiStatusApplicationJSON;
account: ApiAccountJSON;
media_attachments: ApiMediaAttachmentJSON[]; media_attachments: ApiMediaAttachmentJSON[];
mentions: ApiMentionJSON[]; mentions: ApiMentionJSON[];
@ -97,3 +95,13 @@ export interface ApiStatusJSON {
card?: ApiPreviewCardJSON; card?: ApiPreviewCardJSON;
poll?: ApiPollJSON; poll?: ApiPollJSON;
} }
export interface ApiStatusJSON extends BaseApiStatusJSON {
account: ApiAccountJSON;
reblog?: ApiStatusJSON;
}
export interface ShallowApiStatusJSON extends BaseApiStatusJSON {
account_id: string;
reblog_id?: string;
}

View File

@ -7,6 +7,7 @@ import type {
ApiAccountFieldJSON, ApiAccountFieldJSON,
ApiAccountRoleJSON, ApiAccountRoleJSON,
ApiAccountJSON, ApiAccountJSON,
ShallowApiAccountJSON,
} from 'mastodon/api_types/accounts'; } from 'mastodon/api_types/accounts';
import type { ApiCustomEmojiJSON } from 'mastodon/api_types/custom_emoji'; import type { ApiCustomEmojiJSON } from 'mastodon/api_types/custom_emoji';
import emojify from 'mastodon/features/emoji/emoji'; import emojify from 'mastodon/features/emoji/emoji';
@ -149,3 +150,32 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
note_plain: unescapeHTML(accountJSON.note), note_plain: unescapeHTML(accountJSON.note),
}); });
} }
export function createAccountFromServerShallowJSON(
serverJSON: ShallowApiAccountJSON,
) {
const { moved_to_account_id, ...accountJSON } = serverJSON;
const emojiMap = makeEmojiMap(accountJSON.emojis);
const displayName =
accountJSON.display_name.trim().length === 0
? accountJSON.username
: accountJSON.display_name;
return AccountFactory({
...accountJSON,
moved: moved_to_account_id,
fields: List(
serverJSON.fields.map((field) => createAccountField(field, emojiMap)),
),
emojis: List(serverJSON.emojis.map((emoji) => CustomEmojiFactory(emoji))),
roles: List(serverJSON.roles?.map((role) => AccountRoleFactory(role))),
display_name_html: emojify(
escapeTextContentForBrowser(displayName),
emojiMap,
),
note_emojified: emojify(accountJSON.note, emojiMap),
note_plain: unescapeHTML(accountJSON.note),
});
}

View File

@ -1,5 +1,6 @@
import type { import type {
ApiAccountRelationshipSeveranceEventJSON, ApiAccountRelationshipSeveranceEventJSON,
ShallowApiAccountWarningJSON,
ApiAccountWarningJSON, ApiAccountWarningJSON,
BaseNotificationGroupJSON, BaseNotificationGroupJSON,
ApiNotificationGroupJSON, ApiNotificationGroupJSON,
@ -7,14 +8,17 @@ import type {
NotificationType, NotificationType,
NotificationWithStatusType, NotificationWithStatusType,
} from 'mastodon/api_types/notifications'; } from 'mastodon/api_types/notifications';
import type { ApiReportJSON } from 'mastodon/api_types/reports'; import type {
ApiReportJSON,
ShallowApiReportJSON,
} from 'mastodon/api_types/reports';
// Maximum number of avatars displayed in a notification group // Maximum number of avatars displayed in a notification group
// This corresponds to the max lenght of `group.sampleAccountIds` // This corresponds to the max lenght of `group.sampleAccountIds`
export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8; export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8;
interface BaseNotificationGroup interface BaseNotificationGroup
extends Omit<BaseNotificationGroupJSON, 'sample_accounts'> { extends Omit<BaseNotificationGroupJSON, 'sample_account_ids'> {
sampleAccountIds: string[]; sampleAccountIds: string[];
} }
@ -96,6 +100,14 @@ function createReportFromJSON(reportJSON: ApiReportJSON): Report {
}; };
} }
function createReportFromShallowJSON(reportJSON: ShallowApiReportJSON): Report {
const { target_account_id, ...report } = reportJSON;
return {
targetAccountId: target_account_id,
...report,
};
}
function createAccountWarningFromJSON( function createAccountWarningFromJSON(
warningJSON: ApiAccountWarningJSON, warningJSON: ApiAccountWarningJSON,
): AccountWarning { ): AccountWarning {
@ -106,6 +118,16 @@ function createAccountWarningFromJSON(
}; };
} }
function createAccountWarningFromShallowJSON(
warningJSON: ShallowApiAccountWarningJSON,
): AccountWarning {
const { target_account_id, ...warning } = warningJSON;
return {
targetAccountId: target_account_id,
...warning,
};
}
function createAccountRelationshipSeveranceEventFromJSON( function createAccountRelationshipSeveranceEventFromJSON(
eventJson: ApiAccountRelationshipSeveranceEventJSON, eventJson: ApiAccountRelationshipSeveranceEventJSON,
): AccountRelationshipSeveranceEvent { ): AccountRelationshipSeveranceEvent {
@ -115,8 +137,7 @@ function createAccountRelationshipSeveranceEventFromJSON(
export function createNotificationGroupFromJSON( export function createNotificationGroupFromJSON(
groupJson: ApiNotificationGroupJSON, groupJson: ApiNotificationGroupJSON,
): NotificationGroup { ): NotificationGroup {
const { sample_accounts, ...group } = groupJson; const { sample_account_ids: sampleAccountIds, ...group } = groupJson;
const sampleAccountIds = sample_accounts.map((account) => account.id);
switch (group.type) { switch (group.type) {
case 'favourite': case 'favourite':
@ -125,9 +146,9 @@ export function createNotificationGroupFromJSON(
case 'mention': case 'mention':
case 'poll': case 'poll':
case 'update': { case 'update': {
const { status, ...groupWithoutStatus } = group; const { status_id, ...groupWithoutStatus } = group;
return { return {
statusId: status.id, statusId: status_id,
sampleAccountIds, sampleAccountIds,
...groupWithoutStatus, ...groupWithoutStatus,
}; };
@ -135,7 +156,7 @@ export function createNotificationGroupFromJSON(
case 'admin.report': { case 'admin.report': {
const { report, ...groupWithoutTargetAccount } = group; const { report, ...groupWithoutTargetAccount } = group;
return { return {
report: createReportFromJSON(report), report: createReportFromShallowJSON(report),
sampleAccountIds, sampleAccountIds,
...groupWithoutTargetAccount, ...groupWithoutTargetAccount,
}; };
@ -151,7 +172,8 @@ export function createNotificationGroupFromJSON(
const { moderation_warning, ...groupWithoutModerationWarning } = group; const { moderation_warning, ...groupWithoutModerationWarning } = group;
return { return {
...groupWithoutModerationWarning, ...groupWithoutModerationWarning,
moderationWarning: createAccountWarningFromJSON(moderation_warning), moderationWarning:
createAccountWarningFromShallowJSON(moderation_warning),
sampleAccountIds, sampleAccountIds,
}; };
} }

View File

@ -5,12 +5,19 @@ import {
followAccountSuccess, followAccountSuccess,
unfollowAccountSuccess, unfollowAccountSuccess,
importAccounts, importAccounts,
importShallowAccounts,
revealAccount, revealAccount,
} from 'mastodon/actions/accounts_typed'; } from 'mastodon/actions/accounts_typed';
import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; import type {
ApiAccountJSON,
ShallowApiAccountJSON,
} from 'mastodon/api_types/accounts';
import { me } from 'mastodon/initial_state'; import { me } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account'; import type { Account } from 'mastodon/models/account';
import { createAccountFromServerJSON } from 'mastodon/models/account'; import {
createAccountFromServerJSON,
createAccountFromServerShallowJSON,
} from 'mastodon/models/account';
const initialState = ImmutableMap<string, Account>(); const initialState = ImmutableMap<string, Account>();
@ -40,6 +47,32 @@ const normalizeAccounts = (
return state; return state;
}; };
const normalizeShallowAccount = (
state: typeof initialState,
account: ShallowApiAccountJSON,
) => {
return state.set(
account.id,
createAccountFromServerShallowJSON(account).set(
'hidden',
state.get(account.id)?.hidden === false
? false
: account.limited || false,
),
);
};
const normalizeShallowAccounts = (
state: typeof initialState,
accounts: ShallowApiAccountJSON[],
) => {
accounts.forEach((account) => {
state = normalizeShallowAccount(state, account);
});
return state;
};
function getCurrentUser() { function getCurrentUser() {
if (!me) if (!me)
throw new Error( throw new Error(
@ -57,6 +90,8 @@ export const accountsReducer: Reducer<typeof initialState> = (
return state.setIn([action.payload.id, 'hidden'], false); return state.setIn([action.payload.id, 'hidden'], false);
else if (importAccounts.match(action)) else if (importAccounts.match(action))
return normalizeAccounts(state, action.payload.accounts); return normalizeAccounts(state, action.payload.accounts);
else if (importShallowAccounts.match(action))
return normalizeShallowAccounts(state, action.payload.accounts);
else if (followAccountSuccess.match(action)) { else if (followAccountSuccess.match(action)) {
return state return state
.update(action.payload.relationship.id, (account) => .update(action.payload.relationship.id, (account) =>

View File

@ -296,7 +296,9 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
updateLastReadId(state); updateLastReadId(state);
}) })
.addCase(fetchNotificationsGap.fulfilled, (state, action) => { .addCase(fetchNotificationsGap.fulfilled, (state, action) => {
const { notifications } = action.payload; const {
apiResult: { notification_groups: notifications },
} = action.payload;
// find the gap in the existing notifications // find the gap in the existing notifications
const gapIndex = state.groups.findIndex( const gapIndex = state.groups.findIndex(