Move push notification rendering to its own view

This commit is contained in:
Alex Gleason 2024-10-15 17:13:39 -05:00
parent 8bf0a443db
commit 7fbda4a56b
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
3 changed files with 65 additions and 23 deletions

View file

@ -2,7 +2,6 @@ import { NKinds, NostrEvent, NSchema as n } from '@nostrify/nostrify';
import { Stickynotes } from '@soapbox/stickynotes';
import { Kysely, sql } from 'kysely';
import { LRUCache } from 'lru-cache';
import { nip19 } from 'nostr-tools';
import { z } from 'zod';
import { Conf } from '@/config.ts';
@ -14,7 +13,6 @@ import { RelayError } from '@/RelayError.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { hydrateEvents } from '@/storages/hydrate.ts';
import { Storages } from '@/storages.ts';
import { MastodonPush } from '@/types/MastodonPush.ts';
import { eventAge, parseNip05, Time } from '@/utils.ts';
import { getAmount } from '@/utils/bolt11.ts';
import { detectLanguage } from '@/utils/language.ts';
@ -22,7 +20,7 @@ import { nip05Cache } from '@/utils/nip05.ts';
import { purifyEvent } from '@/utils/purify.ts';
import { updateStats } from '@/utils/stats.ts';
import { getTagSet } from '@/utils/tags.ts';
import { renderNotification } from '@/views/mastodon/notifications.ts';
import { renderWebPushNotification } from '@/views/mastodon/push.ts';
import { policyWorker } from '@/workers/policy.ts';
import { verifyEventWorker } from '@/workers/verify.ts';
@ -257,12 +255,14 @@ async function webPush(event: NostrEvent): Promise<void> {
.execute();
for (const row of rows) {
if (row.pubkey === event.pubkey) {
const viewerPubkey = row.pubkey;
if (viewerPubkey === event.pubkey) {
continue; // Don't notify authors about their own events.
}
const notification = await renderNotification(event, { viewerPubkey: row.pubkey });
if (!notification) {
const message = await renderWebPushNotification(event, viewerPubkey);
if (!message) {
continue;
}
@ -274,18 +274,8 @@ async function webPush(event: NostrEvent): Promise<void> {
},
};
const message: MastodonPush = {
notification_id: notification.id,
notification_type: notification.type,
access_token: nip19.npubEncode(row.pubkey),
preferred_locale: 'en',
title: notification.account.display_name || notification.account.username,
icon: notification.account.avatar_static,
body: event.content,
};
await DittoPush.push(subscription, message);
webPushNotificationsCounter.inc({ type: notification.type });
webPushNotificationsCounter.inc({ type: message.notification_type });
}
}

View file

@ -44,7 +44,7 @@ async function renderMention(event: DittoEvent, opts: RenderNotificationOpts) {
return {
id: notificationId(event),
type: 'mention',
type: 'mention' as const,
created_at: nostrDate(event.created_at).toISOString(),
account: status.account,
status: status,
@ -59,7 +59,7 @@ async function renderReblog(event: DittoEvent, opts: RenderNotificationOpts) {
return {
id: notificationId(event),
type: 'reblog',
type: 'reblog' as const,
created_at: nostrDate(event.created_at).toISOString(),
account,
status,
@ -74,7 +74,7 @@ async function renderFavourite(event: DittoEvent, opts: RenderNotificationOpts)
return {
id: notificationId(event),
type: 'favourite',
type: 'favourite' as const,
created_at: nostrDate(event.created_at).toISOString(),
account,
status,
@ -89,7 +89,7 @@ async function renderReaction(event: DittoEvent, opts: RenderNotificationOpts) {
return {
id: notificationId(event),
type: 'pleroma:emoji_reaction',
type: 'pleroma:emoji_reaction' as const,
emoji: event.content,
emoji_url: event.tags.find(([name, value]) => name === 'emoji' && `:${value}:` === event.content)?.[2],
created_at: nostrDate(event.created_at).toISOString(),
@ -106,7 +106,7 @@ async function renderNameGrant(event: DittoEvent) {
return {
id: notificationId(event),
type: 'ditto:name_grant',
type: 'ditto:name_grant' as const,
name: d,
created_at: nostrDate(event.created_at).toISOString(),
account,
@ -125,7 +125,7 @@ async function renderZap(event: DittoEvent, opts: RenderNotificationOpts) {
return {
id: notificationId(event),
type: 'ditto:zap',
type: 'ditto:zap' as const,
amount: zap_amount,
message: zap_message,
created_at: nostrDate(event.created_at).toISOString(),

View file

@ -0,0 +1,52 @@
import type { NostrEvent } from '@nostrify/nostrify';
import { nip19 } from 'nostr-tools';
import { MastodonPush } from '@/types/MastodonPush.ts';
import { renderNotification } from '@/views/mastodon/notifications.ts';
/**
* Render a web push notification for the viewer.
* Unlike other views, only one will be rendered at a time, so making use of async calls is okay.
*/
export async function renderWebPushNotification(
event: NostrEvent,
viewerPubkey: string,
): Promise<MastodonPush | undefined> {
const notification = await renderNotification(event, { viewerPubkey });
if (!notification) {
return;
}
return {
notification_id: notification.id,
notification_type: notification.type,
access_token: nip19.npubEncode(viewerPubkey),
preferred_locale: 'en',
title: renderTitle(notification),
icon: notification.account.avatar_static,
body: event.content,
};
}
type MastodonNotification = NonNullable<Awaited<ReturnType<typeof renderNotification>>>;
function renderTitle(notification: MastodonNotification): string {
const { account } = notification;
switch (notification.type) {
case 'ditto:name_grant':
return `You were granted the name ${notification.name}`;
case 'ditto:zap':
return `${account.display_name} zapped you ${notification.amount} sats`;
case 'pleroma:emoji_reaction':
return `${account.display_name} reacted to your post`;
case 'favourite':
return `${account.display_name} liked your post`;
case 'mention':
return `${account.display_name} mentioned you`;
case 'reblog':
return `${account.display_name} reposted your post`;
default:
return account.display_name || account.username;
}
}