2024-09-27 08:02:02 -07:00
|
|
|
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
import { getNotificationStatus } from 'pl-fe/features/notifications/components/notification';
|
2024-09-27 08:02:02 -07:00
|
|
|
import { useClient } from 'pl-fe/hooks';
|
2024-09-28 13:10:31 -07:00
|
|
|
import { importEntities } from 'pl-fe/pl-hooks/importer';
|
2024-09-27 08:02:02 -07:00
|
|
|
import { queryClient } from 'pl-fe/queries/client';
|
|
|
|
import { flattenPages } from 'pl-fe/utils/queries';
|
|
|
|
|
|
|
|
import type {
|
|
|
|
Account as BaseAccount,
|
|
|
|
Notification as BaseNotification,
|
|
|
|
PaginatedResponse,
|
|
|
|
PlApiClient,
|
|
|
|
} from 'pl-api';
|
|
|
|
import type { NotificationType } from 'pl-fe/utils/notification';
|
|
|
|
|
|
|
|
|
|
|
|
type UseNotificationParams = {
|
|
|
|
types?: Array<NotificationType>;
|
|
|
|
excludeTypes?: Array<NotificationType>;
|
|
|
|
}
|
|
|
|
|
|
|
|
const getQueryKey = (params: UseNotificationParams) => [
|
|
|
|
'notifications',
|
|
|
|
params.types ? params.types.join('|') : params.excludeTypes ? ('exclude:' + params.excludeTypes.join('|')) : 'all',
|
|
|
|
];
|
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
type DeduplicatedNotification = BaseNotification & {
|
|
|
|
accounts: Array<BaseAccount>;
|
|
|
|
duplicate?: boolean;
|
|
|
|
}
|
2024-09-27 08:02:02 -07:00
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
const STATUS_NOTIFICATION_TYPES = [
|
|
|
|
'favourite',
|
|
|
|
'reblog',
|
|
|
|
'emoji_reaction',
|
|
|
|
'event_reminder',
|
|
|
|
'participation_accepted',
|
|
|
|
'participation_request',
|
|
|
|
];
|
2024-09-27 08:02:02 -07:00
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
const deduplicateNotifications = (notifications: Array<BaseNotification>) => {
|
|
|
|
const deduplicatedNotifications: DeduplicatedNotification[] = [];
|
|
|
|
|
|
|
|
for (const notification of notifications) {
|
|
|
|
if (STATUS_NOTIFICATION_TYPES.includes(notification.type)) {
|
|
|
|
const existingNotification = deduplicatedNotifications
|
|
|
|
.find(deduplicated =>
|
|
|
|
deduplicated.type === notification.type
|
|
|
|
&& ((notification.type === 'emoji_reaction' && deduplicated.type === 'emoji_reaction') ? notification.emoji === deduplicated.emoji : true)
|
|
|
|
&& getNotificationStatus(deduplicated)?.id === getNotificationStatus(notification)?.id,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (existingNotification) {
|
|
|
|
existingNotification.accounts.push(notification.account);
|
|
|
|
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: true });
|
|
|
|
} else {
|
|
|
|
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
deduplicatedNotifications.push({ ...notification, accounts: [notification.account], duplicate: false });
|
2024-09-27 08:02:02 -07:00
|
|
|
}
|
2024-09-28 13:10:31 -07:00
|
|
|
}
|
2024-09-27 08:02:02 -07:00
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
return deduplicatedNotifications;
|
|
|
|
};
|
|
|
|
|
|
|
|
const importNotifications = (response: PaginatedResponse<BaseNotification>) => {
|
|
|
|
const deduplicatedNotifications = deduplicateNotifications(response.items);
|
|
|
|
|
|
|
|
importEntities({
|
|
|
|
notifications: deduplicatedNotifications,
|
|
|
|
});
|
2024-09-27 08:02:02 -07:00
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
// const normalizedNotifications = normalizeNotifications(response.items);
|
2024-09-27 08:02:02 -07:00
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
// normalizedNotifications.map(minifyNotification).forEach(importNotification);
|
2024-09-27 08:02:02 -07:00
|
|
|
|
|
|
|
return {
|
2024-09-28 13:10:31 -07:00
|
|
|
items: deduplicatedNotifications.filter(({ duplicate }) => !duplicate).map(({ id }) => id),
|
2024-09-27 08:02:02 -07:00
|
|
|
previous: response.previous,
|
|
|
|
next: response.next,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const useNotifications = (params: UseNotificationParams) => {
|
|
|
|
const client = useClient();
|
|
|
|
|
|
|
|
const notificationsQuery = useInfiniteQuery({
|
|
|
|
queryKey: getQueryKey(params),
|
|
|
|
queryFn: ({ pageParam }) => (pageParam.next ? pageParam.next() : client.notifications.getNotifications({
|
|
|
|
types: params.types,
|
|
|
|
exclude_types: params.excludeTypes,
|
|
|
|
})).then(importNotifications),
|
|
|
|
initialPageParam: { previous: null, next: null } as Pick<PaginatedResponse<BaseNotification>, 'previous' | 'next'>,
|
|
|
|
getNextPageParam: (response) => response,
|
|
|
|
});
|
|
|
|
|
|
|
|
const data = flattenPages<string>(notificationsQuery.data) || [];
|
|
|
|
|
|
|
|
return {
|
|
|
|
...notificationsQuery,
|
|
|
|
data,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const prefetchNotifications = (client: PlApiClient, params: UseNotificationParams) =>
|
|
|
|
queryClient.prefetchInfiniteQuery({
|
|
|
|
queryKey: getQueryKey(params),
|
|
|
|
queryFn: ({ pageParam }) => (pageParam.next ? pageParam.next() : client.notifications.getNotifications({
|
|
|
|
types: params.types,
|
|
|
|
exclude_types: params.excludeTypes,
|
|
|
|
})).then(importNotifications),
|
|
|
|
initialPageParam: { previous: null, next: null } as Pick<PaginatedResponse<BaseNotification>, 'previous' | 'next'>,
|
|
|
|
getNextPageParam: (response) => response,
|
|
|
|
});
|
|
|
|
|
2024-09-28 13:10:31 -07:00
|
|
|
export { useNotifications, prefetchNotifications, type DeduplicatedNotification };
|