kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Group notifications/reposts fetched from the same page
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>merge-requests/2909/head
rodzic
6b3b68a131
commit
cfa6cda48c
|
@ -151,47 +151,50 @@ const isBroken = (status: APIEntity) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const importFetchedStatuses = (statuses: APIEntity[]) =>
|
const importFetchedStatuses = (statuses: APIEntity[]) => (dispatch: AppDispatch) => {
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
const accounts: APIEntity[] = [];
|
||||||
const accounts: APIEntity[] = [];
|
const normalStatuses: APIEntity[] = [];
|
||||||
const normalStatuses: APIEntity[] = [];
|
const polls: APIEntity[] = [];
|
||||||
const polls: APIEntity[] = [];
|
|
||||||
|
|
||||||
function processStatus(status: APIEntity) {
|
function processStatus(status: APIEntity) {
|
||||||
// Skip broken statuses
|
// Skip broken statuses
|
||||||
if (isBroken(status)) return;
|
if (isBroken(status)) return;
|
||||||
|
|
||||||
normalStatuses.push(status);
|
normalStatuses.push(status);
|
||||||
accounts.push(status.account);
|
|
||||||
|
|
||||||
if (status.reblog?.id) {
|
accounts.push(status.account);
|
||||||
processStatus(status.reblog);
|
if (status.accounts) {
|
||||||
}
|
accounts.push(...status.accounts);
|
||||||
|
|
||||||
// Fedibird quotes
|
|
||||||
if (status.quote?.id) {
|
|
||||||
processStatus(status.quote);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.pleroma?.quote?.id) {
|
|
||||||
processStatus(status.pleroma.quote);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.poll?.id) {
|
|
||||||
polls.push(status.poll);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.group?.id) {
|
|
||||||
dispatch(importFetchedGroup(status.group));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses.forEach(processStatus);
|
if (status.reblog?.id) {
|
||||||
|
processStatus(status.reblog);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(importPolls(polls));
|
// Fedibird quotes
|
||||||
dispatch(importFetchedAccounts(accounts));
|
if (status.quote?.id) {
|
||||||
dispatch(importStatuses(normalStatuses));
|
processStatus(status.quote);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (status.pleroma?.quote?.id) {
|
||||||
|
processStatus(status.pleroma.quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.poll?.id) {
|
||||||
|
polls.push(status.poll);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.group?.id) {
|
||||||
|
dispatch(importFetchedGroup(status.group));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statuses.forEach(processStatus);
|
||||||
|
|
||||||
|
dispatch(importPolls(polls));
|
||||||
|
dispatch(importFetchedAccounts(accounts));
|
||||||
|
dispatch(importStatuses(normalStatuses));
|
||||||
|
};
|
||||||
|
|
||||||
const importFetchedPoll = (poll: APIEntity) =>
|
const importFetchedPoll = (poll: APIEntity) =>
|
||||||
(dispatch: AppDispatch) => {
|
(dispatch: AppDispatch) => {
|
||||||
|
|
|
@ -46,7 +46,7 @@ const NOTIFICATIONS_MARK_READ_FAIL = 'NOTIFICATIONS_MARK_READ_FAIL';
|
||||||
const MAX_QUEUED_NOTIFICATIONS = 40;
|
const MAX_QUEUED_NOTIFICATIONS = 40;
|
||||||
|
|
||||||
defineMessages({
|
defineMessages({
|
||||||
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
|
mention: { id: 'notification.mentioned', defaultMessage: '{name} mentioned you' },
|
||||||
group: { id: 'notifications.group', defaultMessage: '{count, plural, one {# notification} other {# notifications}}' },
|
group: { id: 'notifications.group', defaultMessage: '{count, plural, one {# notification} other {# notifications}}' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -175,6 +175,48 @@ const excludeTypesFromFilter = (filter: string) => {
|
||||||
|
|
||||||
const noOp = () => new Promise(f => f(undefined));
|
const noOp = () => new Promise(f => f(undefined));
|
||||||
|
|
||||||
|
const STATUS_NOTIFICATION_TYPES = [
|
||||||
|
'favourite',
|
||||||
|
'group_favourite',
|
||||||
|
'mention',
|
||||||
|
'reblog',
|
||||||
|
'group_reblog',
|
||||||
|
'status',
|
||||||
|
'poll',
|
||||||
|
'update',
|
||||||
|
// WIP separate notifications for each reaction?
|
||||||
|
// 'pleroma:emoji_reaction',
|
||||||
|
'pleroma:event_reminder',
|
||||||
|
'pleroma:participation_accepted',
|
||||||
|
'pleroma:participation_request',
|
||||||
|
];
|
||||||
|
|
||||||
|
const deduplicateNotifications = (notifications: any[]) => {
|
||||||
|
const deduplicatedNotifications: any[] = [];
|
||||||
|
|
||||||
|
for (const notification of notifications) {
|
||||||
|
if (STATUS_NOTIFICATION_TYPES.includes(notification.type)) {
|
||||||
|
const existingNotification = deduplicatedNotifications
|
||||||
|
.find(deduplicatedNotification => deduplicatedNotification.type === notification.type && deduplicatedNotification.status?.id === notification.status?.id);
|
||||||
|
|
||||||
|
if (existingNotification) {
|
||||||
|
if (existingNotification?.accounts) {
|
||||||
|
existingNotification.accounts.push(notification.account);
|
||||||
|
} else {
|
||||||
|
existingNotification.accounts = [existingNotification.account, notification.account];
|
||||||
|
}
|
||||||
|
existingNotification.id += ':' + notification.id;
|
||||||
|
} else {
|
||||||
|
deduplicatedNotifications.push(notification);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deduplicatedNotifications.push(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deduplicatedNotifications;
|
||||||
|
};
|
||||||
|
|
||||||
const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => any = noOp) =>
|
const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => any = noOp) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
if (!isLoggedIn(getState)) return dispatch(noOp);
|
if (!isLoggedIn(getState)) return dispatch(noOp);
|
||||||
|
@ -240,7 +282,9 @@ const expandNotifications = ({ maxId }: Record<string, any> = {}, done: () => an
|
||||||
const statusesFromGroups = (Object.values(entries.statuses) as Status[]).filter((status) => !!status.group);
|
const statusesFromGroups = (Object.values(entries.statuses) as Status[]).filter((status) => !!status.group);
|
||||||
dispatch(fetchGroupRelationships(statusesFromGroups.map((status: any) => status.group?.id)));
|
dispatch(fetchGroupRelationships(statusesFromGroups.map((status: any) => status.group?.id)));
|
||||||
|
|
||||||
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
|
const deduplicatedNotifications = deduplicateNotifications(response.data);
|
||||||
|
|
||||||
|
dispatch(expandNotificationsSuccess(deduplicatedNotifications, next ? next.uri : null, isLoadingMore));
|
||||||
fetchRelatedRelationships(dispatch, response.data);
|
fetchRelatedRelationships(dispatch, response.data);
|
||||||
done();
|
done();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|
|
@ -146,6 +146,27 @@ const parseTags = (tags: Record<string, any[]> = {}, mode: 'any' | 'all' | 'none
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deduplicateStatuses = (statuses: any[]) => {
|
||||||
|
const deduplicatedStatuses: any[] = [];
|
||||||
|
|
||||||
|
for (const status of statuses) {
|
||||||
|
const reblogged = status.reblog && deduplicatedStatuses.find((deduplicatedStatuses) => deduplicatedStatuses.reblog?.id === status.reblog.id);
|
||||||
|
|
||||||
|
if (reblogged) {
|
||||||
|
if (reblogged.accounts) {
|
||||||
|
reblogged.accounts.push(status.account);
|
||||||
|
} else {
|
||||||
|
reblogged.accounts = [reblogged.account, status.account];
|
||||||
|
}
|
||||||
|
reblogged.id += ':' + status.id;
|
||||||
|
} else {
|
||||||
|
deduplicatedStatuses.push(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deduplicatedStatuses;
|
||||||
|
};
|
||||||
|
|
||||||
const expandTimeline = (timelineId: string, path: string, params: Record<string, any> = {}, done = noOp) =>
|
const expandTimeline = (timelineId: string, path: string, params: Record<string, any> = {}, done = noOp) =>
|
||||||
(dispatch: AppDispatch, getState: () => RootState) => {
|
(dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const timeline = getState().timelines.get(timelineId) || {} as Record<string, any>;
|
const timeline = getState().timelines.get(timelineId) || {} as Record<string, any>;
|
||||||
|
@ -172,12 +193,15 @@ const expandTimeline = (timelineId: string, path: string, params: Record<string,
|
||||||
return api(getState).get(path, { params }).then(response => {
|
return api(getState).get(path, { params }).then(response => {
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
|
||||||
|
const statuses = deduplicateStatuses(response.data);
|
||||||
|
dispatch(importFetchedStatuses(statuses.filter(status => status.accounts)));
|
||||||
|
|
||||||
const statusesFromGroups = (response.data as Status[]).filter((status) => !!status.group);
|
const statusesFromGroups = (response.data as Status[]).filter((status) => !!status.group);
|
||||||
dispatch(fetchGroupRelationships(statusesFromGroups.map((status: any) => status.group?.id)));
|
dispatch(fetchGroupRelationships(statusesFromGroups.map((status: any) => status.group?.id)));
|
||||||
|
|
||||||
dispatch(expandTimelineSuccess(
|
dispatch(expandTimelineSuccess(
|
||||||
timelineId,
|
timelineId,
|
||||||
response.data,
|
statuses,
|
||||||
getNextLink(response),
|
getNextLink(response),
|
||||||
getPrevLink(response),
|
getPrevLink(response),
|
||||||
response.status === 206,
|
response.status === 206,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { defineMessages, useIntl, FormattedList, FormattedMessage } from 'react-intl';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { mentionCompose, replyCompose } from 'soapbox/actions/compose';
|
import { mentionCompose, replyCompose } from 'soapbox/actions/compose';
|
||||||
|
@ -249,6 +250,31 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (isReblog) {
|
} else if (isReblog) {
|
||||||
|
const accounts = status.accounts || ImmutableList([status.account]);
|
||||||
|
|
||||||
|
const renderedAccounts = accounts.slice(0, 2).map(account => !!account && (
|
||||||
|
<Link to={`/@${account.acct}`} className='hover:underline'>
|
||||||
|
<bdi className='truncate'>
|
||||||
|
<strong
|
||||||
|
className='text-gray-800 dark:text-gray-200'
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: account.display_name_html,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</bdi>
|
||||||
|
</Link>
|
||||||
|
)).toArray().filter(Boolean);
|
||||||
|
|
||||||
|
if (accounts.size > 2) {
|
||||||
|
renderedAccounts.push(
|
||||||
|
<FormattedMessage
|
||||||
|
id='notification.more'
|
||||||
|
defaultMessage='{count, plural, one {# other} other {# others}}'
|
||||||
|
values={{ count: accounts.size - renderedAccounts.length }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StatusInfo
|
<StatusInfo
|
||||||
avatarSize={avatarSize}
|
avatarSize={avatarSize}
|
||||||
|
@ -258,18 +284,8 @@ const Status: React.FC<IStatus> = (props) => {
|
||||||
id='status.reblogged_by'
|
id='status.reblogged_by'
|
||||||
defaultMessage='{name} reposted'
|
defaultMessage='{name} reposted'
|
||||||
values={{
|
values={{
|
||||||
name: (
|
name: <FormattedList type='conjunction' value={renderedAccounts} />,
|
||||||
<Link to={`/@${status.account.acct}`} className='hover:underline'>
|
count: accounts.size,
|
||||||
<bdi className='truncate'>
|
|
||||||
<strong
|
|
||||||
className='text-gray-800 dark:text-gray-200'
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: status.account.display_name_html,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</bdi>
|
|
||||||
</Link>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { defineMessages, useIntl, FormattedMessage, IntlShape, MessageDescriptor, defineMessage } from 'react-intl';
|
import { defineMessages, useIntl, FormattedList, FormattedMessage, IntlShape, MessageDescriptor } from 'react-intl';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { mentionCompose } from 'soapbox/actions/compose';
|
import { mentionCompose } from 'soapbox/actions/compose';
|
||||||
|
@ -17,7 +18,8 @@ import { makeGetNotification } from 'soapbox/selectors';
|
||||||
import { NotificationType, validType } from 'soapbox/utils/notification';
|
import { NotificationType, validType } from 'soapbox/utils/notification';
|
||||||
|
|
||||||
import type { ScrollPosition } from 'soapbox/components/status';
|
import type { ScrollPosition } from 'soapbox/components/status';
|
||||||
import type { Account as AccountEntity, Status as StatusEntity, Notification as NotificationEntity } from 'soapbox/types/entities';
|
import type { Account as AccountEntity, Status as StatusEntity, Notification as NotificationEntity,
|
||||||
|
} from 'soapbox/types/entities';
|
||||||
|
|
||||||
const notificationForScreenReader = (intl: IntlShape, message: string, timestamp: Date) => {
|
const notificationForScreenReader = (intl: IntlShape, message: string, timestamp: Date) => {
|
||||||
const output = [message];
|
const output = [message];
|
||||||
|
@ -58,11 +60,6 @@ const icons: Record<NotificationType, string> = {
|
||||||
'pleroma:participation_accepted': require('@tabler/icons/calendar-event.svg'),
|
'pleroma:participation_accepted': require('@tabler/icons/calendar-event.svg'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const nameMessage = defineMessage({
|
|
||||||
id: 'notification.name',
|
|
||||||
defaultMessage: '{link}{others}',
|
|
||||||
});
|
|
||||||
|
|
||||||
const messages: Record<NotificationType, MessageDescriptor> = defineMessages({
|
const messages: Record<NotificationType, MessageDescriptor> = defineMessages({
|
||||||
follow: {
|
follow: {
|
||||||
id: 'notification.follow',
|
id: 'notification.follow',
|
||||||
|
@ -138,26 +135,29 @@ const buildMessage = (
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
type: NotificationType,
|
type: NotificationType,
|
||||||
account: AccountEntity,
|
account: AccountEntity,
|
||||||
totalCount: number | null,
|
accounts: ImmutableList<AccountEntity> | null,
|
||||||
targetName: string,
|
targetName: string,
|
||||||
instanceTitle: string,
|
instanceTitle: string,
|
||||||
): React.ReactNode => {
|
): React.ReactNode => {
|
||||||
const link = buildLink(account);
|
if (!accounts) accounts = accounts || ImmutableList([account]);
|
||||||
const name = intl.formatMessage(nameMessage, {
|
|
||||||
link,
|
const renderedAccounts = accounts.slice(0, 2).map(account => buildLink(account)).toArray().filter(Boolean);
|
||||||
others: totalCount && totalCount > 0 ? (
|
|
||||||
|
if (accounts.size > 2) {
|
||||||
|
renderedAccounts.push(
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='notification.others'
|
id='notification.more'
|
||||||
defaultMessage='+ {count, plural, one {# other} other {# others}}'
|
defaultMessage='{count, plural, one {# other} other {# others}}'
|
||||||
values={{ count: totalCount - 1 }}
|
values={{ count: accounts.size - renderedAccounts.length }}
|
||||||
/>
|
/>,
|
||||||
) : '',
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
return intl.formatMessage(messages[type], {
|
return intl.formatMessage(messages[type], {
|
||||||
name,
|
name: <FormattedList type='conjunction' value={renderedAccounts} />,
|
||||||
targetName,
|
targetName,
|
||||||
instance: instanceTitle,
|
instance: instanceTitle,
|
||||||
|
count: accounts.size,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ const Notification: React.FC<INotificaton> = (props) => {
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
|
|
||||||
const type = notification.type;
|
const type = notification.type;
|
||||||
const { account, status } = notification;
|
const { account, accounts, status } = notification;
|
||||||
|
|
||||||
const getHandlers = () => ({
|
const getHandlers = () => ({
|
||||||
reply: handleMention,
|
reply: handleMention,
|
||||||
|
@ -356,7 +356,9 @@ const Notification: React.FC<INotificaton> = (props) => {
|
||||||
|
|
||||||
const targetName = notification.target && typeof notification.target === 'object' ? notification.target.acct : '';
|
const targetName = notification.target && typeof notification.target === 'object' ? notification.target.acct : '';
|
||||||
|
|
||||||
const message: React.ReactNode = validType(type) && account && typeof account === 'object' ? buildMessage(intl, type, account, notification.total_count, targetName, instance.title) : null;
|
const message: React.ReactNode = validType(type) && account && typeof account === 'object'
|
||||||
|
? buildMessage(intl, type, account, accounts as ImmutableList<AccountEntity>, targetName, instance.title)
|
||||||
|
: null;
|
||||||
|
|
||||||
const ariaLabel = validType(type) ? (
|
const ariaLabel = validType(type) ? (
|
||||||
notificationForScreenReader(
|
notificationForScreenReader(
|
||||||
|
|
|
@ -1069,11 +1069,9 @@
|
||||||
"notification.follow_request": "{name} has requested to follow you",
|
"notification.follow_request": "{name} has requested to follow you",
|
||||||
"notification.group_favourite": "{name} liked your group post",
|
"notification.group_favourite": "{name} liked your group post",
|
||||||
"notification.group_reblog": "{name} reposted your group post",
|
"notification.group_reblog": "{name} reposted your group post",
|
||||||
"notification.mention": "{name} mentioned you",
|
|
||||||
"notification.mentioned": "{name} mentioned you",
|
"notification.mentioned": "{name} mentioned you",
|
||||||
|
"notification.more": "{count, plural, one {# other} other {# others}}",
|
||||||
"notification.move": "{name} moved to {targetName}",
|
"notification.move": "{name} moved to {targetName}",
|
||||||
"notification.name": "{link}{others}",
|
|
||||||
"notification.others": "+ {count, plural, one {# other} other {# others}}",
|
|
||||||
"notification.pleroma:chat_mention": "{name} sent you a message",
|
"notification.pleroma:chat_mention": "{name} sent you a message",
|
||||||
"notification.pleroma:emoji_reaction": "{name} reacted to your post",
|
"notification.pleroma:emoji_reaction": "{name} reacted to your post",
|
||||||
"notification.pleroma:event_reminder": "An event you are participating in starts soon",
|
"notification.pleroma:event_reminder": "An event you are participating in starts soon",
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* @see {@link https://docs.joinmastodon.org/entities/notification/}
|
* @see {@link https://docs.joinmastodon.org/entities/notification/}
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
|
List as ImmutableList,
|
||||||
Map as ImmutableMap,
|
Map as ImmutableMap,
|
||||||
Record as ImmutableRecord,
|
Record as ImmutableRecord,
|
||||||
fromJS,
|
fromJS,
|
||||||
|
@ -14,6 +15,7 @@ import type { Account, Status, EmbeddedEntity } from 'soapbox/types/entities';
|
||||||
// https://docs.joinmastodon.org/entities/notification/
|
// https://docs.joinmastodon.org/entities/notification/
|
||||||
export const NotificationRecord = ImmutableRecord({
|
export const NotificationRecord = ImmutableRecord({
|
||||||
account: null as EmbeddedEntity<Account>,
|
account: null as EmbeddedEntity<Account>,
|
||||||
|
accounts: null as ImmutableList<EmbeddedEntity<Account>> | null,
|
||||||
chat_message: null as ImmutableMap<string, any> | string | null, // pleroma:chat_mention
|
chat_message: null as ImmutableMap<string, any> | string | null, // pleroma:chat_mention
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
emoji: null as string | null, // pleroma:emoji_reaction
|
emoji: null as string | null, // pleroma:emoji_reaction
|
||||||
|
|
|
@ -44,6 +44,7 @@ interface Tombstone {
|
||||||
// https://docs.joinmastodon.org/entities/status/
|
// https://docs.joinmastodon.org/entities/status/
|
||||||
export const StatusRecord = ImmutableRecord({
|
export const StatusRecord = ImmutableRecord({
|
||||||
account: null as unknown as Account,
|
account: null as unknown as Account,
|
||||||
|
accounts: null as ImmutableList<Account> | null,
|
||||||
application: null as ImmutableMap<string, any> | null,
|
application: null as ImmutableMap<string, any> | null,
|
||||||
approval_status: 'approved' as StatusApprovalStatus,
|
approval_status: 'approved' as StatusApprovalStatus,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
|
@ -265,6 +266,17 @@ const parseAccount = (status: ImmutableMap<string, any>) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseAccounts = (status: ImmutableMap<string, any>) => {
|
||||||
|
try {
|
||||||
|
if (status.get('accounts')) {
|
||||||
|
const accounts = status.get('accounts').map((account: ImmutableMap<string, any>) => accountSchema.parse(maybeFromJS(account)));
|
||||||
|
return status.set('accounts', accounts);
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
return status.set('accounts', null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const parseGroup = (status: ImmutableMap<string, any>) => {
|
const parseGroup = (status: ImmutableMap<string, any>) => {
|
||||||
try {
|
try {
|
||||||
const group = groupSchema.parse(status.get('group').toJS());
|
const group = groupSchema.parse(status.get('group').toJS());
|
||||||
|
@ -293,6 +305,7 @@ export const normalizeStatus = (status: Record<string, any>) => {
|
||||||
normalizeDislikes(status);
|
normalizeDislikes(status);
|
||||||
normalizeTombstone(status);
|
normalizeTombstone(status);
|
||||||
parseAccount(status);
|
parseAccount(status);
|
||||||
|
parseAccounts(status);
|
||||||
parseGroup(status);
|
parseGroup(status);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -71,6 +71,7 @@ const comparator = (a: NotificationRecord, b: NotificationRecord) => {
|
||||||
const minifyNotification = (notification: NotificationRecord) => {
|
const minifyNotification = (notification: NotificationRecord) => {
|
||||||
return notification.mergeWith((o, n) => n || o, {
|
return notification.mergeWith((o, n) => n || o, {
|
||||||
account: notification.getIn(['account', 'id']) as string,
|
account: notification.getIn(['account', 'id']) as string,
|
||||||
|
accounts: notification.accounts?.map((account: any) => account.get('id')),
|
||||||
target: notification.getIn(['target', 'id']) as string,
|
target: notification.getIn(['target', 'id']) as string,
|
||||||
status: notification.getIn(['status', 'id']) as string,
|
status: notification.getIn(['status', 'id']) as string,
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,7 @@ const statusPleromaSchema = z.object({
|
||||||
|
|
||||||
const baseStatusSchema = z.object({
|
const baseStatusSchema = z.object({
|
||||||
account: accountSchema,
|
account: accountSchema,
|
||||||
|
accounts: z.array(accountSchema),
|
||||||
application: z.object({
|
application: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
website: z.string().url().nullable().catch(null),
|
website: z.string().url().nullable().catch(null),
|
||||||
|
|
|
@ -28,6 +28,10 @@ export function selectAccount(state: RootState, accountId: string) {
|
||||||
return state.entities[Entities.ACCOUNTS]?.store[accountId] as AccountSchema | undefined;
|
return state.entities[Entities.ACCOUNTS]?.store[accountId] as AccountSchema | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectAccounts(state: RootState, accountIds: ImmutableList<string>) {
|
||||||
|
return accountIds.map(accountId => state.entities[Entities.ACCOUNTS]?.store[accountId] as AccountSchema | undefined);
|
||||||
|
}
|
||||||
|
|
||||||
export function selectOwnAccount(state: RootState) {
|
export function selectOwnAccount(state: RootState) {
|
||||||
if (state.me) {
|
if (state.me) {
|
||||||
return selectAccount(state, state.me);
|
return selectAccount(state, state.me);
|
||||||
|
@ -162,7 +166,8 @@ export const makeGetNotification = () => {
|
||||||
(state: RootState, notification: Notification) => selectAccount(state, normalizeId(notification.account)),
|
(state: RootState, notification: Notification) => selectAccount(state, normalizeId(notification.account)),
|
||||||
(state: RootState, notification: Notification) => selectAccount(state, normalizeId(notification.target)),
|
(state: RootState, notification: Notification) => selectAccount(state, normalizeId(notification.target)),
|
||||||
(state: RootState, notification: Notification) => state.statuses.get(normalizeId(notification.status)),
|
(state: RootState, notification: Notification) => state.statuses.get(normalizeId(notification.status)),
|
||||||
], (notification, account, target, status) => {
|
(state: RootState, notification: Notification) => notification.accounts ? selectAccounts(state, notification.accounts?.map(normalizeId)) : null,
|
||||||
|
], (notification, account, target, status, accounts) => {
|
||||||
return notification.merge({
|
return notification.merge({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
account: account || null,
|
account: account || null,
|
||||||
|
@ -170,6 +175,8 @@ export const makeGetNotification = () => {
|
||||||
target: target || null,
|
target: target || null,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
status: status || null,
|
status: status || null,
|
||||||
|
// @ts-ignore
|
||||||
|
accounts,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Ładowanie…
Reference in New Issue