From 719cb2d50783495a72d32437a73c4e33c9f66f8e Mon Sep 17 00:00:00 2001 From: ekzyis Date: Tue, 15 Apr 2025 02:40:43 +0200 Subject: [PATCH] Prompt to attach receive wallet on post (#2059) Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> --- api/resolvers/user.js | 8 + api/typeDefs/user.js | 2 + components/use-item-submit.js | 11 +- fragments/users.js | 6 + .../migration.sql | 2 + prisma/schema.prisma | 1 + styles/wallet.module.css | 26 ++- wallets/prompt.js | 151 ++++++++++++++++++ 8 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20250402221806_hide_wallet_prompt/migration.sql create mode 100644 wallets/prompt.js 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 ( + <> +
+ +
or
+ +
or
+ +