diff --git a/api/resolvers/user.js b/api/resolvers/user.js
index 1ae8d256..7e255671 100644
--- a/api/resolvers/user.js
+++ b/api/resolvers/user.js
@@ -898,6 +898,14 @@ export default {
await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
return true
+ },
+ hideWalletRecvPrompt: async (parent, data, { me, models }) => {
+ if (!me) {
+ throw new GqlAuthenticationError()
+ }
+
+ await models.user.update({ where: { id: me.id }, data: { hideWalletRecvPrompt: true } })
+ return true
}
},
diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js
index 191e44a2..7cb4e560 100644
--- a/api/typeDefs/user.js
+++ b/api/typeDefs/user.js
@@ -38,6 +38,7 @@ export default gql`
unlinkAuth(authType: String!): AuthMethods!
linkUnverifiedEmail(email: String!): Boolean
hideWelcomeBanner: Boolean
+ hideWalletRecvPrompt: Boolean
subscribeUserPosts(id: ID): User
subscribeUserComments(id: ID): User
toggleMute(id: ID): User
@@ -141,6 +142,7 @@ export default gql`
"""
lastCheckedJobs: String
hideWelcomeBanner: Boolean!
+ hideWalletRecvPrompt: Boolean!
tipPopover: Boolean!
upvotePopover: Boolean!
hasInvites: Boolean!
diff --git a/components/use-item-submit.js b/components/use-item-submit.js
index 0135113e..cd6eb867 100644
--- a/components/use-item-submit.js
+++ b/components/use-item-submit.js
@@ -8,6 +8,7 @@ import { RETRY_PAID_ACTION } from '@/fragments/paidAction'
import gql from 'graphql-tag'
import { USER_ID } from '@/lib/constants'
import { useMe } from './me'
+import { useWalletRecvPrompt, WalletPromptClosed } from '@/wallets/prompt'
// this is intented to be compatible with upsert item mutations
// so that it can be reused for all post types and comments and we don't have
@@ -22,9 +23,17 @@ export default function useItemSubmit (mutation,
const crossposter = useCrossposter()
const [upsertItem] = usePaidMutation(mutation)
const { me } = useMe()
+ const walletPrompt = useWalletRecvPrompt()
return useCallback(
async ({ boost, crosspost, title, options, bounty, status, ...values }, { resetForm }) => {
+ try {
+ await walletPrompt()
+ } catch (err) {
+ if (err instanceof WalletPromptClosed) return
+ throw err
+ }
+
if (options) {
// remove existing poll options since else they will be appended as duplicates
options = options.slice(item?.poll?.options?.length || 0).filter(o => o.trim().length > 0)
@@ -93,7 +102,7 @@ export default function useItemSubmit (mutation,
}
}
}, [me, upsertItem, router, crossposter, item, sub, onSuccessfulSubmit,
- navigateOnSubmit, extraValues, paidMutationOptions]
+ navigateOnSubmit, extraValues, paidMutationOptions, walletPrompt]
)
}
diff --git a/fragments/users.js b/fragments/users.js
index e591cb2a..adabbe0f 100644
--- a/fragments/users.js
+++ b/fragments/users.js
@@ -34,6 +34,7 @@ ${STREAK_FIELDS}
hideFromTopUsers
hideWalletBalance
hideWelcomeBanner
+ hideWalletRecvPrompt
imgproxyOnly
showImagesAndVideos
nostrCrossposting
@@ -167,6 +168,11 @@ export const USER_SUGGESTIONS = gql`
}
}`
+export const HIDE_WALLET_RECV_PROMPT_MUTATION = gql`
+ mutation hideWalletRecvPrompt {
+ hideWalletRecvPrompt
+ }`
+
export const USER_SEARCH = gql`
${STREAK_FIELDS}
query searchUsers($q: String!, $limit: Limit, $similarity: Float) {
diff --git a/prisma/migrations/20250402221806_hide_wallet_prompt/migration.sql b/prisma/migrations/20250402221806_hide_wallet_prompt/migration.sql
new file mode 100644
index 00000000..dfd5d447
--- /dev/null
+++ b/prisma/migrations/20250402221806_hide_wallet_prompt/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "users" ADD COLUMN "hideWalletRecvPrompt" BOOLEAN NOT NULL DEFAULT false;
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 9daea9ee..afbf1d2a 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -116,6 +116,7 @@ model User {
followers UserSubscription[] @relation("follower")
followees UserSubscription[] @relation("followee")
hideWelcomeBanner Boolean @default(false)
+ hideWalletRecvPrompt Boolean @default(false)
diagnostics Boolean @default(false)
hideIsContributor Boolean @default(false)
lnAddr String?
diff --git a/styles/wallet.module.css b/styles/wallet.module.css
index 42c18909..8c2501f3 100644
--- a/styles/wallet.module.css
+++ b/styles/wallet.module.css
@@ -125,4 +125,28 @@
color: var(--theme-toolbarHover) !important;
background-color: var(--theme-toolbarHover) !important;
border: 1px solid var(--theme-toolbarActive);
-}
\ No newline at end of file
+}
+
+.separator {
+ display: flex;
+ align-items: center;
+ text-align: center;
+ color: var(--theme-grey);
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
+}
+
+.separator::before,
+.separator::after {
+ content: '';
+ flex: 1;
+ border-bottom: 1px solid var(--theme-grey);
+}
+
+.separator:not(:empty)::before {
+ margin-right: .25em;
+}
+
+.separator:not(:empty)::after {
+ margin-left: .25em;
+}
diff --git a/wallets/prompt.js b/wallets/prompt.js
new file mode 100644
index 00000000..cf62cfde
--- /dev/null
+++ b/wallets/prompt.js
@@ -0,0 +1,151 @@
+import { useCallback } from 'react'
+import { boolean, object } from 'yup'
+import { Button } from 'react-bootstrap'
+import { Form, ClientInput, SubmitButton, Checkbox } from '@/components/form'
+import { useMe } from '@/components/me'
+import { useShowModal } from '@/components/modal'
+import Link from 'next/link'
+import { useWallet } from '@/wallets/index'
+import { useWalletConfigurator } from '@/wallets/config'
+import styles from '@/styles/wallet.module.css'
+import { externalLightningAddressValidator } from '@/lib/validate'
+import { autowithdrawInitial } from '@/components/autowithdraw-shared'
+import { useMutation } from '@apollo/client'
+import { HIDE_WALLET_RECV_PROMPT_MUTATION } from '@/fragments/users'
+import { useToast } from '@/components/toast'
+
+export class WalletPromptClosed extends Error {
+ constructor () {
+ super('wallet prompt closed')
+ }
+}
+
+export function useWalletRecvPrompt () {
+ const { me } = useMe()
+ const showModal = useShowModal()
+ const toaster = useToast()
+
+ const onAttach = useCallback(({ onClose, resolve }) =>
+ () => {
+ toaster.success('lightning address saved', { persistOnNavigate: true })
+ resolve()
+ onClose()
+ }, [toaster])
+
+ const onSkip = useCallback(({ onClose, resolve }) =>
+ () => {
+ resolve()
+ onClose()
+ }, [])
+
+ return useCallback((e) => {
+ return new Promise((resolve, reject) => {
+ // TODO: check if user told us to not show again
+ if (!me || me.optional?.hasRecvWallet || me.privates?.hideWalletRecvPrompt) return resolve()
+
+ showModal(onClose => {
+ return (
+ <>
+