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 */}