diff --git a/api/resolvers/notifications.js b/api/resolvers/notifications.js index 5fe0832d..52c5f84b 100644 --- a/api/resolvers/notifications.js +++ b/api/resolvers/notifications.js @@ -221,6 +221,19 @@ export default { ) } + if (meFull.noteWithdrawals) { + queries.push( + `(SELECT "Withdrawl".id::text, "Withdrawl".created_at AS "sortTime", FLOOR("msatsPaid" / 1000) as "earnedSats", + 'WithdrawlPaid' AS type + FROM "Withdrawl" + WHERE "Withdrawl"."userId" = $1 + AND status = 'CONFIRMED' + AND created_at < $2 + ORDER BY "sortTime" DESC + LIMIT ${LIMIT})` + ) + } + if (meFull.noteInvites) { queries.push( `(SELECT "Invite".id, MAX(users.created_at) AS "sortTime", NULL as "earnedSats", diff --git a/api/resolvers/user.js b/api/resolvers/user.js index 8032b99a..efbfe642 100644 --- a/api/resolvers/user.js +++ b/api/resolvers/user.js @@ -353,6 +353,22 @@ export default { } } + if (user.noteWithdrawals) { + const wdrwl = await models.withdrawl.findFirst({ + where: { + userId: me.id, + status: 'CONFIRMED', + updatedAt: { + gt: lastChecked + } + } + }) + if (wdrwl) { + foundNotes() + return true + } + } + // check if new invites have been redeemed if (user.noteInvites) { const [newInvites] = await models.$queryRawUnsafe(` diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index bfca346b..e10fe422 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -97,7 +97,7 @@ export default { } }) }, - withdrawl: async (parent, { id }, { me, models, lnd }) => { + withdrawl: async (parent, { id }, { me, models }) => { if (!me) { throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } }) } @@ -111,6 +111,10 @@ export default { } }) + if (!wdrwl) { + throw new GraphQLError('withdrawal not found', { extensions: { code: 'BAD_INPUT' } }) + } + if (wdrwl.user.id !== me.id) { throw new GraphQLError('not ur withdrawal', { extensions: { code: 'FORBIDDEN' } }) } diff --git a/api/typeDefs/notifications.js b/api/typeDefs/notifications.js index 07d94701..12c58acc 100644 --- a/api/typeDefs/notifications.js +++ b/api/typeDefs/notifications.js @@ -91,6 +91,12 @@ export default gql` sortTime: Date! } + type WithdrawlPaid { + id: ID! + earnedSats: Int! + sortTime: Date! + } + type Referral { id: ID! sortTime: Date! @@ -115,7 +121,7 @@ export default gql` } union Notification = Reply | Votification | Mention - | Invitification | Earn | JobChanged | InvoicePaid | Referral + | Invitification | Earn | JobChanged | InvoicePaid | WithdrawlPaid | Referral | Streak | FollowActivity | ForwardedVotification | Revenue | SubStatus | TerritoryPost | TerritoryTransfer diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js index 800eb648..2300fdc3 100644 --- a/api/typeDefs/user.js +++ b/api/typeDefs/user.js @@ -82,7 +82,8 @@ export default gql` nostrRelays: [String!] noteAllDescendants: Boolean! noteCowboyHat: Boolean! - noteDeposits: Boolean! + noteDeposits: Boolean!, + noteWithdrawals: Boolean!, noteEarning: Boolean! noteForwardedSats: Boolean! noteInvites: Boolean! @@ -148,6 +149,7 @@ export default gql` noteAllDescendants: Boolean! noteCowboyHat: Boolean! noteDeposits: Boolean! + noteWithdrawals: Boolean! noteEarning: Boolean! noteForwardedSats: Boolean! noteInvites: Boolean! diff --git a/components/notifications.js b/components/notifications.js index 9a9d3360..529d4bcf 100644 --- a/components/notifications.js +++ b/components/notifications.js @@ -40,6 +40,7 @@ function Notification ({ n, fresh }) { (type === 'Revenue' && ) || (type === 'Invitification' && ) || (type === 'InvoicePaid' && (n.invoice.nostr ? : )) || + (type === 'WithdrawlPaid' && ) || (type === 'Referral' && ) || (type === 'Streak' && ) || (type === 'Votification' && ) || @@ -95,6 +96,7 @@ const defaultOnClick = n => { if (type === 'SubStatus') return { href: `/~${n.sub.name}` } if (type === 'Invitification') return { href: '/invites' } if (type === 'InvoicePaid') return { href: `/invoices/${n.invoice.id}` } + if (type === 'WithdrawlPaid') return { href: `/withdrawals/${n.id}` } if (type === 'Referral') return { href: '/referrals/month' } if (type === 'Streak') return {} if (type === 'TerritoryTransfer') return { href: `/~${n.sub.name}` } @@ -277,6 +279,15 @@ function InvoicePaid ({ n }) { ) } +function WithdrawlPaid ({ n }) { + return ( +
+ {numWithUnits(n.earnedSats, { abbreviate: false, unitSingular: 'sat was', unitPlural: 'sats were' })} withdrawn from your account + {timeSince(new Date(n.sortTime))} +
+ ) +} + function Referral ({ n }) { return ( diff --git a/fragments/notifications.js b/fragments/notifications.js index 247506da..38bb0bab 100644 --- a/fragments/notifications.js +++ b/fragments/notifications.js @@ -133,6 +133,11 @@ export const NOTIFICATIONS = gql` lud18Data } } + ... on WithdrawlPaid { + id + sortTime + earnedSats + } } } } ` diff --git a/fragments/users.js b/fragments/users.js index f9225eca..8f94c10e 100644 --- a/fragments/users.js +++ b/fragments/users.js @@ -30,6 +30,7 @@ export const ME = gql` noteAllDescendants noteCowboyHat noteDeposits + noteWithdrawals noteEarning noteForwardedSats noteInvites @@ -72,6 +73,7 @@ export const SETTINGS_FIELDS = gql` noteAllDescendants noteMentions noteDeposits + noteWithdrawals noteInvites noteJobIndicator noteCowboyHat diff --git a/lib/webPush.js b/lib/webPush.js index c4daa44a..3140ac0c 100644 --- a/lib/webPush.js +++ b/lib/webPush.js @@ -43,6 +43,7 @@ const createUserFilter = (tag) => { INVITE: 'noteInvites', EARN: 'noteEarning', DEPOSIT: 'noteDeposits', + WITHDRAWAL: 'noteWithdrawals', STREAK: 'noteCowboyHat' } const key = tagMap[tag.split('-')[0]] @@ -307,7 +308,7 @@ export async function notifyEarner (userId, earnings) { export async function notifyDeposit (userId, invoice) { try { await sendUserNotification(userId, { - title: `${numWithUnits(msatsToSats(invoice.received_mtokens), { abbreviate: false })} were deposited in your account`, + title: `${numWithUnits(msatsToSats(invoice.received_mtokens), { abbreviate: false, unitSingular: 'sat was', unitPlural: 'sats were' })} deposited in your account`, body: invoice.comment || undefined, tag: 'DEPOSIT', data: { sats: msatsToSats(invoice.received_mtokens) } @@ -317,6 +318,18 @@ export async function notifyDeposit (userId, invoice) { } } +export async function notifyWithdrawal (userId, wdrwl) { + try { + await sendUserNotification(userId, { + title: `${numWithUnits(msatsToSats(wdrwl.payment.mtokens), { abbreviate: false, unitSingular: 'sat was', unitPlural: 'sats were' })} withdrawn from your account`, + tag: 'WITHDRAWAL', + data: { sats: msatsToSats(wdrwl.payment.mtokens) } + }) + } catch (err) { + console.error(err) + } +} + export async function notifyNewStreak (userId, streak) { const index = streak.id % FOUND_BLURBS.length const blurb = FOUND_BLURBS[index] diff --git a/pages/settings/index.js b/pages/settings/index.js index e67c6326..7de221e3 100644 --- a/pages/settings/index.js +++ b/pages/settings/index.js @@ -75,6 +75,7 @@ export default function Settings ({ ssrData }) { noteAllDescendants: settings?.noteAllDescendants, noteMentions: settings?.noteMentions, noteDeposits: settings?.noteDeposits, + noteWithdrawals: settings?.noteWithdrawals, noteInvites: settings?.noteInvites, noteJobIndicator: settings?.noteJobIndicator, noteCowboyHat: settings?.noteCowboyHat, @@ -223,6 +224,11 @@ export default function Settings ({ ssrData }) { name='noteDeposits' groupClassName='mb-0' /> +