From c18de1ba1629006d4ce6fcdf0bad9026ea1f2dc9 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 4 Oct 2025 23:05:52 +0200 Subject: [PATCH] relay logic in threads --- .../DiscussionsPage/CreateThreadDialog.tsx | 125 ++++++++++++++++-- .../primary/DiscussionsPage/ThreadCard.tsx | 30 ++++- src/pages/primary/DiscussionsPage/index.tsx | 72 ++++++++-- 3 files changed, 202 insertions(+), 25 deletions(-) diff --git a/src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx b/src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx index 7612452..fa862d0 100644 --- a/src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx +++ b/src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx @@ -5,16 +5,37 @@ import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { Badge } from '@/components/ui/badge' -import { Hash, X, Users, Code, Coins, Newspaper, BookOpen, Scroll, Cpu, Trophy, Film, Heart, TrendingUp, Utensils, MapPin, Home, PawPrint, Shirt } from 'lucide-react' +import { Switch } from '@/components/ui/switch' +import { Slider } from '@/components/ui/slider' +import { Hash, X, Users, Code, Coins, Newspaper, BookOpen, Scroll, Cpu, Trophy, Film, Heart, TrendingUp, Utensils, MapPin, Home, PawPrint, Shirt, Image, Zap } from 'lucide-react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useNostr } from '@/providers/NostrProvider' import { TDraftEvent } from '@/types' import dayjs from 'dayjs' +// Utility functions for thread creation +function extractImagesFromContent(content: string): string[] { + const imageRegex = /(https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|webp|svg)(\?[^\s]*)?)/gi + return content.match(imageRegex) || [] +} + +function generateImetaTags(imageUrls: string[]): string[][] { + return imageUrls.map(url => ['imeta', 'url', url]) +} + +function buildNsfwTag(): string[] { + return ['content-warning', ''] +} + +function buildClientTag(): string[] { + return ['client', 'jumble'] +} + interface CreateThreadDialogProps { topic: string availableRelays: string[] + selectedRelay?: string | null onClose: () => void onThreadCreated: () => void } @@ -42,6 +63,7 @@ export const DISCUSSION_TOPICS = [ export default function CreateThreadDialog({ topic: initialTopic, availableRelays, + selectedRelay: initialRelay, onClose, onThreadCreated }: CreateThreadDialogProps) { @@ -50,9 +72,12 @@ export default function CreateThreadDialog({ const [title, setTitle] = useState('') const [content, setContent] = useState('') const [selectedTopic] = useState(initialTopic) - const [selectedRelay, setSelectedRelay] = useState('') + const [selectedRelay, setSelectedRelay] = useState(initialRelay || '') const [isSubmitting, setIsSubmitting] = useState(false) const [errors, setErrors] = useState<{ title?: string; content?: string; relay?: string }>({}) + const [isNsfw, setIsNsfw] = useState(false) + const [addClientTag, setAddClientTag] = useState(true) + const [minPow, setMinPow] = useState(0) const validateForm = () => { const newErrors: { title?: string; content?: string; relay?: string } = {} @@ -92,21 +117,43 @@ export default function CreateThreadDialog({ setIsSubmitting(true) try { + // Extract images from content + const images = extractImagesFromContent(content.trim()) + + // Build tags array + const tags = [ + ['title', title.trim()], + ['t', selectedTopic], + ['-'] // Required tag for relay privacy + ] + + // Add image metadata tags if images are found + if (images && images.length > 0) { + tags.push(...generateImetaTags(images)) + } + + // Add NSFW tag if enabled + if (isNsfw) { + tags.push(buildNsfwTag()) + } + + // Add client tag if enabled + if (addClientTag) { + tags.push(buildClientTag()) + } + // Create the thread event (kind 11) const threadEvent: TDraftEvent = { kind: 11, content: content.trim(), - tags: [ - ['title', title.trim()], - ['t', selectedTopic], - ['-'] // Required tag for relay privacy - ], + tags, created_at: dayjs().unix() } // Publish to the selected relay only const publishedEvent = await publish(threadEvent, { - specifiedRelayUrls: [selectedRelay] + specifiedRelayUrls: [selectedRelay], + minPow }) if (publishedEvent) { @@ -218,6 +265,68 @@ export default function CreateThreadDialog({

+ {/* Advanced Options */} +
+

{t('Advanced Options')}

+ + {/* NSFW Toggle */} +
+
+ + +
+ +
+ + {/* Client Tag Toggle */} +
+
+ + +
+ +
+ + {/* PoW Setting */} +
+
+ + +
+
+ setMinPow(value[0])} + max={20} + min={0} + step={1} + className="w-full" + /> +
+ {t('No PoW')} + {t('High PoW')} +
+
+

+ {t('Higher values make your thread harder to mine but more unique.')} +

+
+
+ {/* Form Actions */}