diff --git a/api/resolvers/notifications.js b/api/resolvers/notifications.js index 15a739e1..73fb55be 100644 --- a/api/resolvers/notifications.js +++ b/api/resolvers/notifications.js @@ -1,6 +1,7 @@ import { AuthenticationError } from 'apollo-server-micro' import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor' import { getItem } from './item' +import { getInvoice } from './wallet' export default { Query: { @@ -70,6 +71,7 @@ export default { // at most 25 ancesestors belonging to the same user for a given reply, see: (LIMIT+OFFSET)*25 which is // undoubtably the case today ... this probably won't hold indefinitely though // One other less HACKy way to do this is to store in each reply, an set of users it's in response to + // or to simply denormalize the replies (simply the ids which we wouldn't have to be concerned about consitency) const notifications = await models.$queryRaw(` SELECT DISTINCT * FROM @@ -128,8 +130,17 @@ export default { (SELECT "Earn".id::text, "Earn".created_at AS "sortTime", FLOOR(msats / 1000) as "earnedSats", 'Earn' AS type FROM "Earn" - WHERE "Earn"."userId" = $1 AND - FLOOR(msats / 1000) > 0 + WHERE "Earn"."userId" = $1 + AND FLOOR(msats / 1000) > 0 + AND created_at <= $2 + ORDER BY "sortTime" DESC + LIMIT ${LIMIT}+$3) + UNION ALL + (SELECT "Invoice".id::text, "Invoice"."confirmedAt" AS "sortTime", FLOOR("msatsReceived" / 1000) as "earnedSats", + 'InvoicePaid' AS type + FROM "Invoice" + WHERE "Invoice"."userId" = $1 + AND "confirmedAt" IS NOT NULL AND created_at <= $2 ORDER BY "sortTime" DESC LIMIT ${LIMIT}+$3)) AS n @@ -165,6 +176,9 @@ export default { mention: async (n, args, { models }) => true, item: async (n, args, { models }) => getItem(n, { id: n.id }, { models }) }, + InvoicePaid: { + invoice: async (n, args, { me, models }) => getInvoice(n, { id: n.id }, { me, models }) + }, Invitification: { invite: async (n, args, { models }) => { return await models.invite.findUnique({ diff --git a/api/resolvers/user.js b/api/resolvers/user.js index 9e566a52..020f0a1c 100644 --- a/api/resolvers/user.js +++ b/api/resolvers/user.js @@ -260,6 +260,18 @@ export default { return true } + const invoice = await models.invoice.findFirst({ + where: { + userId: user.id, + confirmedAt: { + gt: user.checkedNotesAt || new Date(0) + } + } + }) + if (invoice) { + return true + } + // check if new invites have been redeemed const newInvitees = await models.$queryRaw(` SELECT "Invite".id diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index bd2b700d..a9abc972 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -5,28 +5,30 @@ import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor' import lnpr from 'bolt11' import { SELECT } from './item' +export async function getInvoice (parent, { id }, { me, models }) { + if (!me) { + throw new AuthenticationError('you must be logged in') + } + + const inv = await models.invoice.findUnique({ + where: { + id: Number(id) + }, + include: { + user: true + } + }) + + if (inv.user.id !== me.id) { + throw new AuthenticationError('not ur invoice') + } + + return inv +} + export default { Query: { - invoice: async (parent, { id }, { me, models, lnd }) => { - if (!me) { - throw new AuthenticationError('you must be logged in') - } - - const inv = await models.invoice.findUnique({ - where: { - id: Number(id) - }, - include: { - user: true - } - }) - - if (inv.user.id !== me.id) { - throw new AuthenticationError('not ur invoice') - } - - return inv - }, + invoice: getInvoice, withdrawl: async (parent, { id }, { me, models, lnd }) => { if (!me) { throw new AuthenticationError('you must be logged in') diff --git a/api/typeDefs/notifications.js b/api/typeDefs/notifications.js index e7dba19c..65783c93 100644 --- a/api/typeDefs/notifications.js +++ b/api/typeDefs/notifications.js @@ -37,8 +37,14 @@ export default gql` sortTime: String! } + type InvoicePaid { + earnedSats: Int! + invoice: Invoice! + sortTime: String! + } + union Notification = Reply | Votification | Mention - | Invitification | JobChanged | Earn + | Invitification | JobChanged | Earn | InvoicePaid type Notifications { lastChecked: String diff --git a/components/notifications.js b/components/notifications.js index bbfbfd19..31dc79f6 100644 --- a/components/notifications.js +++ b/components/notifications.js @@ -7,7 +7,9 @@ import MoreFooter from './more-footer' import Invite from './invite' import { ignoreClick } from '../lib/clicks' import Link from 'next/link' +import Check from '../svgs/check-double-line.svg' +// TODO: oh man, this is a mess ... each notification type should just be a component ... function Notification ({ n }) { const router = useRouter() return ( @@ -22,7 +24,9 @@ function Notification ({ n }) { return } - if (n.__typename === 'Invitification') { + if (n.__typename === 'InvoicePaid') { + router.push(`/invoices/${n.invoice.id}`) + } else if (n.__typename === 'Invitification') { router.push('/invites') } else if (!n.item.title) { router.push({ @@ -64,33 +68,35 @@ function Notification ({ n }) { ) - : ( - <> - {n.__typename === 'Votification' && - - your {n.item.title ? 'post' : 'reply'} stacked {n.earnedSats} sats - } - {n.__typename === 'Mention' && - - you were mentioned in - } - {n.__typename === 'JobChanged' && - - {n.item.status === 'NOSATS' - ? 'your job ran out of sats' - : 'your job is active again'} - } -
- {n.item.maxBid - ? - : n.item.title - ? - : ( -
- -
)} -
- )} + : n.__typename === 'InvoicePaid' + ?
{n.earnedSats} sats were deposited in your account
+ : ( + <> + {n.__typename === 'Votification' && + + your {n.item.title ? 'post' : 'reply'} stacked {n.earnedSats} sats + } + {n.__typename === 'Mention' && + + you were mentioned in + } + {n.__typename === 'JobChanged' && + + {n.item.status === 'NOSATS' + ? 'your job ran out of sats' + : 'your job is active again'} + } +
+ {n.item.maxBid + ? + : n.item.title + ? + : ( +
+ +
)} +
+ )} ) } diff --git a/fragments/notifications.js b/fragments/notifications.js index 3ef3871a..f5c44f24 100644 --- a/fragments/notifications.js +++ b/fragments/notifications.js @@ -51,6 +51,13 @@ export const NOTIFICATIONS = gql` sortTime earnedSats } + ... on InvoicePaid { + sortTime + earnedSats + invoice { + id + } + } } } } ` diff --git a/styles/globals.scss b/styles/globals.scss index 4be6c498..581c1dba 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -380,11 +380,15 @@ textarea.form-control { } .fill-success { - fill: #5c8001; + fill: var(--success); +} + +.fill-info { + fill: var(--info); } .fill-danger { - fill: #c03221; + fill: var(--danger); } .fill-theme-color {