notify user when invoice is paid
This commit is contained in:
		
							parent
							
								
									69155139e6
								
							
						
					
					
						commit
						987a5ed3a3
					
				@ -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({
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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')
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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 }) {
 | 
			
		||||
              </div>
 | 
			
		||||
            </>
 | 
			
		||||
            )
 | 
			
		||||
          : (
 | 
			
		||||
            <>
 | 
			
		||||
              {n.__typename === 'Votification' &&
 | 
			
		||||
                <small className='font-weight-bold text-success ml-2'>
 | 
			
		||||
                  your {n.item.title ? 'post' : 'reply'} stacked {n.earnedSats} sats
 | 
			
		||||
                </small>}
 | 
			
		||||
              {n.__typename === 'Mention' &&
 | 
			
		||||
                <small className='font-weight-bold text-info ml-2'>
 | 
			
		||||
                  you were mentioned in
 | 
			
		||||
                </small>}
 | 
			
		||||
              {n.__typename === 'JobChanged' &&
 | 
			
		||||
                <small className={`font-weight-bold text-${n.item.status === 'NOSATS' ? 'danger' : 'success'} ml-1`}>
 | 
			
		||||
                  {n.item.status === 'NOSATS'
 | 
			
		||||
                    ? 'your job ran out of sats'
 | 
			
		||||
                    : 'your job is active again'}
 | 
			
		||||
                </small>}
 | 
			
		||||
              <div className={n.__typename === 'Votification' || n.__typename === 'Mention' || n.__typename === 'JobChanged' ? '' : 'py-2'}>
 | 
			
		||||
                {n.item.maxBid
 | 
			
		||||
                  ? <ItemJob item={n.item} />
 | 
			
		||||
                  : n.item.title
 | 
			
		||||
                    ? <Item item={n.item} />
 | 
			
		||||
                    : (
 | 
			
		||||
                      <div className='pb-2'>
 | 
			
		||||
                        <Comment item={n.item} noReply includeParent rootText={n.__typename === 'Reply' ? 'replying on:' : undefined} clickToContext />
 | 
			
		||||
                      </div>)}
 | 
			
		||||
              </div>
 | 
			
		||||
            </>)}
 | 
			
		||||
          : n.__typename === 'InvoicePaid'
 | 
			
		||||
            ? <div className='font-weight-bold text-info ml-2 py-1'><Check className='fill-info mr-1' />{n.earnedSats} sats were deposited in your account</div>
 | 
			
		||||
            : (
 | 
			
		||||
              <>
 | 
			
		||||
                {n.__typename === 'Votification' &&
 | 
			
		||||
                  <small className='font-weight-bold text-success ml-2'>
 | 
			
		||||
                    your {n.item.title ? 'post' : 'reply'} stacked {n.earnedSats} sats
 | 
			
		||||
                  </small>}
 | 
			
		||||
                {n.__typename === 'Mention' &&
 | 
			
		||||
                  <small className='font-weight-bold text-info ml-2'>
 | 
			
		||||
                    you were mentioned in
 | 
			
		||||
                  </small>}
 | 
			
		||||
                {n.__typename === 'JobChanged' &&
 | 
			
		||||
                  <small className={`font-weight-bold text-${n.item.status === 'NOSATS' ? 'danger' : 'success'} ml-1`}>
 | 
			
		||||
                    {n.item.status === 'NOSATS'
 | 
			
		||||
                      ? 'your job ran out of sats'
 | 
			
		||||
                      : 'your job is active again'}
 | 
			
		||||
                  </small>}
 | 
			
		||||
                <div className={n.__typename === 'Votification' || n.__typename === 'Mention' || n.__typename === 'JobChanged' ? '' : 'py-2'}>
 | 
			
		||||
                  {n.item.maxBid
 | 
			
		||||
                    ? <ItemJob item={n.item} />
 | 
			
		||||
                    : n.item.title
 | 
			
		||||
                      ? <Item item={n.item} />
 | 
			
		||||
                      : (
 | 
			
		||||
                        <div className='pb-2'>
 | 
			
		||||
                          <Comment item={n.item} noReply includeParent rootText={n.__typename === 'Reply' ? 'replying on:' : undefined} clickToContext />
 | 
			
		||||
                        </div>)}
 | 
			
		||||
                </div>
 | 
			
		||||
              </>)}
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,13 @@ export const NOTIFICATIONS = gql`
 | 
			
		||||
          sortTime
 | 
			
		||||
          earnedSats
 | 
			
		||||
        }
 | 
			
		||||
        ... on InvoicePaid {
 | 
			
		||||
          sortTime
 | 
			
		||||
          earnedSats
 | 
			
		||||
          invoice {
 | 
			
		||||
            id
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } `
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user