Browse Source

update relay list only when mentions added or removed

imwald
Silberengel 5 months ago
parent
commit
d30ee3e830
  1. 3
      src/components/PostEditor/Mentions.tsx
  2. 49
      src/components/PostEditor/PostRelaySelector.tsx
  3. 4
      src/constants.ts
  4. 105
      src/services/relay-selection.service.ts

3
src/components/PostEditor/Mentions.tsx

@ -36,7 +36,8 @@ export default function Mentions({
if (_parentEventPubkey) { if (_parentEventPubkey) {
potentialMentions.push(_parentEventPubkey) potentialMentions.push(_parentEventPubkey)
} }
setPotentialMentions(potentialMentions) // Deduplicate the potential mentions array
setPotentialMentions(Array.from(new Set(potentialMentions)))
setRemovedPubkeys((pubkeys) => { setRemovedPubkeys((pubkeys) => {
return Array.from( return Array.from(
new Set( new Set(

49
src/components/PostEditor/PostRelaySelector.tsx

@ -27,13 +27,15 @@ export default function PostRelaySelector({
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { isSmallScreen } = useScreenSize() const { isSmallScreen } = useScreenSize()
const { relayUrls } = useCurrentRelays() useCurrentRelays() // Keep this hook call for any side effects
const { relaySets, favoriteRelays, blockedRelays } = useFavoriteRelays() const { relaySets, favoriteRelays, blockedRelays } = useFavoriteRelays()
const { pubkey, relayList } = useNostr() const { pubkey, relayList } = useNostr()
const [selectedRelayUrls, setSelectedRelayUrls] = useState<string[]>([]) const [selectedRelayUrls, setSelectedRelayUrls] = useState<string[]>([])
const [selectableRelays, setSelectableRelays] = useState<string[]>([]) const [selectableRelays, setSelectableRelays] = useState<string[]>([])
const [description, setDescription] = useState('') const [description, setDescription] = useState('')
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [hasManualSelection, setHasManualSelection] = useState(false)
const [previousSelectableCount, setPreviousSelectableCount] = useState(0)
// Use centralized relay selection service // Use centralized relay selection service
useEffect(() => { useEffect(() => {
@ -41,7 +43,7 @@ export default function PostRelaySelector({
setIsLoading(true) setIsLoading(true)
try { try {
const result = await relaySelectionService.selectRelays({ const result = await relaySelectionService.selectRelays({
userWriteRelays: relayList?.write || relayUrls, userWriteRelays: relayList?.write || [],
userReadRelays: relayList?.read || [], userReadRelays: relayList?.read || [],
favoriteRelays, favoriteRelays,
blockedRelays, blockedRelays,
@ -53,32 +55,59 @@ export default function PostRelaySelector({
openFrom openFrom
}) })
const newSelectableCount = result.selectableRelays.length
const selectableRelaysChanged = newSelectableCount !== previousSelectableCount
setSelectableRelays(result.selectableRelays) setSelectableRelays(result.selectableRelays)
setSelectedRelayUrls(result.selectedRelays) setPreviousSelectableCount(newSelectableCount)
setDescription(result.description)
// Only update selected relays if:
// 1. User hasn't manually modified them, OR
// 2. New mention relays were added (selectable count changed)
if (!hasManualSelection || selectableRelaysChanged) {
setSelectedRelayUrls(result.selectedRelays)
setDescription(result.description)
// Reset manual selection flag if mentions changed
if (selectableRelaysChanged && hasManualSelection) {
setHasManualSelection(false)
}
}
console.log('PostRelaySelector: Updated relay selection:', result) console.log('PostRelaySelector: Updated relay selection:', result)
} catch (error) { } catch (error) {
console.error('Failed to update relay selection:', error) console.error('Failed to update relay selection:', error)
setSelectableRelays([]) setSelectableRelays([])
setSelectedRelayUrls([]) if (!hasManualSelection) {
setDescription('No relays selected') setSelectedRelayUrls([])
setDescription('No relays selected')
}
} finally { } finally {
setIsLoading(false) setIsLoading(false)
} }
} }
updateRelaySelection() updateRelaySelection()
}, [openFrom, _parentEvent, relayUrls, favoriteRelays, blockedRelays, relaySets, isPublicMessage, postContent, pubkey, relayList]) }, [openFrom, _parentEvent, favoriteRelays, blockedRelays, relaySets, isPublicMessage, postContent, pubkey, relayList, hasManualSelection, previousSelectableCount])
// Update description when selected relays change due to manual selection
useEffect(() => {
if (hasManualSelection && !isLoading) {
const count = selectedRelayUrls.length
setDescription(count === 0 ? 'No relays selected' : count === 1 ? simplifyUrl(selectedRelayUrls[0]) : `${count} relays`)
}
}, [selectedRelayUrls, hasManualSelection, isLoading])
// Update parent component with selected relays // Update parent component with selected relays
useEffect(() => { useEffect(() => {
const isProtectedEvent = selectedRelayUrls.length > 0 && !selectedRelayUrls.some(url => relayUrls.includes(url)) // An event is "protected" if we have selected relays that aren't the default user write relays
const userWriteRelays = relayList?.write || []
const isProtectedEvent = selectedRelayUrls.length > 0 && !selectedRelayUrls.every(url => userWriteRelays.includes(url))
setIsProtectedEvent(isProtectedEvent) setIsProtectedEvent(isProtectedEvent)
setAdditionalRelayUrls(selectedRelayUrls) setAdditionalRelayUrls(selectedRelayUrls)
}, [selectedRelayUrls, relayUrls, setIsProtectedEvent, setAdditionalRelayUrls]) }, [selectedRelayUrls, relayList, setIsProtectedEvent, setAdditionalRelayUrls])
const handleRelayCheckedChange = useCallback((checked: boolean, url: string) => { const handleRelayCheckedChange = useCallback((checked: boolean, url: string) => {
setHasManualSelection(true)
if (checked) { if (checked) {
setSelectedRelayUrls(prev => [...prev, url]) setSelectedRelayUrls(prev => [...prev, url])
} else { } else {
@ -87,10 +116,12 @@ export default function PostRelaySelector({
}, []) }, [])
const handleSelectAll = useCallback(() => { const handleSelectAll = useCallback(() => {
setHasManualSelection(true)
setSelectedRelayUrls([...selectableRelays]) setSelectedRelayUrls([...selectableRelays])
}, [selectableRelays]) }, [selectableRelays])
const handleClearAll = useCallback(() => { const handleClearAll = useCallback(() => {
setHasManualSelection(true)
setSelectedRelayUrls([]) setSelectedRelayUrls([])
}, []) }, [])

4
src/constants.ts

@ -66,7 +66,8 @@ export const BIG_RELAY_URLS = [
'wss://nostr.land', 'wss://nostr.land',
'wss://nostr.wine', 'wss://nostr.wine',
'wss://nostr.sovbit.host', 'wss://nostr.sovbit.host',
'wss://nostr21.com' 'wss://nostr21.com',
'wss://thecitadel.nostr1.com'
] ]
// Optimized relay list for read operations (includes aggregator) // Optimized relay list for read operations (includes aggregator)
@ -74,6 +75,7 @@ export const FAST_READ_RELAY_URLS = [
'wss://theforest.nostr1.com', 'wss://theforest.nostr1.com',
'wss://orly-relay.imwald.eu', 'wss://orly-relay.imwald.eu',
'wss://nostr.wine', 'wss://nostr.wine',
'wss://thecitadel.nostr1.com',
'wss://aggr.nostr.land' 'wss://aggr.nostr.land'
] ]

105
src/services/relay-selection.service.ts

@ -99,6 +99,7 @@ class RelaySelectionService {
const { parentEvent, isPublicMessage, content, userPubkey } = context const { parentEvent, isPublicMessage, content, userPubkey } = context
const contextualRelays = new Set<string>() const contextualRelays = new Set<string>()
try { try {
// For replies (any kind) and public messages // For replies (any kind) and public messages
if (parentEvent || isPublicMessage) { if (parentEvent || isPublicMessage) {
@ -116,17 +117,32 @@ class RelaySelectionService {
eventHints.forEach(url => contextualRelays.add(url)) eventHints.forEach(url => contextualRelays.add(url))
} }
// For public messages, also get mentioned users' read relays // For replies and public messages, get mentioned users' relays
if (isPublicMessage && content && userPubkey) { if (userPubkey) {
const mentions = await this.extractMentions(content, parentEvent) let mentions: string[] = []
// Always include parent event author for replies
if (parentEvent) {
mentions.push(parentEvent.pubkey)
}
// Extract additional mentions from content if available
if (content) {
const contentMentions = await this.extractMentions(content, parentEvent)
mentions = [...new Set([...mentions, ...contentMentions])] // deduplicate
}
const mentionedPubkeys = mentions.filter(p => p !== userPubkey) const mentionedPubkeys = mentions.filter(p => p !== userPubkey)
if (mentionedPubkeys.length > 0) { if (mentionedPubkeys.length > 0) {
const mentionRelayLists = await Promise.all( const mentionRelayLists = await Promise.all(
mentionedPubkeys.map(async (pubkey) => { mentionedPubkeys.map(async (pubkey) => {
try { try {
const relayList = await client.fetchRelayList(pubkey) const relayList = await client.fetchRelayList(pubkey)
return relayList?.read || [] // Use write relays for replies, read relays for public messages
const relayType = isPublicMessage ? 'read' : 'write'
return relayList?.[relayType] || []
} catch (error) { } catch (error) {
console.warn(`Failed to fetch relay list for ${pubkey}:`, error) console.warn(`Failed to fetch relay list for ${pubkey}:`, error)
return [] return []
@ -155,7 +171,9 @@ class RelaySelectionService {
userWriteRelays, userWriteRelays,
parentEvent, parentEvent,
isPublicMessage, isPublicMessage,
openFrom openFrom,
content,
userPubkey
} = context } = context
let selectedRelays: string[] = [] let selectedRelays: string[] = []
@ -177,7 +195,43 @@ class RelaySelectionService {
} }
// For regular replies, use user's write relays + mention relays // For regular replies, use user's write relays + mention relays
else if (parentEvent && this.isRegularReply(parentEvent)) { else if (parentEvent && this.isRegularReply(parentEvent)) {
selectedRelays = await this.getRegularReplyRelays(context) // Get user's write relays
const userRelays = userWriteRelays.length > 0 ? userWriteRelays : FAST_WRITE_RELAY_URLS
selectedRelays = userRelays.map(url => normalizeUrl(url) || url).filter(Boolean)
// Add mention relays
if (userPubkey) {
let mentions: string[] = []
// Always include parent event author for replies
if (parentEvent) {
mentions.push(parentEvent.pubkey)
}
// Extract additional mentions from content if available
if (content) {
const contentMentions = await this.extractMentions(content, parentEvent)
mentions = [...new Set([...mentions, ...contentMentions])] // deduplicate
}
const mentionedPubkeys = mentions.filter(p => p !== userPubkey)
if (mentionedPubkeys.length > 0) {
const mentionRelayLists = await Promise.all(
mentionedPubkeys.map(async (pubkey) => {
try {
const relayList = await client.fetchRelayList(pubkey)
return relayList?.write || []
} catch (error) {
console.warn(`Failed to fetch relay list for ${pubkey}:`, error)
return []
}
})
)
const mentionRelays = mentionRelayLists.flat().map(url => normalizeUrl(url) || url).filter(Boolean)
selectedRelays = [...selectedRelays, ...mentionRelays]
}
}
} }
// Default: user's write relays (or fallback to fast write relays if no user relays) // Default: user's write relays (or fallback to fast write relays if no user relays)
else { else {
@ -239,44 +293,6 @@ class RelaySelectionService {
return Array.from(relays) return Array.from(relays)
} }
/**
* Get relays for regular replies: user's write relays + mention relays
*/
private async getRegularReplyRelays(context: RelaySelectionContext): Promise<string[]> {
const { userWriteRelays, parentEvent, content, userPubkey } = context
const relays = new Set<string>()
try {
// Add user's write relays - fallback to fast write relays if no user relays
const userRelays = userWriteRelays.length > 0 ? userWriteRelays : FAST_WRITE_RELAY_URLS
userRelays.forEach(url => relays.add(normalizeUrl(url) || url))
// Add mentioned users' write relays
if (content && userPubkey) {
const mentions = await this.extractMentions(content, parentEvent)
const mentionedPubkeys = mentions.filter(p => p !== userPubkey)
if (mentionedPubkeys.length > 0) {
const mentionRelayLists = await Promise.all(
mentionedPubkeys.map(async (pubkey) => {
try {
const relayList = await client.fetchRelayList(pubkey)
return relayList?.write || []
} catch (error) {
console.warn(`Failed to fetch relay list for ${pubkey}:`, error)
return []
}
})
)
mentionRelayLists.flat().forEach(url => relays.add(normalizeUrl(url) || url))
}
}
} catch (error) {
console.error('Failed to get regular reply relays:', error)
}
return Array.from(relays)
}
/** /**
* Check if this is a regular reply (Kind 1 or Kind 1111, not to Kind 11) * Check if this is a regular reply (Kind 1 or Kind 1111, not to Kind 11)
@ -329,6 +345,7 @@ class RelaySelectionService {
/nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+|note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g /nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+|note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g
) )
if (matches) { if (matches) {
for (const match of matches) { for (const match of matches) {
try { try {

Loading…
Cancel
Save