Notifications for when you are forwarded sats from a post (#467)

* Notifications for when you are forwarded sats from a post

Support notifications when a post for which you are forwarded gets zapped

This is controlled by a new boolean flag in user settings

* Send push notifications to forwarded users when they get forwarded sats

* Add `Promise.allSettled` per PR feedback

* Remove `FEE` act type when building forwarded zaps notifications

Don't include `FEE` actions, only `TIP` actions to avoid "0 sats forwarded" notifications

---------

Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com>
This commit is contained in:
SatsAllDay 2023-09-12 11:31:46 -04:00 committed by GitHub
parent 9064224fd3
commit 94fbabcdf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 108 additions and 5 deletions

View File

@ -812,6 +812,16 @@ export default {
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)
}

View File

@ -176,6 +176,23 @@ export default {
)
}
if (meFull.noteForwardedSats) {
queries.push(
`(SELECT "Item".id::TEXT, MAX("ItemAct".created_at) AS "sortTime",
MAX("Item".msats / 1000 * "ItemForward".pct / 100) as "earnedSats", 'ForwardedVotification' AS type
FROM "Item"
JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id
JOIN "ItemForward" ON "ItemForward"."itemId" = "Item".id AND "ItemForward"."userId" = $1
WHERE "ItemAct"."userId" <> $1
AND "Item"."userId" <> $1
AND "ItemAct".created_at <= $2
AND "ItemAct".act IN ('TIP')
GROUP BY "Item".id
ORDER BY "sortTime" DESC
LIMIT ${LIMIT}+$3)`
)
}
if (meFull.noteDeposits) {
queries.push(
`(SELECT "Invoice".id::text, "Invoice"."confirmedAt" AS "sortTime", FLOOR("msatsReceived" / 1000) as "earnedSats",
@ -310,6 +327,9 @@ export default {
Votification: {
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
},
ForwardedVotification: {
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
},
Reply: {
item: async (n, args, { models, me }) => getItem(n, { id: n.id }, { models, me })
},

View File

@ -344,6 +344,25 @@ export default {
}
}
if (user.noteForwardedSats) {
const votes = await models.$queryRawUnsafe(`
SELECT 1
FROM "Item"
JOIN "ItemAct" ON
"ItemAct"."itemId" = "Item".id
AND "ItemAct"."userId" <> "Item"."userId"
JOIN "ItemForward" ON
"ItemForward"."itemId" = "Item".id
AND "ItemForward"."userId" = $1
WHERE "ItemAct".created_at > $2
AND "Item"."userId" <> $1
AND "ItemAct".act = 'TIP'
LIMIT 1`, me.id, lastChecked)
if (votes.length > 0) {
return true
}
}
const job = await models.item.findFirst({
where: {
maxBid: {

View File

@ -17,6 +17,13 @@ export default gql`
sortTime: Date!
}
type ForwardedVotification {
id: ID!
earnedSats: Int!
item: Item!
sortTime: Date!
}
type FollowActivity {
id: ID!
item: Item!
@ -84,7 +91,7 @@ export default gql`
union Notification = Reply | Votification | Mention
| Invitification | Earn | JobChanged | InvoicePaid | Referral
| Streak | FollowActivity
| Streak | FollowActivity | ForwardedVotification
type Notifications {
lastChecked: Date

View File

@ -24,7 +24,8 @@ export default gql`
noteEarning: Boolean!, noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!,
noteInvites: Boolean!, noteJobIndicator: Boolean!, noteCowboyHat: Boolean!, hideInvoiceDesc: Boolean!,
hideFromTopUsers: Boolean!, hideCowboyHat: Boolean!, clickToLoadImg: Boolean!,
wildWestMode: Boolean!, greeterMode: Boolean!, nostrPubkey: String, nostrRelays: [String!], hideBookmarks: Boolean!): User
wildWestMode: Boolean!, greeterMode: Boolean!, nostrPubkey: String, nostrRelays: [String!], hideBookmarks: Boolean!,
noteForwardedSats: Boolean!): User
setPhoto(photoId: ID!): Int!
upsertBio(bio: String!): User!
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
@ -78,6 +79,7 @@ export default gql`
noteInvites: Boolean!
noteJobIndicator: Boolean!
noteCowboyHat: Boolean!
noteForwardedSats: Boolean!
hideInvoiceDesc: Boolean!
hideFromTopUsers: Boolean!
hideCowboyHat: Boolean!

View File

@ -36,7 +36,8 @@ const createUserFilter = (tag) => {
const tagMap = {
REPLY: 'noteAllDescendants',
MENTION: 'noteMentions',
TIP: 'noteItemSats'
TIP: 'noteItemSats',
FORWARDEDTIP: 'noteForwardedSats'
}
const key = tagMap[tag.split('-')[0]]
return key ? { user: { [key]: true } } : undefined

View File

@ -38,6 +38,7 @@ function Notification ({ n, fresh }) {
(type === 'Referral' && <Referral n={n} />) ||
(type === 'Streak' && <Streak n={n} />) ||
(type === 'Votification' && <Votification n={n} />) ||
(type === 'ForwardedVotification' && <ForwardedVotification n={n} />) ||
(type === 'Mention' && <Mention n={n} />) ||
(type === 'JobChanged' && <JobChanged n={n} />) ||
(type === 'Reply' && <Reply n={n} />) ||
@ -291,6 +292,27 @@ function Votification ({ n }) {
)
}
function ForwardedVotification ({ n }) {
return (
<>
<small className='fw-bold text-success d-inline-block ms-2 my-1' style={{ lineHeight: '1.25' }}>
you were forwarded {numWithUnits(n.earnedSats, { abbreviate: false })} from
</small>
<div>
{n.item.title
? <Item item={n.item} />
: (
<div className='pb-2'>
<RootProvider root={n.item.root}>
<Comment item={n.item} noReply includeParent clickToContext />
</RootProvider>
</div>
)}
</div>
</>
)
}
function Mention ({ n }) {
return (
<>

View File

@ -32,6 +32,15 @@ export const NOTIFICATIONS = gql`
text
}
}
... on ForwardedVotification {
id
sortTime
earnedSats
item {
...ItemFullFields
text
}
}
... on Streak {
id
sortTime

View File

@ -26,6 +26,7 @@ export const ME = gql`
noteInvites
noteJobIndicator
noteCowboyHat
noteForwardedSats
hideInvoiceDesc
hideFromTopUsers
hideCowboyHat
@ -50,6 +51,7 @@ export const SETTINGS_FIELDS = gql`
noteInvites
noteJobIndicator
noteCowboyHat
noteForwardedSats
hideInvoiceDesc
hideFromTopUsers
hideCowboyHat
@ -83,13 +85,15 @@ mutation setSettings($tipDefault: Int!, $turboTipping: Boolean!, $fiatCurrency:
$noteEarning: Boolean!, $noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!,
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $noteCowboyHat: Boolean!, $hideInvoiceDesc: Boolean!,
$hideFromTopUsers: Boolean!, $hideCowboyHat: Boolean!, $clickToLoadImg: Boolean!,
$wildWestMode: Boolean!, $greeterMode: Boolean!, $nostrPubkey: String, $nostrRelays: [String!], $hideBookmarks: Boolean!) {
$wildWestMode: Boolean!, $greeterMode: Boolean!, $nostrPubkey: String, $nostrRelays: [String!], $hideBookmarks: Boolean!,
$noteForwardedSats: Boolean!) {
setSettings(tipDefault: $tipDefault, turboTipping: $turboTipping, fiatCurrency: $fiatCurrency,
noteItemSats: $noteItemSats, noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
noteJobIndicator: $noteJobIndicator, noteCowboyHat: $noteCowboyHat, hideInvoiceDesc: $hideInvoiceDesc,
hideFromTopUsers: $hideFromTopUsers, hideCowboyHat: $hideCowboyHat, clickToLoadImg: $clickToLoadImg,
wildWestMode: $wildWestMode, greeterMode: $greeterMode, nostrPubkey: $nostrPubkey, nostrRelays: $nostrRelays, hideBookmarks: $hideBookmarks) {
wildWestMode: $wildWestMode, greeterMode: $greeterMode, nostrPubkey: $nostrPubkey, nostrRelays: $nostrRelays, hideBookmarks: $hideBookmarks,
noteForwardedSats: $noteForwardedSats) {
...SettingsFields
}
}

View File

@ -67,6 +67,7 @@ export default function Settings ({ ssrData }) {
noteInvites: settings?.noteInvites,
noteJobIndicator: settings?.noteJobIndicator,
noteCowboyHat: settings?.noteCowboyHat,
noteForwardedSats: settings?.noteForwardedSats,
hideInvoiceDesc: settings?.hideInvoiceDesc,
hideFromTopUsers: settings?.hideFromTopUsers,
hideCowboyHat: settings?.hideCowboyHat,
@ -160,6 +161,11 @@ export default function Settings ({ ssrData }) {
name='noteItemSats'
groupClassName='mb-0'
/>
<Checkbox
label='I get forwarded sats from a post'
name='noteForwardedSats'
groupClassName='mb-0'
/>
<Checkbox
label='I get a daily airdrop'
name='noteEarning'

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "noteForwardedSats" BOOLEAN NOT NULL DEFAULT true;

View File

@ -39,6 +39,7 @@ model User {
noteInvites Boolean @default(true)
noteItemSats Boolean @default(true)
noteMentions Boolean @default(true)
noteForwardedSats Boolean @default(true)
lastCheckedJobs DateTime?
noteJobIndicator Boolean @default(true)
photoId Int?