Skip to content

Commit

Permalink
Fix notifications without pending retries missing if no send wallets
Browse files Browse the repository at this point in the history
If a stacker has no send wallets, they would miss notifications about failed payments because they would never get retried.

This commit fixes this by making the notifications query aware if the stacker has send wallets. This way, it can tell if a notification will be retried or not.
  • Loading branch information
ekzyis committed Feb 12, 2025
1 parent a25242e commit 4a4658d
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 13 deletions.
7 changes: 4 additions & 3 deletions api/resolvers/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,10 @@ export default {
WHERE "Invoice"."userId" = $1
AND "Invoice"."updated_at" < $2
AND "Invoice"."actionState" = 'FAILED'
-- we want to show failed payments for posts in /notifications immediately and not wait for retries.
-- also, retries would never happen if the user has no wallet attached.
AND ("Invoice"."paymentAttempt" >= ${WALLET_MAX_RETRIES} OR "Invoice"."actionType" = 'ITEM_CREATE')
-- we want to show notifications only if no more automated retries will be attempted.
-- automated retries depend on if the user has wallets or not.
-- failed posts are an exception where we want to show them immediately and thus never automatically retry.
${meFull.sendWallets ? `AND ("Invoice"."paymentAttempt" >= ${WALLET_MAX_RETRIES} OR "Invoice"."actionType" = 'ITEM_CREATE')` : ''}
AND (
"Invoice"."actionType" = 'ITEM_CREATE' OR
"Invoice"."actionType" = 'ZAP' OR
Expand Down
31 changes: 22 additions & 9 deletions api/resolvers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,16 +544,21 @@ export default {
in: INVOICE_ACTION_NOTIFICATION_TYPES
},
actionState: 'FAILED',
OR: [
{
paymentAttempt: {
gte: WALLET_MAX_RETRIES
...(user.sendWallets
? {
OR: [
{
paymentAttempt: {
gte: WALLET_MAX_RETRIES
}
},
{
actionType: 'ITEM_CREATE'
}
]
}
},
{
actionType: 'ITEM_CREATE'
}
]
: {})

}
})

Expand Down Expand Up @@ -873,6 +878,14 @@ export default {

await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
return true
},
setSendWallets: async (parent, { sendWallets }, { me, models }) => {
if (!me) {
throw new GqlAuthenticationError()
}

await models.user.update({ where: { id: me.id }, data: { sendWallets } })
return sendWallets
}
},

Expand Down
2 changes: 2 additions & 0 deletions api/resolvers/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ const resolvers = {
AND "cancelledAt" < now() - ${`${WALLET_RETRY_AFTER_MS} milliseconds`}::interval
AND "cancelledAt" > now() - ${`${WALLET_RETRY_BEFORE_MS} milliseconds`}::interval
AND "paymentAttempt" < ${WALLET_MAX_RETRIES}
-- never retry failed posts because we always immediately show them in notifications
AND "actionType" <> 'ITEM_CREATE'
ORDER BY id DESC`
}
},
Expand Down
1 change: 1 addition & 0 deletions api/typeDefs/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default gql`
generateApiKey(id: ID!): String
deleteApiKey(id: ID!): User
disableFreebies: Boolean
setSendWallets(sendWallets: Boolean!): Boolean
}
type User {
Expand Down
6 changes: 6 additions & 0 deletions fragments/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,9 @@ export const FAILED_INVOICES = gql`
}
}
`

export const SET_SEND_WALLETS = gql`
mutation SetSendWallets($sendWallets: Boolean!) {
setSendWallets(sendWallets: $sendWallets)
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
ALTER TABLE "Invoice" ADD COLUMN "paymentAttempt" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "Invoice" ADD COLUMN "retryPendingSince" TIMESTAMP(3);
CREATE INDEX "Invoice_cancelledAt_idx" ON "Invoice"("cancelledAt");

ALTER TABLE "users" ADD COLUMN "sendWallets" BOOLEAN NOT NULL DEFAULT false;
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ model User {
nostrCrossposting Boolean @default(false)
slashtagId String? @unique(map: "users.slashtagId_unique")
noteCowboyHat Boolean @default(true)
sendWallets Boolean @default(false)
streak Int?
gunStreak Int?
horseStreak Int?
Expand Down
11 changes: 10 additions & 1 deletion wallets/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMe } from '@/components/me'
import { FAILED_INVOICES, SET_WALLET_PRIORITY, WALLETS } from '@/fragments/wallet'
import { FAILED_INVOICES, SET_WALLET_PRIORITY, WALLETS, SET_SEND_WALLETS } from '@/fragments/wallet'
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
import { useApolloClient, useLazyQuery, useMutation, useQuery } from '@apollo/client'
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
Expand Down Expand Up @@ -235,6 +235,7 @@ function RetryHandler ({ children }) {
const waitForWalletPayment = useWalletPayment()
const invoiceHelper = useInvoice()
const [getFailedInvoices] = useLazyQuery(FAILED_INVOICES, { fetchPolicy: 'network-only', nextFetchPolicy: 'network-only' })
const [setSendWallets] = useMutation(SET_SEND_WALLETS)

const retry = useCallback(async (invoice) => {
const newInvoice = await invoiceHelper.retry({ ...invoice, newAttempt: true })
Expand Down Expand Up @@ -289,5 +290,13 @@ function RetryHandler ({ children }) {
return stopPolling
}, [wallets, getFailedInvoices, retry])

// save on server if user has send wallets so we can render notifications on the server
useEffect(() => {
setSendWallets({ variables: { sendWallets: wallets.length > 0 } })
.catch(err => {
console.error('setSendWallets mutation failed:', err)
})
}, [wallets.length])

return children
}

0 comments on commit 4a4658d

Please sign in to comment.