mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import { NostrFilter, NSchema as n } from '@nostrify/nostrify';
|
|
import { z } from 'zod';
|
|
|
|
import { AppContext, AppController } from '@/app.ts';
|
|
import { Conf } from '@/config.ts';
|
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
|
import { paginated, PaginationParams, paginationSchema } from '@/utils/api.ts';
|
|
import { renderNotification } from '@/views/mastodon/notifications.ts';
|
|
|
|
/** Set of known notification types across backends. */
|
|
const notificationTypes = new Set([
|
|
'mention',
|
|
'status',
|
|
'reblog',
|
|
'follow',
|
|
'follow_request',
|
|
'favourite',
|
|
'poll',
|
|
'update',
|
|
'admin.sign_up',
|
|
'admin.report',
|
|
'severed_relationships',
|
|
'pleroma:emoji_reaction',
|
|
'ditto:name_grant',
|
|
]);
|
|
|
|
const notificationsSchema = z.object({
|
|
account_id: n.id().optional(),
|
|
});
|
|
|
|
const notificationsController: AppController = async (c) => {
|
|
const pubkey = await c.get('signer')?.getPublicKey()!;
|
|
const params = paginationSchema.parse(c.req.query());
|
|
|
|
const types = notificationTypes
|
|
.intersection(new Set(c.req.queries('types[]') ?? notificationTypes))
|
|
.difference(new Set(c.req.queries('exclude_types[]')));
|
|
|
|
const { account_id } = notificationsSchema.parse(c.req.query());
|
|
|
|
const kinds = new Set<number>();
|
|
|
|
if (types.has('mention')) {
|
|
kinds.add(1);
|
|
}
|
|
if (types.has('reblog')) {
|
|
kinds.add(6);
|
|
}
|
|
if (types.has('favourite') || types.has('pleroma:emoji_reaction')) {
|
|
kinds.add(7);
|
|
}
|
|
|
|
const filter: NostrFilter = {
|
|
kinds: [...kinds],
|
|
'#p': [pubkey],
|
|
...params,
|
|
};
|
|
|
|
const filters: NostrFilter[] = [filter];
|
|
|
|
if (account_id) {
|
|
filter.authors = [account_id];
|
|
}
|
|
|
|
if (types.has('ditto:name_grant') && !account_id) {
|
|
filters.push({ kinds: [30360], authors: [Conf.pubkey], '#p': [pubkey], ...params });
|
|
}
|
|
|
|
return renderNotifications(filters, types, params, c);
|
|
};
|
|
|
|
async function renderNotifications(
|
|
filters: NostrFilter[],
|
|
types: Set<string>,
|
|
params: PaginationParams,
|
|
c: AppContext,
|
|
) {
|
|
const store = c.get('store');
|
|
const pubkey = await c.get('signer')?.getPublicKey()!;
|
|
const { signal } = c.req.raw;
|
|
const opts = { signal, limit: params.limit };
|
|
|
|
const events = await store
|
|
.query(filters, opts)
|
|
.then((events) => events.filter((event) => event.pubkey !== pubkey))
|
|
.then((events) => hydrateEvents({ events, store, signal }));
|
|
|
|
if (!events.length) {
|
|
return c.json([]);
|
|
}
|
|
|
|
const notifications = (await Promise.all(events.map((event) => renderNotification(event, { viewerPubkey: pubkey }))))
|
|
.filter((notification) => notification && types.has(notification.type));
|
|
|
|
if (!notifications.length) {
|
|
return c.json([]);
|
|
}
|
|
|
|
return paginated(c, events, notifications);
|
|
}
|
|
|
|
export { notificationsController };
|