/** * Query the unseen notification count for a DID. * Used to set the badge number on push notifications. */ function blockMuteFilter(didCol = "did"): string { return ` AND ${didCol} NOT IN (SELECT subject FROM "social.grain.graph.block" WHERE did = $1) AND ${didCol} NOT IN (SELECT did FROM "social.grain.graph.block" WHERE subject = $1) AND ${didCol} NOT IN (SELECT subject FROM _mutes WHERE did = $1) AND ${didCol} NOT IN (SELECT did FROM _repos WHERE status IN ('takendown', 'deactivated', 'deleted')) `; } export async function getUnseenCount( db: { query: (sql: string, params?: unknown[]) => Promise }, did: string, ): Promise { const prefRows = (await db.query( `SELECT value FROM _preferences WHERE did = $1 AND key = 'lastSeenNotifications'`, [did], )) as { value: string }[]; let lastSeen: string | null = null; if (prefRows[0]) { try { lastSeen = typeof prefRows[0].value === "string" ? JSON.parse(prefRows[0].value) : prefRows[0].value; } catch {} } const timeFilter = lastSeen ? `AND created_at > $2` : ""; const params = lastSeen ? [did, lastSeen] : [did]; const rows = (await db.query( `SELECT count(*) as cnt FROM ( SELECT uri FROM "social.grain.favorite" WHERE subject IN (SELECT uri FROM "social.grain.gallery" WHERE did = $1) AND did != $1 ${blockMuteFilter()} ${timeFilter} UNION ALL SELECT uri FROM "social.grain.comment" WHERE subject IN (SELECT uri FROM "social.grain.gallery" WHERE did = $1) AND did != $1 AND reply_to IS NULL ${blockMuteFilter()} ${timeFilter} UNION ALL SELECT c.uri FROM "social.grain.comment" c WHERE c.reply_to IN (SELECT uri FROM "social.grain.comment" WHERE did = $1) AND c.did != $1 ${blockMuteFilter("c.did")} ${timeFilter} UNION ALL SELECT uri FROM "social.grain.graph.follow" WHERE subject = $1 AND did != $1 ${blockMuteFilter()} ${timeFilter} UNION ALL SELECT uri FROM "social.grain.favorite" WHERE subject IN (SELECT uri FROM "social.grain.story" WHERE did = $1) AND did != $1 ${blockMuteFilter()} ${timeFilter} UNION ALL SELECT uri FROM "social.grain.comment" WHERE subject IN (SELECT uri FROM "social.grain.story" WHERE did = $1) AND did != $1 AND reply_to IS NULL ${blockMuteFilter()} ${timeFilter} )`, params, )) as { cnt: number }[]; return rows[0]?.cnt ?? 0; }