diff --git a/src/components/Profile/index.tsx b/src/components/Profile/index.tsx
index e9bd9a2..18fd163 100644
--- a/src/components/Profile/index.tsx
+++ b/src/components/Profile/index.tsx
@@ -14,11 +14,12 @@ import { SecondaryPageLink, useSecondaryPage } from '@/PageManager'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
-import { Link, Zap } from 'lucide-react'
+import { Link, Zap, Bitcoin, Check, Copy } from 'lucide-react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import NotFound from '../NotFound'
import SearchInput from '../SearchInput'
+import SpQrCode from '../SpQrCode'
import TextWithEmojis from '../TextWithEmojis'
import TrustScoreBadge from '../TrustScoreBadge'
import AvatarWithLightbox from './AvatarWithLightbox'
@@ -112,7 +113,7 @@ export default function Profile({ id }: { id?: string }) {
}
if (!profile) return
- const { banner, username, about, pubkey, website, lightningAddress, emojis } = profile
+ const { banner, username, about, pubkey, website, lightningAddress, sp, emojis } = profile
return (
<>
@@ -160,6 +161,13 @@ export default function Profile({ id }: { id?: string }) {
{lightningAddress}
)}
+ {sp && (
+
+
+
+
+
+ )}
@@ -210,3 +218,24 @@ export default function Profile({ id }: { id?: string }) {
>
)
}
+
+function SpCopy({ sp }: { sp: string }) {
+ const [copied, setCopied] = useState(false)
+ const truncated = sp.length > 24 ? sp.slice(0, 12) + '...' + sp.slice(-6) : sp
+
+ const copy = () => {
+ navigator.clipboard.writeText(sp)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ }
+
+ return (
+
+
{truncated}
+ {copied ?
:
}
+
+ )
+}
diff --git a/src/components/SpQrCode/index.tsx b/src/components/SpQrCode/index.tsx
new file mode 100644
index 0000000..55a5635
--- /dev/null
+++ b/src/components/SpQrCode/index.tsx
@@ -0,0 +1,64 @@
+import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
+import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
+import { useScreenSize } from '@/providers/ScreenSizeProvider'
+import { Bitcoin, Check, Copy, QrCodeIcon } from 'lucide-react'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import QrCode from '../QrCode'
+
+export default function SpQrCode({ sp }: { sp: string }) {
+ const { t } = useTranslation()
+ const { isSmallScreen } = useScreenSize()
+ const [copied, setCopied] = useState(false)
+
+ if (!sp) return null
+
+ const truncated = sp.length > 24 ? sp.slice(0, 12) + '...' + sp.slice(-6) : sp
+
+ const copySp = () => {
+ navigator.clipboard.writeText(sp)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ }
+
+ const trigger = (
+
+
+
+ )
+
+ const content = (
+
+
+
+
{t('Silent Payment')}
+
+
+
+
{truncated}
+ {copied ?
:
}
+
+
+ )
+
+ if (isSmallScreen) {
+ return (
+
+ {trigger}
+ {content}
+
+ )
+ }
+
+ return (
+
+ )
+}
diff --git a/src/lib/event-metadata.ts b/src/lib/event-metadata.ts
index 969f26a..e85839b 100644
--- a/src/lib/event-metadata.ts
+++ b/src/lib/event-metadata.ts
@@ -74,6 +74,7 @@ export function getProfileFromEvent(event: Event) {
lud06: profileObj.lud06,
lud16: profileObj.lud16,
lightningAddress: getLightningAddressFromProfile(profileObj),
+ sp: profileObj.sp,
created_at: event.created_at,
emojis: emojis.length > 0 ? emojis : undefined
}
diff --git a/src/pages/secondary/ProfileEditorPage/index.tsx b/src/pages/secondary/ProfileEditorPage/index.tsx
index e39dd1a..3141ea4 100644
--- a/src/pages/secondary/ProfileEditorPage/index.tsx
+++ b/src/pages/secondary/ProfileEditorPage/index.tsx
@@ -30,6 +30,8 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
const [nip05Error, setNip05Error] = useState
('')
const [lightningAddress, setLightningAddress] = useState('')
const [lightningAddressError, setLightningAddressError] = useState('')
+ const [silentPaymentAddress, setSilentPaymentAddress] = useState('')
+ const [silentPaymentAddressError, setSilentPaymentAddressError] = useState('')
const [hasChanged, setHasChanged] = useState(false)
const [saving, setSaving] = useState(false)
const [uploadingBanner, setUploadingBanner] = useState(false)
@@ -48,6 +50,7 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
setWebsite(profile.website ?? '')
setNip05(profile.nip05 ?? '')
setLightningAddress(profile.lightningAddress || '')
+ setSilentPaymentAddress(profile.sp || '')
} else {
setBanner('')
setAvatar('')
@@ -56,6 +59,7 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
setWebsite('')
setNip05('')
setLightningAddress('')
+ setSilentPaymentAddress('')
}
}, [profile])
@@ -93,6 +97,17 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
delete newProfileContent.lud16
}
+ if (silentPaymentAddress) {
+ if (silentPaymentAddress.startsWith('sp1')) {
+ newProfileContent.sp = silentPaymentAddress
+ } else {
+ setSilentPaymentAddressError(t('Silent Payment address must start with sp1'))
+ return
+ }
+ } else {
+ delete newProfileContent.sp
+ }
+
setSaving(true)
setHasChanged(false)
const profileDraftEvent = createProfileDraftEvent(
@@ -228,6 +243,25 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
{lightningAddressError}
)}
+ -
+
+ {
+ setSilentPaymentAddressError('')
+ setSilentPaymentAddress(e.target.value)
+ setHasChanged(true)
+ }}
+ className={silentPaymentAddressError ? 'border-destructive' : ''}
+ />
+ {silentPaymentAddressError && (
+
{silentPaymentAddressError}
+ )}
+
)
diff --git a/src/types/index.d.ts b/src/types/index.d.ts
index f2f2d9d..1831554 100644
--- a/src/types/index.d.ts
+++ b/src/types/index.d.ts
@@ -27,6 +27,7 @@ export type TProfile = {
lud06?: string
lud16?: string
lightningAddress?: string
+ sp?: string
created_at?: number
emojis?: TEmoji[]
}