import { sendUserNotification } from '../api/webPush' import { ANON_USER_ID } from './constants' import { msatsToSats, numWithUnits } from './format' export const notifyUserSubscribers = async ({ models, item }) => { try { const isPost = !!item.title const userSubs = await models.userSubscription.findMany({ where: { followeeId: Number(item.userId), [isPost ? 'postsSubscribedAt' : 'commentsSubscribedAt']: { not: null } }, include: { followee: true } }) const subType = isPost ? 'POST' : 'COMMENT' const tag = `FOLLOW-${item.userId}-${subType}` await Promise.allSettled(userSubs.map(({ followerId, followee }) => sendUserNotification(followerId, { title: `@${followee.name} ${isPost ? 'created a post' : 'replied to a post'}`, body: isPost ? item.title : item.text, item, data: { followeeName: followee.name, subType }, tag }))) } catch (err) { console.error(err) } } export const notifyTerritorySubscribers = async ({ models, item }) => { try { const isPost = !!item.title const { subName } = item // only notify on posts in subs if (!isPost || !subName) return const territorySubs = await models.subSubscription.findMany({ where: { subName } }) const author = await models.user.findUnique({ where: { id: item.userId } }) const tag = `TERRITORY_POST-${subName}` await Promise.allSettled( territorySubs // don't send push notification to author itself .filter(({ userId }) => userId !== author.id) .map(({ userId }) => sendUserNotification(userId, { title: `@${author.name} created a post in ~${subName}`, body: item.title, item, data: { subName }, tag }))) } catch (err) { console.error(err) } } export const notifyItemParents = async ({ models, item, me }) => { try { const user = await models.user.findUnique({ where: { id: me?.id || ANON_USER_ID } }) const parents = await models.$queryRawUnsafe( 'SELECT DISTINCT p."userId" FROM "Item" i JOIN "Item" p ON p.path @> i.path WHERE i.id = $1 and p."userId" <> $2 ' + 'AND NOT EXISTS (SELECT 1 FROM "Mute" m WHERE m."muterId" = p."userId" AND m."mutedId" = $2)', Number(item.parentId), Number(user.id)) Promise.allSettled( parents.map(({ userId }) => sendUserNotification(userId, { title: `@${user.name} replied to you`, body: item.text, item, tag: 'REPLY' })) ) } catch (err) { console.error(err) } } export const notifyZapped = async ({ models, id }) => { try { const updatedItem = await models.item.findUnique({ where: { id: Number(id) } }) const forwards = await models.itemForward.findMany({ where: { itemId: Number(id) } }) const userPromises = forwards.map(fwd => models.user.findUnique({ where: { id: fwd.userId } })) const userResults = await Promise.allSettled(userPromises) const mappedForwards = forwards.map((fwd, index) => ({ ...fwd, user: userResults[index].value ?? null })) let forwardedSats = 0 let forwardedUsers = '' if (mappedForwards.length) { forwardedSats = Math.floor(msatsToSats(updatedItem.msats) * mappedForwards.map(fwd => fwd.pct).reduce((sum, cur) => sum + cur) / 100) forwardedUsers = mappedForwards.map(fwd => `@${fwd.user.name}`).join(', ') } let notificationTitle if (updatedItem.title) { if (forwards.length > 0) { notificationTitle = `your post forwarded ${numWithUnits(forwardedSats)} to ${forwardedUsers}` } else { notificationTitle = `your post stacked ${numWithUnits(msatsToSats(updatedItem.msats))}` } } else { if (forwards.length > 0) { // I don't think this case is possible notificationTitle = `your reply forwarded ${numWithUnits(forwardedSats)} to ${forwardedUsers}` } else { notificationTitle = `your reply stacked ${numWithUnits(msatsToSats(updatedItem.msats))}` } } await sendUserNotification(updatedItem.userId, { title: notificationTitle, body: updatedItem.title ? updatedItem.title : updatedItem.text, item: updatedItem, tag: `TIP-${updatedItem.id}` }) // send push notifications to forwarded recipients if (mappedForwards.length) { await Promise.allSettled(mappedForwards.map(forward => sendUserNotification(forward.user.id, { title: `you were forwarded ${numWithUnits(msatsToSats(updatedItem.msats) * forward.pct / 100)}`, body: updatedItem.title ?? updatedItem.text, item: updatedItem, tag: `FORWARDEDTIP-${updatedItem.id}` }))) } } catch (err) { console.error(err) } } export const notifyTerritoryTransfer = async ({ models, sub, to }) => { try { await sendUserNotification(to.id, { title: `~${sub.name} was transferred to you`, tag: `TERRITORY_TRANSFER-${sub.name}` }) } catch (err) { console.error(err) } }