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 ( + + {trigger} + e.preventDefault()}> + {content} + + + ) +} 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[] }