import { useEffect, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; import { Layout } from '@/components/Layout'; import { ImageWithFallback } from '@/components/ImageWithFallback'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Link } from 'react-router-dom'; import { Skeleton } from '@/components/ui/skeleton'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@/components/ui/dialog'; import { Zap, MapPin, Shield, Layers, Crown, Radio, ArrowRight, Share2, CheckCircle2, Users, Store, Calendar, GraduationCap, Route, Eye, Send, } from 'lucide-react'; import { useAuthor } from '@/hooks/useAuthor'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useNostrPublish } from '@/hooks/useNostrPublish'; import { useGrowthStats } from '@/hooks/useGrowthStats'; import { useToast } from '@/hooks/useToast'; import { genUserName } from '@/lib/genUserName'; import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'; import { NewsletterSubscribeButton } from '@/components/NewsletterSubscribeButton'; import { NewsletterComposer } from '@/components/NewsletterComposer'; import { NewsletterBackfillPanel } from '@/components/NewsletterBackfillPanel'; import { useNewsletterIssues } from '@/hooks/useNewsletterIssues'; import { useNewsletterSubscriberCount } from '@/hooks/useNewsletterSubscriberCount'; import { useNewsletterRead } from '@/hooks/useNewsletterRead'; import { NEWSLETTER_KIND, type SkateNewsletterIssue } from '@/lib/types'; // ─── Admin pubkeys allowed to publish newsletter as kind 1 ─────────────────── import { isAdminPubkey } from '@/lib/admins'; // ─── Newsletter issue structure ─────────────────────────────────────────────── // import { NEWSLETTER_ISSUES, type NewsletterUpdate, type SpotOfTheWeek, type NewsletterIssue } from '@/lib/newsletterIssues'; // Preserve the previous public surface for existing importers. export { NEWSLETTER_ISSUES } from '@/lib/newsletterIssues'; export type { NewsletterUpdate, SpotOfTheWeek, NewsletterIssue } from '@/lib/newsletterIssues'; // ─── Nostr post preview + publish ──────────────────────────────────────────── function NostrPreviewModal({ issue, open, onClose, onConfirm, isPending, }: { issue: NewsletterIssue; open: boolean; onClose: () => void; onConfirm: (text: string) => void; isPending: boolean; }) { const { user } = useCurrentUser(); const author = useAuthor(user?.pubkey ?? ''); const metadata = author.data?.metadata; const displayName = metadata?.name ?? genUserName(user?.pubkey ?? ''); const avatar = metadata?.picture; const now = new Date(); const timeStr = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); const [editing, setEditing] = useState(false); const [editedText, setEditedText] = useState(issue.nostrText); const currentText = editing ? editedText : issue.nostrText; const renderPreviewText = (text: string) => { return text.split('\n').map((line, i) => { const parts = line.split(/(\s+)/).map((word, j) => { if (word.startsWith('#')) return {word}; if (word.startsWith('https://')) return {word}; return {word}; }); return

{parts}

; }); }; return ( {editing ? 'Edit' : 'Preview'} — Drop #{String(issue.number).padStart(3, '0')} {editing ? 'Edit the note text below. Changes only affect this publish — the source file stays unchanged.' : 'This is how the note will appear on most Nostr clients. Formatting varies by client.'} {editing ? ( /* ── Edit mode ── */