Fix missing push notifications for thread subscriptions (#1843)

* Fix missing push notifications for thread subscriptions

* Filter by comments in calling context

* Fix mutes not considered

* Fix duplicate push notification (reply+thread subscription) sent
This commit is contained in:
ekzyis 2025-01-25 20:47:58 +01:00 committed by GitHub
parent b28407ee99
commit c023e8d7d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 1 deletions

View File

@ -1,5 +1,5 @@
import { ANON_ITEM_SPAM_INTERVAL, ITEM_SPAM_INTERVAL, PAID_ACTION_PAYMENT_METHODS, USER_ID } from '@/lib/constants'
import { notifyItemMention, notifyItemParents, notifyMention, notifyTerritorySubscribers, notifyUserSubscribers } from '@/lib/webPush'
import { notifyItemMention, notifyItemParents, notifyMention, notifyTerritorySubscribers, notifyUserSubscribers, notifyThreadSubscribers } from '@/lib/webPush'
import { getItemMentions, getMentions, performBotBehavior } from './lib/item'
import { msatsToSats, satsToMsats } from '@/lib/format'
import { GqlInputError } from '@/lib/error'
@ -259,6 +259,7 @@ export async function nonCriticalSideEffects ({ invoice, id }, { models }) {
if (item.parentId) {
notifyItemParents({ item, models }).catch(console.error)
notifyThreadSubscribers({ models, item }).catch(console.error)
}
for (const { userId } of item.mentions) {
notifyMention({ models, item, userId }).catch(console.error)

View File

@ -202,6 +202,44 @@ export const notifyTerritorySubscribers = async ({ models, item }) => {
}
}
export const notifyThreadSubscribers = async ({ models, item }) => {
try {
const author = await models.user.findUnique({ where: { id: item.userId } })
const subscribers = await models.$queryRaw`
SELECT DISTINCT "ThreadSubscription"."userId" FROM "ThreadSubscription"
JOIN users ON users.id = "ThreadSubscription"."userId"
JOIN "Reply" r ON "ThreadSubscription"."itemId" = r."ancestorId"
WHERE r."itemId" = ${item.id}
-- don't send notifications for own items
AND r."userId" <> "ThreadSubscription"."userId"
-- send notifications for all levels?
AND CASE WHEN users."noteAllDescendants" THEN TRUE ELSE r.level = 1 END
-- muted?
AND NOT EXISTS (SELECT 1 FROM "Mute" m WHERE m."muterId" = users.id AND m."mutedId" = r."userId")
-- already received notification as reply to self?
AND NOT EXISTS (
SELECT 1 FROM "Item" i
JOIN "Item" p ON p.path @> i.path
WHERE i.id = ${item.parentId} AND p."userId" = "ThreadSubscription"."userId" AND users."noteAllDescendants"
)`
await Promise.allSettled(subscribers.map(({ userId }) =>
sendUserNotification(userId, {
// we reuse the same payload as for user subscriptions because they use the same title+body we want to use here
// so we should also merge them together (= same tag+data) to avoid confusion
title: `@${author.name} replied to a post`,
body: item.text,
item,
data: { followeeName: author.name, subType: 'COMMENT' },
tag: `FOLLOW-${author.id}-COMMENT`
})
))
} catch (err) {
console.error(err)
}
}
export const notifyItemParents = async ({ models, item }) => {
try {
const user = await models.user.findUnique({ where: { id: item.userId } })