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 })}
- />
-
-
-
-
-
+ {rows.map((row) => (
+
+
+
+ updateRow(row.id, { name: e.target.value })}
+ />
- )
- })}
+
+
+
+
+
+ ))}
@@ -491,19 +503,7 @@ function ZapDialogContent({
className="space-y-3 border-t border-border bg-background px-4 pt-3"
style={{ paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))' }}
>
-
-
-
-
+ {t('Zap superchat flow hint')}