mirror of
https://github.com/mastodon/mastodon.git
synced 2024-11-26 15:31:52 +00:00
[WiP] Front-end changes for the new API shape
This commit is contained in:
parent
4be16a3e9e
commit
e7933543ef
|
@ -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: {
|
||||||
|
|
|
@ -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]))]));
|
||||||
|
|
|
@ -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,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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 = () =>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
9
app/javascript/mastodon/api_types/generic.ts
Normal file
9
app/javascript/mastodon/api_types/generic.ts
Normal 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[];
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user