From a18b049eb7e8720acc94481b12ddd89b39d8bd16 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Mon, 16 Sep 2024 14:08:45 -0300 Subject: [PATCH] feat: make notifications great again it works the same as before, but with way less code --- src/controllers/api/notifications.ts | 44 ++-------------------------- src/controllers/api/streaming.ts | 27 ++--------------- src/views/mastodon/notifications.ts | 26 +++++++--------- 3 files changed, 15 insertions(+), 82 deletions(-) diff --git a/src/controllers/api/notifications.ts b/src/controllers/api/notifications.ts index 1b9c746a..144064a6 100644 --- a/src/controllers/api/notifications.ts +++ b/src/controllers/api/notifications.ts @@ -4,10 +4,9 @@ import { z } from 'zod'; import { AppContext, AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; import { DittoPagination } from '@/interfaces/DittoPagination.ts'; -import { getAmount } from '@/utils/bolt11.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { paginated } from '@/utils/api.ts'; -import { renderNotification, RenderNotificationOpts } from '@/views/mastodon/notifications.ts'; +import { renderNotification } from '@/views/mastodon/notifications.ts'; /** Set of known notification types across backends. */ const notificationTypes = new Set([ @@ -86,54 +85,17 @@ async function renderNotifications( const { signal } = c.req.raw; const opts = { signal, limit: params.limit, timeout: Conf.db.timeouts.timelines }; - const zapsRelatedFilter: NostrFilter[] = []; - const events = await store .query(filters, opts) - .then((events) => - events.filter((event) => { - if (event.kind === 9735) { - const zappedEventId = event.tags.find(([name]) => name === 'e')?.[1]; - if (zappedEventId) zapsRelatedFilter.push({ kinds: [1], ids: [zappedEventId] }); - const zapSender = event.tags.find(([name]) => name === 'P')?.[1]; - if (zapSender) zapsRelatedFilter.push({ kinds: [0], authors: [zapSender] }); - } - - return event.pubkey !== pubkey; - }) - ) + .then((events) => events.filter((event) => event.pubkey !== pubkey)) .then((events) => hydrateEvents({ events, store, signal })); if (!events.length) { return c.json([]); } - const zapSendersAndPosts = await store - .query(zapsRelatedFilter, opts) - .then((events) => hydrateEvents({ events, store, signal })); - const notifications = (await Promise.all(events.map((event) => { - const opts: RenderNotificationOpts = { viewerPubkey: pubkey }; - if (event.kind === 9735) { - const zapRequestString = event?.tags?.find(([name]) => name === 'description')?.[1]; - const zapRequest = n.json().pipe(n.event()).optional().catch(undefined).parse(zapRequestString); - // By getting the pubkey from the zap request we guarantee who is the sender - // some clients don't put the P tag in the zap receipt... - const zapSender = zapRequest?.pubkey; - const zappedPost = event.tags.find(([name]) => name === 'e')?.[1]; - - const amountSchema = z.coerce.number().int().nonnegative().catch(0); - // amount in millisats - const amount = amountSchema.parse(getAmount(event?.tags.find(([name]) => name === 'bolt11')?.[1])); - - opts['zap'] = { - zapSender: zapSendersAndPosts.find(({ pubkey, kind }) => kind === 0 && pubkey === zapSender) ?? zapSender, - zappedPost: zapSendersAndPosts.find(({ id }) => id === zappedPost), - amount, - message: zapRequest?.content, - }; - } - return renderNotification(event, opts); + return renderNotification(event, { viewerPubkey: pubkey }); }))) .filter((notification) => notification && types.has(notification.type)); diff --git a/src/controllers/api/streaming.ts b/src/controllers/api/streaming.ts index 3f05669d..23a94407 100644 --- a/src/controllers/api/streaming.ts +++ b/src/controllers/api/streaming.ts @@ -1,5 +1,5 @@ import TTLCache from '@isaacs/ttlcache'; -import { NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; +import { NostrEvent, NostrFilter } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; import { z } from 'zod'; @@ -11,10 +11,8 @@ import { getFeedPubkeys } from '@/queries.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { Storages } from '@/storages.ts'; import { bech32ToPubkey, Time } from '@/utils.ts'; -import { getAmount } from '@/utils/bolt11.ts'; import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; import { renderNotification } from '@/views/mastodon/notifications.ts'; -import { RenderNotificationOpts } from '@/views/mastodon/notifications.ts'; const debug = Debug('ditto:streaming'); @@ -157,28 +155,7 @@ const streamingController: AppController = async (c) => { if (['user', 'user:notification'].includes(stream) && pubkey) { sub([{ '#p': [pubkey] }], async (event) => { if (event.pubkey === pubkey) return; // skip own events - - const opts: RenderNotificationOpts = { viewerPubkey: pubkey }; - - if (event.kind === 9735) { - const zapRequestString = event?.tags?.find(([name]) => name === 'description')?.[1]; - const zapRequest = n.json().pipe(n.event()).optional().catch(undefined).parse(zapRequestString); - // By getting the pubkey from the zap request we guarantee who is the sender - // some clients don't put the P tag in the zap receipt... - const zapSender = zapRequest?.pubkey; - - const amountSchema = z.coerce.number().int().nonnegative().catch(0); - // amount in millisats - const amount = amountSchema.parse(getAmount(event?.tags.find(([name]) => name === 'bolt11')?.[1])); - - opts['zap'] = { - zapSender, - amount, - message: zapRequest?.content, - }; - } - - const payload = await renderNotification(event, opts); + const payload = await renderNotification(event, { viewerPubkey: pubkey }); if (payload) { return { event: 'notification', diff --git a/src/views/mastodon/notifications.ts b/src/views/mastodon/notifications.ts index f2438ad2..973a7a6d 100644 --- a/src/views/mastodon/notifications.ts +++ b/src/views/mastodon/notifications.ts @@ -6,14 +6,8 @@ import { DittoEvent } from '@/interfaces/DittoEvent.ts'; import { nostrDate } from '@/utils.ts'; import { renderStatus } from '@/views/mastodon/statuses.ts'; -export interface RenderNotificationOpts { +interface RenderNotificationOpts { viewerPubkey: string; - zap?: { - zapSender?: NostrEvent | NostrEvent['pubkey']; // kind 0 or pubkey - zappedPost?: NostrEvent; - amount?: number; - message?: string; - }; } function renderNotification(event: DittoEvent, opts: RenderNotificationOpts) { @@ -120,23 +114,23 @@ async function renderNameGrant(event: DittoEvent) { } async function renderZap(event: DittoEvent, opts: RenderNotificationOpts) { - if (!opts.zap?.zapSender) return; + if (!event.zap_sender) return; - const { amount = 0, message = '' } = opts.zap; - if (amount < 1) return; + const { zap_amount = 0, zap_message = '' } = event; + if (zap_amount < 1) return; - const account = typeof opts.zap.zapSender !== 'string' - ? await renderAccount(opts.zap.zapSender) - : await accountFromPubkey(opts.zap.zapSender); + const account = typeof event.zap_sender !== 'string' + ? await renderAccount(event.zap_sender) + : await accountFromPubkey(event.zap_sender); return { id: notificationId(event), type: 'ditto:zap', - amount, - message, + amount: zap_amount, + message: zap_message, created_at: nostrDate(event.created_at).toISOString(), account, - ...(opts.zap?.zappedPost ? { status: await renderStatus(opts.zap?.zappedPost, opts) } : {}), + ...(event.zapped ? { status: await renderStatus(event.zapped, opts) } : {}), }; }