From 3db38eed30e2e06e524bd44f7bc3543924cc3419 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 23 May 2026 10:03:09 +0200 Subject: [PATCH] Implement superchats --- .../AdvancedEventLabTagsEditor.tsx | 116 ++++----- src/components/ContentPreview/index.tsx | 11 +- src/components/EventPowLabel/index.tsx | 36 +++ src/components/Note/Superchat.tsx | 153 ++++++++++++ .../Note/SuperchatPaymentMethodLabel.tsx | 45 ++++ src/components/Note/UnknownNote.tsx | 10 +- src/components/Note/Zap.tsx | 10 +- src/components/Note/index.tsx | 9 +- src/components/NoteStats/ZapButton.tsx | 32 ++- .../PaymentMethodsSection/index.tsx | 12 +- .../PaytoDialog/LightningInvoiceSection.tsx | 22 +- src/components/PaytoDialog/index.tsx | 107 ++++++-- src/components/PaytoLink/index.tsx | 12 +- src/components/Profile/ProfileBadges.tsx | 68 +++--- .../Profile/ProfileWallSuperchats.tsx | 37 +++ src/components/ReplyNote/index.tsx | 27 ++- src/components/ReplyNoteList/index.tsx | 134 ++++++---- .../ZapDialog/PostPaymentMessagePrompt.tsx | 182 ++++++++++++++ ...essagePrompt.tsx => PublicMessageForm.tsx} | 123 ++-------- .../ZapDialog/SuperchatRequestForm.tsx | 129 ++++++++++ src/components/ZapDialog/index.tsx | 130 +++++----- src/constants.ts | 4 + src/hooks/useProfileWall.tsx | 42 +++- src/i18n/locales/en.ts | 32 +++ src/lib/composer-extra-tags.test.ts | 23 +- src/lib/composer-extra-tags.ts | 29 ++- src/lib/draft-event.ts | 42 ++++ src/lib/event-pow.test.ts | 76 ++++++ src/lib/event-pow.ts | 34 +++ src/lib/event.ts | 8 +- src/lib/kind-description.ts | 4 + src/lib/note-renderable-kinds.ts | 1 + src/lib/payto.ts | 13 + src/lib/post-payment-context.ts | 55 +++++ src/lib/superchat.test.ts | 229 ++++++++++++++++++ src/lib/superchat.ts | 212 ++++++++++++++++ src/lib/thread-interaction-req.ts | 6 +- .../IncludePublicZapReceiptSwitch.tsx | 25 -- .../WalletPage/WalletZapSendingSettings.tsx | 5 +- src/services/lightning.service.ts | 45 ++-- 40 files changed, 1858 insertions(+), 432 deletions(-) create mode 100644 src/components/EventPowLabel/index.tsx create mode 100644 src/components/Note/Superchat.tsx create mode 100644 src/components/Note/SuperchatPaymentMethodLabel.tsx create mode 100644 src/components/Profile/ProfileWallSuperchats.tsx create mode 100644 src/components/ZapDialog/PostPaymentMessagePrompt.tsx rename src/components/ZapDialog/{TipPublicMessagePrompt.tsx => PublicMessageForm.tsx} (52%) create mode 100644 src/components/ZapDialog/SuperchatRequestForm.tsx create mode 100644 src/lib/event-pow.ts create mode 100644 src/lib/post-payment-context.ts create mode 100644 src/lib/superchat.test.ts create mode 100644 src/lib/superchat.ts delete mode 100644 src/pages/secondary/WalletPage/IncludePublicZapReceiptSwitch.tsx diff --git a/src/components/AdvancedEventLab/AdvancedEventLabTagsEditor.tsx b/src/components/AdvancedEventLab/AdvancedEventLabTagsEditor.tsx index 05d9471a..f896b41f 100644 --- a/src/components/AdvancedEventLab/AdvancedEventLabTagsEditor.tsx +++ b/src/components/AdvancedEventLab/AdvancedEventLabTagsEditor.tsx @@ -8,20 +8,20 @@ import { CollapsibleTrigger } from '@/components/ui/collapsible' import { - formatComposerTagValuesInput, + composerTagRowFromNostrTag, newComposerTagRow, normalizeComposerExtraTags, - parseComposerTagValuesInput, type ComposerExtraTagRow } from '@/lib/composer-extra-tags' import { cn } from '@/lib/utils' import { ChevronDown, Plus, Trash2 } from 'lucide-react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' export function labTagsToEditableRows(tags: string[][]): ComposerExtraTagRow[] { const normalized = tags.filter((t) => Array.isArray(t) && String(t[0] ?? '').trim()) if (!normalized.length) return [newComposerTagRow()] - return normalized.map((tag) => newComposerTagRow([...tag])) + return normalized.map((tag) => composerTagRowFromNostrTag([...tag])) } export function editableRowsToLabTags(rows: ComposerExtraTagRow[]): string[][] { @@ -38,21 +38,15 @@ export default function AdvancedEventLabTagsEditor({ className?: string }) { const { t } = useTranslation() + const filledCount = rows.filter((r) => r.name.trim()).length + const [open, setOpen] = useState(filledCount > 0) - const updateRow = (id: string, patch: Partial<{ name: string; valuesRaw: string }>) => { - onChange( - rows.map((row) => { - if (row.id !== id) return row - const name = patch.name !== undefined ? patch.name : (row.tag[0] ?? '') - const valuesRaw = - patch.valuesRaw !== undefined ? patch.valuesRaw : formatComposerTagValuesInput(row.tag) - const vals = parseComposerTagValuesInput(valuesRaw) - return { - ...row, - tag: name.trim() ? [name.trim(), ...vals] : vals.length ? ['', ...vals] : ['', ''] - } - }) - ) + useEffect(() => { + if (filledCount > 0) setOpen(true) + }, [filledCount]) + + const updateRow = (id: string, patch: Partial>) => { + onChange(rows.map((row) => (row.id === id ? { ...row, ...patch } : row))) } const removeRow = (id: string) => { @@ -62,15 +56,11 @@ export default function AdvancedEventLabTagsEditor({ const addRow = () => { onChange([...rows, newComposerTagRow()]) + setOpen(true) } - const filledCount = rows.filter((r) => (r.tag[0] ?? '').trim()).length - return ( - 0} - className={cn('rounded-lg border bg-muted/30', className)} - > + {t('advancedLabTagsTitle', { defaultValue: 'Event tags' })} @@ -89,49 +79,45 @@ export default function AdvancedEventLabTagsEditor({ })}

- {rows.map((row) => { - const name = row.tag[0] ?? '' - const valuesRaw = formatComposerTagValuesInput(row.tag) - return ( -
-
- - updateRow(row.id, { name: e.target.value })} - /> -
-
- -