Browse Source

cleaned up replies and mentions

imwald
Silberengel 5 months ago
parent
commit
7a4e270ff0
  1. 76
      src/components/PostEditor/PostContent.tsx
  2. 123
      src/components/PostEditor/PostRelaySelector.tsx
  3. 79
      src/components/RelayStatusDisplay/index.tsx
  4. 4
      src/lib/publishing-feedback.tsx
  5. 14
      src/providers/NostrProvider/index.tsx
  6. 76
      src/services/client.service.ts
  7. 1
      src/types/index.d.ts

76
src/components/PostEditor/PostContent.tsx

@ -59,7 +59,7 @@ export default function PostContent({ @@ -59,7 +59,7 @@ export default function PostContent({
const [isNsfw, setIsNsfw] = useState(false)
const [isPoll, setIsPoll] = useState(false)
const [isPublicMessage, setIsPublicMessage] = useState(false)
const [publicMessageRecipients, setPublicMessageRecipients] = useState<string[]>([])
const [extractedMentions, setExtractedMentions] = useState<string[]>([])
const [isProtectedEvent, setIsProtectedEvent] = useState(false)
const [additionalRelayUrls, setAdditionalRelayUrls] = useState<string[]>([])
const [isHighlight, setIsHighlight] = useState(false)
@ -82,7 +82,7 @@ export default function PostContent({ @@ -82,7 +82,7 @@ export default function PostContent({
!posting &&
!uploadProgresses.length &&
(!isPoll || pollCreateData.options.filter((option) => !!option.trim()).length >= 2) &&
(!isPublicMessage || publicMessageRecipients.length > 0 || parentEvent?.kind === ExtendedKind.PUBLIC_MESSAGE) &&
(!isPublicMessage || extractedMentions.length > 0 || parentEvent?.kind === ExtendedKind.PUBLIC_MESSAGE) &&
(!isProtectedEvent || additionalRelayUrls.length > 0) &&
(!isHighlight || highlightData.sourceValue.trim() !== '')
)
@ -96,7 +96,7 @@ export default function PostContent({ @@ -96,7 +96,7 @@ export default function PostContent({
isPoll,
pollCreateData,
isPublicMessage,
publicMessageRecipients,
extractedMentions,
parentEvent?.kind,
isProtectedEvent,
additionalRelayUrls,
@ -151,25 +151,20 @@ export default function PostContent({ @@ -151,25 +151,20 @@ export default function PostContent({
// For now, we'll use the nostr mentions and show that we detected @ mentions
// In a real implementation, you'd resolve @ mentions to pubkeys
setPublicMessageRecipients(nostrPubkeys)
setExtractedMentions(nostrPubkeys)
} catch (error) {
console.error('Error extracting mentions:', error)
setPublicMessageRecipients([])
setExtractedMentions([])
}
}, [])
useEffect(() => {
if (!isPublicMessage) {
setPublicMessageRecipients([])
return
}
if (!text) {
setPublicMessageRecipients([])
setExtractedMentions([])
return
}
// Debounce the mention extraction
// Debounce the mention extraction for all posts (not just public messages)
const timeoutId = setTimeout(() => {
extractMentionsFromContent(text)
}, 300)
@ -177,7 +172,7 @@ export default function PostContent({ @@ -177,7 +172,7 @@ export default function PostContent({
return () => {
clearTimeout(timeoutId)
}
}, [text, isPublicMessage, extractMentionsFromContent])
}, [text, extractMentionsFromContent])
const post = async (e?: React.MouseEvent) => {
e?.stopPropagation()
@ -197,9 +192,11 @@ export default function PostContent({ @@ -197,9 +192,11 @@ export default function PostContent({
// })
setPosting(true)
let draftEvent: any = null
let newEvent: any = null
try {
let draftEvent
if (isHighlight) {
// For highlights, pass the original sourceValue which contains the full identifier
// The createHighlightDraftEvent function will parse it correctly
@ -215,7 +212,7 @@ export default function PostContent({ @@ -215,7 +212,7 @@ export default function PostContent({
}
)
} else if (isPublicMessage) {
draftEvent = await createPublicMessageDraftEvent(text, publicMessageRecipients, {
draftEvent = await createPublicMessageDraftEvent(text, extractedMentions, {
addClientTag,
isNsfw
})
@ -245,10 +242,11 @@ export default function PostContent({ @@ -245,10 +242,11 @@ export default function PostContent({
}
// console.log('Publishing draft event:', draftEvent)
const newEvent = await publish(draftEvent, {
newEvent = await publish(draftEvent, {
specifiedRelayUrls: additionalRelayUrls.length > 0 ? additionalRelayUrls : undefined,
additionalRelayUrls: isPoll ? pollCreateData.relays : additionalRelayUrls,
minPow
minPow,
disableFallbacks: additionalRelayUrls.length > 0 // Don't use fallbacks if user explicitly selected relays
})
// console.log('Published event:', newEvent)
@ -283,6 +281,7 @@ export default function PostContent({ @@ -283,6 +281,7 @@ export default function PostContent({
showSimplePublishSuccess(parentEvent ? t('Reply published') : t('Post published'))
}
// Full success - clean up and close
postEditorCache.clearPostCache({ defaultContent, parentEvent })
deleteDraftEventCache(draftEvent)
addReplies([newEvent])
@ -313,6 +312,15 @@ export default function PostContent({ @@ -313,6 +312,15 @@ export default function PostContent({
(parentEvent ? t('Failed to publish reply') : t('Failed to publish post')),
duration: 6000
})
// Handle partial success
if (successCount > 0) {
// Clean up and close on partial success
postEditorCache.clearPostCache({ defaultContent, parentEvent })
if (draftEvent) deleteDraftEventCache(draftEvent)
if (newEvent) addReplies([newEvent])
close()
}
} else {
// Use standard publishing error feedback for cases without relay statuses
if (error instanceof AggregateError) {
@ -323,6 +331,7 @@ export default function PostContent({ @@ -323,6 +331,7 @@ export default function PostContent({
} else {
showPublishingError('Failed to publish')
}
// Don't close form on complete failure - let user try again
}
} finally {
setPosting(false)
@ -443,12 +452,12 @@ export default function PostContent({ @@ -443,12 +452,12 @@ export default function PostContent({
<Mentions
content={text}
parentEvent={undefined}
mentions={publicMessageRecipients}
setMentions={setPublicMessageRecipients}
mentions={extractedMentions}
setMentions={setExtractedMentions}
/>
{publicMessageRecipients.length > 0 ? (
{extractedMentions.length > 0 ? (
<div className="text-sm text-muted-foreground">
{t('Recipients detected from your message:')} {publicMessageRecipients.length}
{t('Recipients detected from your message:')} {extractedMentions.length}
</div>
) : (
<div className="text-sm text-muted-foreground">
@ -486,14 +495,23 @@ export default function PostContent({ @@ -486,14 +495,23 @@ export default function PostContent({
</div>
))}
{!isPoll && (
<PostRelaySelector
setIsProtectedEvent={setIsProtectedEvent}
setAdditionalRelayUrls={setAdditionalRelayUrls}
parentEvent={parentEvent}
openFrom={openFrom}
content={text}
isPublicMessage={isPublicMessage}
/>
<>
{console.log('PostContent: Rendering PostRelaySelector with:', {
extractedMentions,
isPublicMessage,
isPoll,
textLength: text.length
})}
<PostRelaySelector
setIsProtectedEvent={setIsProtectedEvent}
setAdditionalRelayUrls={setAdditionalRelayUrls}
parentEvent={parentEvent}
openFrom={openFrom}
content={text}
isPublicMessage={isPublicMessage}
mentions={extractedMentions}
/>
</>
)}
<div className="flex items-center justify-between">
<div className="flex gap-2 items-center">

123
src/components/PostEditor/PostRelaySelector.tsx

@ -5,7 +5,7 @@ import { useScreenSize } from '@/providers/ScreenSizeProvider' @@ -5,7 +5,7 @@ import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useNostr } from '@/providers/NostrProvider'
import { Check } from 'lucide-react'
import { NostrEvent } from 'nostr-tools'
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'
import { Dispatch, SetStateAction, useCallback, useEffect, useState, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import RelayIcon from '../RelayIcon'
import relaySelectionService from '@/services/relay-selection.service'
@ -16,7 +16,8 @@ export default function PostRelaySelector({ @@ -16,7 +16,8 @@ export default function PostRelaySelector({
setIsProtectedEvent,
setAdditionalRelayUrls,
content: postContent = '',
isPublicMessage = false
isPublicMessage = false,
mentions = []
}: {
parentEvent?: NostrEvent
openFrom?: string[]
@ -24,6 +25,7 @@ export default function PostRelaySelector({ @@ -24,6 +25,7 @@ export default function PostRelaySelector({
setAdditionalRelayUrls: Dispatch<SetStateAction<string[]>>
content?: string
isPublicMessage?: boolean
mentions?: string[]
}) {
const { t } = useTranslation()
const { isSmallScreen } = useScreenSize()
@ -36,8 +38,38 @@ export default function PostRelaySelector({ @@ -36,8 +38,38 @@ export default function PostRelaySelector({
const [isLoading, setIsLoading] = useState(true)
const [hasManualSelection, setHasManualSelection] = useState(false)
const [previousSelectableCount, setPreviousSelectableCount] = useState(0)
const [previousMentions, setPreviousMentions] = useState<string[]>([])
// Use centralized relay selection service
// Initialize previousMentions with the initial mentions value
useEffect(() => {
setPreviousMentions(mentions)
}, []) // Only run once on mount
// For discussion replies, content doesn't affect relay selection
// Check if this is a reply to a discussion by looking for "K" tag with "11"
const isDiscussionReply = useMemo(() => {
if (!_parentEvent) return false
// Direct reply to discussion
if (_parentEvent.kind === 11) return true
// Check if parent event has "K" tag containing "11" (discussion root kind)
const eventTags = _parentEvent.tags || []
const kindTag = eventTags.find(([tagName]) => tagName === 'K')
if (kindTag && kindTag[1] === '11') {
return true
}
return false
}, [_parentEvent])
// Memoize arrays to prevent unnecessary re-renders
const memoizedFavoriteRelays = useMemo(() => favoriteRelays, [favoriteRelays])
const memoizedBlockedRelays = useMemo(() => blockedRelays, [blockedRelays])
const memoizedRelaySets = useMemo(() => relaySets, [relaySets])
const memoizedOpenFrom = useMemo(() => openFrom, [openFrom])
// Use centralized relay selection service - only for non-content dependencies
useEffect(() => {
const updateRelaySelection = async () => {
setIsLoading(true)
@ -45,14 +77,14 @@ export default function PostRelaySelector({ @@ -45,14 +77,14 @@ export default function PostRelaySelector({
const result = await relaySelectionService.selectRelays({
userWriteRelays: relayList?.write || [],
userReadRelays: relayList?.read || [],
favoriteRelays,
blockedRelays,
relaySets,
favoriteRelays: memoizedFavoriteRelays,
blockedRelays: memoizedBlockedRelays,
relaySets: memoizedRelaySets,
parentEvent: _parentEvent,
isPublicMessage,
content: postContent,
content: isDiscussionReply ? '' : postContent, // Don't use content for discussion replies
userPubkey: pubkey || undefined,
openFrom
openFrom: memoizedOpenFrom
})
const newSelectableCount = result.selectableRelays.length
@ -63,17 +95,16 @@ export default function PostRelaySelector({ @@ -63,17 +95,16 @@ export default function PostRelaySelector({
// Only update selected relays if:
// 1. User hasn't manually modified them, OR
// 2. New mention relays were added (selectable count changed)
// 2. Selectable relays changed
if (!hasManualSelection || selectableRelaysChanged) {
setSelectedRelayUrls(result.selectedRelays)
setDescription(result.description)
// Reset manual selection flag if mentions changed
// Reset manual selection flag if relays changed
if (selectableRelaysChanged && hasManualSelection) {
setHasManualSelection(false)
}
}
console.log('PostRelaySelector: Updated relay selection:', result)
} catch (error) {
console.error('Failed to update relay selection:', error)
setSelectableRelays([])
@ -87,7 +118,75 @@ export default function PostRelaySelector({ @@ -87,7 +118,75 @@ export default function PostRelaySelector({
}
updateRelaySelection()
}, [openFrom, _parentEvent, favoriteRelays, blockedRelays, relaySets, isPublicMessage, postContent, pubkey, relayList])
}, [memoizedOpenFrom, _parentEvent, memoizedFavoriteRelays, memoizedBlockedRelays, memoizedRelaySets, isPublicMessage, pubkey, relayList, isDiscussionReply])
// Separate effect for mention changes in non-discussion replies
useEffect(() => {
console.log('PostRelaySelector: Mentions effect triggered', {
mentions,
previousMentions,
isDiscussionReply,
mentionsLength: mentions.length,
previousMentionsLength: previousMentions.length
})
if (isDiscussionReply) {
console.log('PostRelaySelector: Skipping mention update - is discussion reply')
return // Skip for discussion replies
}
const mentionsChanged = JSON.stringify(mentions) !== JSON.stringify(previousMentions)
console.log('PostRelaySelector: Mentions changed?', mentionsChanged)
if (mentionsChanged) {
console.log('PostRelaySelector: Updating relay selection due to mention changes')
setPreviousMentions(mentions)
// Update relay selection when mentions change
const updateRelaySelection = async () => {
setIsLoading(true)
try {
const result = await relaySelectionService.selectRelays({
userWriteRelays: relayList?.write || [],
userReadRelays: relayList?.read || [],
favoriteRelays: memoizedFavoriteRelays,
blockedRelays: memoizedBlockedRelays,
relaySets: memoizedRelaySets,
parentEvent: _parentEvent,
isPublicMessage,
content: postContent,
userPubkey: pubkey || undefined,
openFrom: memoizedOpenFrom
})
const newSelectableCount = result.selectableRelays.length
const selectableRelaysChanged = newSelectableCount !== previousSelectableCount
setSelectableRelays(result.selectableRelays)
setPreviousSelectableCount(newSelectableCount)
// Only update selected relays if:
// 1. User hasn't manually modified them, OR
// 2. Selectable relays changed
if (!hasManualSelection || selectableRelaysChanged) {
setSelectedRelayUrls(result.selectedRelays)
setDescription(result.description)
// Reset manual selection flag if relays changed
if (selectableRelaysChanged && hasManualSelection) {
setHasManualSelection(false)
}
}
} catch (error) {
console.error('Failed to update relay selection:', error)
} finally {
setIsLoading(false)
}
}
updateRelaySelection()
}
}, [mentions, isDiscussionReply, memoizedFavoriteRelays, memoizedBlockedRelays, memoizedRelaySets, _parentEvent, isPublicMessage, pubkey, relayList, memoizedOpenFrom, previousSelectableCount, hasManualSelection, postContent])
// Update description when selected relays change due to manual selection
useEffect(() => {

79
src/components/RelayStatusDisplay/index.tsx

@ -1,6 +1,49 @@ @@ -1,6 +1,49 @@
import { Check, X } from 'lucide-react'
import { simplifyUrl } from '@/lib/url'
/**
* Format relay error messages to be more user-friendly
*/
function formatRelayError(error: string): string {
const lowerError = error.toLowerCase()
// Handle confusing relay error messages
if (lowerError.includes('blocked') && lowerError.includes('event marked as protected')) {
return 'Relay rejected this content (may be due to content policy)'
}
if (lowerError.includes('blocked')) {
return 'Relay blocked this content'
}
if (lowerError.includes('rate limit') || lowerError.includes('rate-limit')) {
return 'Rate limited - please wait before trying again'
}
if (lowerError.includes('auth') && lowerError.includes('required')) {
return 'Authentication required'
}
if (lowerError.includes('writes disabled') || lowerError.includes('write disabled')) {
return 'Relay has temporarily disabled writes'
}
if (lowerError.includes('invalid key')) {
return 'Authentication failed - invalid key'
}
if (lowerError.includes('timeout')) {
return 'Request timed out'
}
if (lowerError.includes('connection') && lowerError.includes('refused')) {
return 'Connection refused by relay'
}
// Return original error if no specific formatting applies
return error
}
interface RelayStatus {
url: string
success: boolean
@ -31,13 +74,13 @@ export default function RelayStatusDisplay({ @@ -31,13 +74,13 @@ export default function RelayStatusDisplay({
Published to {successCount} of {totalCount} relays
</div>
<div className="space-y-1">
<div className="space-y-1 max-w-full">
{relayStatuses.map((status, index) => (
<div
key={index}
className="flex items-center gap-2 text-sm"
className="flex items-start gap-2 text-sm min-w-0"
>
<div className="flex-shrink-0">
<div className="flex-shrink-0 mt-0.5">
{status.success ? (
<Check className="h-4 w-4 text-green-500" />
) : (
@ -45,23 +88,25 @@ export default function RelayStatusDisplay({ @@ -45,23 +88,25 @@ export default function RelayStatusDisplay({
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="font-mono text-xs truncate">
{simplifyUrl(status.url)}
</span>
{status.authAttempted && !status.success && (
<span className="text-xs text-red-600 dark:text-red-400">
(auth failed)
<div className="flex-1 min-w-0 overflow-hidden">
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2 min-w-0">
<span className="font-mono text-xs break-all">
{simplifyUrl(status.url)}
</span>
{status.authAttempted && !status.success && (
<span className="text-xs text-red-600 dark:text-red-400 flex-shrink-0">
(auth failed)
</span>
)}
</div>
{!status.success && status.error && (
<div className="text-xs text-red-600 dark:text-red-400 break-words">
{formatRelayError(status.error)}
</div>
)}
</div>
{!status.success && status.error && (
<div className="text-xs text-red-600 dark:text-red-400 mt-0.5">
{status.error}
</div>
)}
</div>
</div>
))}

4
src/lib/publishing-feedback.tsx

@ -43,7 +43,7 @@ export function showPublishingFeedback( @@ -43,7 +43,7 @@ export function showPublishingFeedback(
const toastFunction = isSuccess ? toast.success : toast.error
toastFunction(
<div className="w-full">
<div className="w-full min-w-0">
<div className="flex items-center gap-2 mb-3">
<CheckCircle2 className={`w-5 h-5 ${isSuccess ? 'text-green-500' : 'text-red-500'}`} />
<div className="font-semibold">{message}</div>
@ -59,7 +59,7 @@ export function showPublishingFeedback( @@ -59,7 +59,7 @@ export function showPublishingFeedback(
</div>,
{
duration,
className: 'max-w-md'
className: 'max-w-lg w-full'
}
)
}

14
src/providers/NostrProvider/index.tsx

@ -772,13 +772,25 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -772,13 +772,25 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const relays = await client.determineTargetRelays(event, options)
try {
const publishResult = await client.publishEvent(relays, event)
const publishResult = await client.publishEvent(relays, event, {
disableFallbacks: options.disableFallbacks
})
// Store relay status for display
if (publishResult.relayStatuses.length > 0) {
(event as any).relayStatuses = publishResult.relayStatuses
}
// If publishing failed completely, throw an error so the form doesn't close
if (!publishResult.success) {
const error = new AggregateError(
publishResult.relayStatuses.map(s => new Error(s.error || 'Failed')),
'Failed to publish to any relay'
)
;(error as any).relayStatuses = publishResult.relayStatuses
throw error
}
return event
} catch (error) {
// Check for authentication-related errors

76
src/services/client.service.ts

@ -100,10 +100,16 @@ class ClientService extends EventTarget { @@ -100,10 +100,16 @@ class ClientService extends EventTarget {
if (specifiedRelayUrls?.length && (event.kind === ExtendedKind.DISCUSSION || event.kind === ExtendedKind.COMMENT)) {
// For discussion replies, try ONLY the specified relay first
// The fallback will be handled in the publishing logic if this fails
relays = specifiedRelayUrls
// But still filter blocked relays from specified relays
if (this.pubkey) {
const blockedRelays = await this.fetchBlockedRelays(this.pubkey)
relays = this.filterBlockedRelays(specifiedRelayUrls, blockedRelays)
} else {
relays = specifiedRelayUrls
}
return relays
} else if (specifiedRelayUrls?.length) {
// For non-discussion events, use specified relays as-is
// For non-discussion events, use specified relays (will be filtered below)
relays = specifiedRelayUrls
} else {
const _additionalRelayUrls: string[] = additionalRelayUrls ?? []
@ -155,10 +161,16 @@ class ClientService extends EventTarget { @@ -155,10 +161,16 @@ class ClientService extends EventTarget {
relays.push(...FAST_WRITE_RELAY_URLS)
}
// Filter out blocked relays
if (this.pubkey) {
const blockedRelays = await this.fetchBlockedRelays(this.pubkey)
relays = this.filterBlockedRelays(relays, blockedRelays)
}
return relays
}
async publishEvent(relayUrls: string[], event: NEvent): Promise<{
async publishEvent(relayUrls: string[], event: NEvent, options: { disableFallbacks?: boolean } = {}): Promise<{
success: boolean
relayStatuses: Array<{
url: string
@ -170,7 +182,8 @@ class ClientService extends EventTarget { @@ -170,7 +182,8 @@ class ClientService extends EventTarget {
totalCount: number
}> {
// Special handling for discussion events: try relay hint first, then fallback
if ((event.kind === ExtendedKind.DISCUSSION || event.kind === ExtendedKind.COMMENT) && relayUrls.length === 1) {
// BUT: if disableFallbacks is true (user explicitly selected relays), don't use fallbacks
if ((event.kind === ExtendedKind.DISCUSSION || event.kind === ExtendedKind.COMMENT) && relayUrls.length === 1 && !options.disableFallbacks) {
try {
// Try publishing to the relay hint first
const result = await this._publishToRelays(relayUrls, event)
@ -180,9 +193,11 @@ class ClientService extends EventTarget { @@ -180,9 +193,11 @@ class ClientService extends EventTarget {
return result
}
// If failed, try fallback relays
// If failed, try fallback relays (filtering out blocked relays)
const userRelays = this.pubkey ? await this.fetchRelayList(this.pubkey) : { write: [], read: [] }
const fallbackRelays = userRelays.write.length > 0 ? userRelays.write.slice(0, 3) : FAST_WRITE_RELAY_URLS
const blockedRelays = this.pubkey ? await this.fetchBlockedRelays(this.pubkey) : []
let fallbackRelays = userRelays.write.length > 0 ? userRelays.write.slice(0, 3) : FAST_WRITE_RELAY_URLS
fallbackRelays = this.filterBlockedRelays(fallbackRelays, blockedRelays)
console.log('Relay hint failed, trying fallback relays:', fallbackRelays)
const fallbackResult = await this._publishToRelays(fallbackRelays, event)
@ -208,7 +223,9 @@ class ClientService extends EventTarget { @@ -208,7 +223,9 @@ class ClientService extends EventTarget {
}
const userRelays = this.pubkey ? await this.fetchRelayList(this.pubkey) : { write: [], read: [] }
const fallbackRelays = userRelays.write.length > 0 ? userRelays.write.slice(0, 3) : FAST_WRITE_RELAY_URLS
const blockedRelays = this.pubkey ? await this.fetchBlockedRelays(this.pubkey) : []
let fallbackRelays = userRelays.write.length > 0 ? userRelays.write.slice(0, 3) : FAST_WRITE_RELAY_URLS
fallbackRelays = this.filterBlockedRelays(fallbackRelays, blockedRelays)
console.log('Trying fallback relays:', fallbackRelays)
const fallbackResult = await this._publishToRelays(fallbackRelays, event)
@ -243,6 +260,13 @@ class ClientService extends EventTarget { @@ -243,6 +260,13 @@ class ClientService extends EventTarget {
}> {
const uniqueRelayUrls = this.optimizeRelaySelection(Array.from(new Set(relayUrls)))
// Handle case where no relays are available (all filtered out)
if (uniqueRelayUrls.length === 0) {
const error = new Error('No relays available for publishing - all relays may be blocked or unavailable')
;(error as any).relayStatuses = []
throw error
}
const relayStatuses: Array<{
url: string
success: boolean
@ -1515,6 +1539,44 @@ class ClientService extends EventTarget { @@ -1515,6 +1539,44 @@ class ClientService extends EventTarget {
await this.updateReplaceableEventFromBigRelaysCache(event)
}
/**
* Fetch blocked relays from IndexedDB
*/
async fetchBlockedRelays(pubkey: string): Promise<string[]> {
try {
const blockedRelaysEvent = await indexedDb.getReplaceableEvent(pubkey, ExtendedKind.BLOCKED_RELAYS)
if (!blockedRelaysEvent) {
return []
}
// Extract relay URLs from the relay tags
const relayUrls = blockedRelaysEvent.tags
.filter(([tagName]) => tagName === 'relay')
.map(([, url]) => url)
.filter(Boolean)
return relayUrls
} catch (error) {
console.error('Failed to fetch blocked relays:', error)
return []
}
}
/**
* Filter out blocked relays from a relay list
*/
private filterBlockedRelays(relays: string[], blockedRelays: string[]): string[] {
if (!blockedRelays || blockedRelays.length === 0) {
return relays
}
const normalizedBlocked = blockedRelays.map(url => normalizeUrl(url) || url)
return relays.filter(relay => {
const normalizedRelay = normalizeUrl(relay) || relay
return !normalizedBlocked.includes(normalizedRelay)
})
}
/** =========== Replaceable event from big relays dataloader =========== */
private replaceableEventFromBigRelaysDataloader = new DataLoader<

1
src/types/index.d.ts vendored

@ -123,6 +123,7 @@ export type TPublishOptions = { @@ -123,6 +123,7 @@ export type TPublishOptions = {
specifiedRelayUrls?: string[]
additionalRelayUrls?: string[]
minPow?: number
disableFallbacks?: boolean // If true, don't use fallback relays when publishing fails
}
export type TNoteListMode = 'posts' | 'postsAndReplies' | 'you'

Loading…
Cancel
Save