|
|
|
@ -1,6 +1,6 @@ |
|
|
|
import { Button } from '@/components/ui/button' |
|
|
|
import { Button } from '@/components/ui/button' |
|
|
|
import { Card, CardContent } from '@/components/ui/card' |
|
|
|
import { Card, CardContent } from '@/components/ui/card' |
|
|
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' |
|
|
|
// Removed dropdown menu import - no longer using relay selection
|
|
|
|
import { FAST_READ_RELAY_URLS } from '@/constants' |
|
|
|
import { FAST_READ_RELAY_URLS } from '@/constants' |
|
|
|
import { normalizeUrl } from '@/lib/url' |
|
|
|
import { normalizeUrl } from '@/lib/url' |
|
|
|
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' |
|
|
|
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' |
|
|
|
@ -193,14 +193,14 @@ type EventMapEntry = { |
|
|
|
|
|
|
|
|
|
|
|
const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
const { t } = useTranslation() |
|
|
|
const { t } = useTranslation() |
|
|
|
const { relaySets, favoriteRelays } = useFavoriteRelays() |
|
|
|
const { favoriteRelays } = useFavoriteRelays() |
|
|
|
const { pubkey } = useNostr() |
|
|
|
const { pubkey } = useNostr() |
|
|
|
const { push } = useSecondaryPage() |
|
|
|
const { push } = useSecondaryPage() |
|
|
|
|
|
|
|
|
|
|
|
// State management
|
|
|
|
// State management
|
|
|
|
const [selectedTopic, setSelectedTopic] = useState('all') |
|
|
|
const [selectedTopic, setSelectedTopic] = useState('all') |
|
|
|
const [selectedSubtopic, setSelectedSubtopic] = useState<string | null>(null) |
|
|
|
const [selectedSubtopic, setSelectedSubtopic] = useState<string | null>(null) |
|
|
|
const [selectedRelay, setSelectedRelay] = useState<string | null>(null) |
|
|
|
// Removed relay filtering - using all relays
|
|
|
|
const [selectedSort, setSelectedSort] = useState<SortOption>('newest') |
|
|
|
const [selectedSort, setSelectedSort] = useState<SortOption>('newest') |
|
|
|
const [eventMap, setEventMap] = useState<Map<string, EventMapEntry>>(new Map()) |
|
|
|
const [eventMap, setEventMap] = useState<Map<string, EventMapEntry>>(new Map()) |
|
|
|
const [filteredEvents, setFilteredEvents] = useState<NostrEvent[]>([]) |
|
|
|
const [filteredEvents, setFilteredEvents] = useState<NostrEvent[]>([]) |
|
|
|
@ -218,26 +218,25 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
const isFetchingRef = useRef(false) |
|
|
|
const isFetchingRef = useRef(false) |
|
|
|
const lastFetchTimeRef = useRef(0) |
|
|
|
const lastFetchTimeRef = useRef(0) |
|
|
|
|
|
|
|
|
|
|
|
// Get all available relays (use favorite relays from provider + additional relays)
|
|
|
|
// Get all available relays (use favorite relays from provider + user's read relays or fast read relays)
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
const updateRelays = async () => { |
|
|
|
const updateRelays = async () => { |
|
|
|
let userWriteRelays: string[] = [] |
|
|
|
let userReadRelays: string[] = [] |
|
|
|
|
|
|
|
|
|
|
|
if (pubkey) { |
|
|
|
if (pubkey) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
// Get user's write relays
|
|
|
|
// Get user's read relays
|
|
|
|
const relayList = await client.fetchRelayList(pubkey) |
|
|
|
const relayList = await client.fetchRelayList(pubkey) |
|
|
|
userWriteRelays = relayList?.write || [] |
|
|
|
userReadRelays = relayList?.read || [] |
|
|
|
} catch (error) { |
|
|
|
} catch (error) { |
|
|
|
console.warn('Failed to fetch user relay list:', error) |
|
|
|
console.warn('Failed to fetch user relay list:', error) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Use favorite relays from provider (includes stored relay sets) + additional relays
|
|
|
|
// Use favorite relays from provider (includes stored relay sets) + user's read relays or fast read relays
|
|
|
|
const allRawRelays = [ |
|
|
|
const allRawRelays = [ |
|
|
|
...favoriteRelays, |
|
|
|
...favoriteRelays, |
|
|
|
...userWriteRelays, |
|
|
|
...(userReadRelays.length > 0 ? userReadRelays : FAST_READ_RELAY_URLS) |
|
|
|
...FAST_READ_RELAY_URLS |
|
|
|
|
|
|
|
] |
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
// Normalize and deduplicate all relays
|
|
|
|
// Normalize and deduplicate all relays
|
|
|
|
@ -267,6 +266,13 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
const [dynamicTopics, setDynamicTopics] = useState<string[]>([]) |
|
|
|
const [dynamicTopics, setDynamicTopics] = useState<string[]>([]) |
|
|
|
const [dynamicSubtopics, setDynamicSubtopics] = useState<string[]>([]) |
|
|
|
const [dynamicSubtopics, setDynamicSubtopics] = useState<string[]>([]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Manual reset function for debugging
|
|
|
|
|
|
|
|
const resetFetchState = useCallback(() => { |
|
|
|
|
|
|
|
console.log('Manually resetting fetch state') |
|
|
|
|
|
|
|
isFetchingRef.current = false |
|
|
|
|
|
|
|
setLoading(false) |
|
|
|
|
|
|
|
}, []) |
|
|
|
|
|
|
|
|
|
|
|
// Fetch all kind 11 events from all relays
|
|
|
|
// Fetch all kind 11 events from all relays
|
|
|
|
const fetchAllEvents = useCallback(async () => { |
|
|
|
const fetchAllEvents = useCallback(async () => { |
|
|
|
// Prevent multiple simultaneous fetches using ref to avoid dependency
|
|
|
|
// Prevent multiple simultaneous fetches using ref to avoid dependency
|
|
|
|
@ -282,10 +288,18 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('Starting fetchAllEvents...') |
|
|
|
|
|
|
|
|
|
|
|
isFetchingRef.current = true |
|
|
|
isFetchingRef.current = true |
|
|
|
lastFetchTimeRef.current = now |
|
|
|
lastFetchTimeRef.current = now |
|
|
|
setLoading(true) |
|
|
|
setLoading(true) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Safety timeout to reset fetch state if it gets stuck
|
|
|
|
|
|
|
|
const safetyTimeout = setTimeout(() => { |
|
|
|
|
|
|
|
console.warn('Fetch timeout - resetting fetch state') |
|
|
|
|
|
|
|
isFetchingRef.current = false |
|
|
|
|
|
|
|
setLoading(false) |
|
|
|
|
|
|
|
}, 30000) // 30 second timeout
|
|
|
|
try { |
|
|
|
try { |
|
|
|
// Fetch recent kind 11 events (last 30 days)
|
|
|
|
// Fetch recent kind 11 events (last 30 days)
|
|
|
|
const thirtyDaysAgo = Math.floor((Date.now() - (30 * 24 * 60 * 60 * 1000)) / 1000) |
|
|
|
const thirtyDaysAgo = Math.floor((Date.now() - (30 * 24 * 60 * 60 * 1000)) / 1000) |
|
|
|
@ -396,6 +410,7 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
setDynamicTopics([]) |
|
|
|
setDynamicTopics([]) |
|
|
|
setDynamicSubtopics([]) |
|
|
|
setDynamicSubtopics([]) |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
|
|
|
|
clearTimeout(safetyTimeout) |
|
|
|
setLoading(false) |
|
|
|
setLoading(false) |
|
|
|
isFetchingRef.current = false |
|
|
|
isFetchingRef.current = false |
|
|
|
} |
|
|
|
} |
|
|
|
@ -412,30 +427,10 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
} |
|
|
|
} |
|
|
|
}, [allRelays]) |
|
|
|
}, [allRelays]) |
|
|
|
|
|
|
|
|
|
|
|
// Filter events based on selected relay
|
|
|
|
// Simplified filtering - no relay filtering, just return all events
|
|
|
|
const getFilteredEvents = useCallback(() => { |
|
|
|
const getFilteredEvents = useCallback(() => { |
|
|
|
const events = Array.from(eventMap.values()) |
|
|
|
return Array.from(eventMap.values()).map(entry => entry.event) |
|
|
|
|
|
|
|
}, [eventMap]) |
|
|
|
// Filter by selected relay if specified
|
|
|
|
|
|
|
|
let filtered = events |
|
|
|
|
|
|
|
if (selectedRelay) { |
|
|
|
|
|
|
|
// Check if it's a relay set
|
|
|
|
|
|
|
|
const relaySet = relaySets.find(set => set.id === selectedRelay) |
|
|
|
|
|
|
|
if (relaySet) { |
|
|
|
|
|
|
|
filtered = events.filter(entry =>
|
|
|
|
|
|
|
|
entry.relaySources.some(source => relaySet.relayUrls.includes(source)) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// It's an individual relay - normalize both for comparison
|
|
|
|
|
|
|
|
const normalizedSelectedRelay = normalizeUrl(selectedRelay) |
|
|
|
|
|
|
|
filtered = events.filter(entry =>
|
|
|
|
|
|
|
|
entry.relaySources.some(source => normalizeUrl(source) === normalizedSelectedRelay) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return filtered.map(entry => entry.event) |
|
|
|
|
|
|
|
}, [eventMap, selectedRelay, relaySets]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Filter threads by topic and search
|
|
|
|
// Filter threads by topic and search
|
|
|
|
const filterAndSortEvents = useCallback(() => { |
|
|
|
const filterAndSortEvents = useCallback(() => { |
|
|
|
@ -610,33 +605,25 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If still no sources, use the selected relay or all relays
|
|
|
|
// If still no sources, use first few relays
|
|
|
|
if (relaySources.length === 0) { |
|
|
|
if (relaySources.length === 0) { |
|
|
|
relaySources = selectedRelay ? [selectedRelay] : allRelays.slice(0, 3) |
|
|
|
relaySources = allRelays.slice(0, 3) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
console.log('Using relay sources:', relaySources) |
|
|
|
console.log('Using relay sources:', relaySources) |
|
|
|
|
|
|
|
|
|
|
|
// Ensure the event hints are properly set for navigation
|
|
|
|
// Note: Event tracking will happen automatically when the event is fetched
|
|
|
|
// This is important for the toNote() function to include relay hints in the URL
|
|
|
|
// from the relays during the next fetchAllEvents call. The relaySources
|
|
|
|
if (relaySources.length > 0) { |
|
|
|
// are stored in the eventMap so the event can be found and displayed.
|
|
|
|
console.log('Tracking event on relays for navigation:', relaySources) |
|
|
|
console.log('Event will be tracked automatically on next fetch from relays:', relaySources) |
|
|
|
// Create a temporary relay object to track the event
|
|
|
|
|
|
|
|
relaySources.forEach(relayUrl => { |
|
|
|
// Debug: Check if the event hints are already set
|
|
|
|
try { |
|
|
|
const currentHints = client.getEventHints(publishedEvent.id) |
|
|
|
// Import the Relay class from nostr-tools
|
|
|
|
console.log('Current event hints:', currentHints) |
|
|
|
const { Relay } = require('nostr-tools') |
|
|
|
|
|
|
|
const tempRelay = new Relay(relayUrl) |
|
|
|
|
|
|
|
client.trackEventSeenOn(publishedEvent.id, tempRelay) |
|
|
|
|
|
|
|
console.log(`Tracked event ${publishedEvent.id} on relay ${relayUrl}`) |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
console.warn('Failed to create relay object for tracking:', relayUrl, error) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Verify the hints are set
|
|
|
|
// If no hints are set, the event wasn't properly tracked during publishing
|
|
|
|
const hints = client.getEventHints(publishedEvent.id) |
|
|
|
if (currentHints.length === 0) { |
|
|
|
console.log('Event hints after tracking:', hints) |
|
|
|
console.warn('Event has no relay hints - navigation may not work properly') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add to event map
|
|
|
|
// Add to event map
|
|
|
|
@ -667,7 +654,11 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Also refetch in the background to ensure we have the latest
|
|
|
|
// Also refetch in the background to ensure we have the latest
|
|
|
|
setTimeout(() => fetchAllEvents(), 2000) // Wait 2 seconds for the event to propagate
|
|
|
|
// This will help ensure the event is properly tracked on relays
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
|
|
console.log('Background fetch after thread creation') |
|
|
|
|
|
|
|
fetchAllEvents() |
|
|
|
|
|
|
|
}, 3000) // Wait 3 seconds for the event to propagate
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
@ -695,42 +686,7 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
threads={viewMode === 'grouped' && selectedTopic === 'all' ? filteredEvents : filteredEvents} |
|
|
|
threads={viewMode === 'grouped' && selectedTopic === 'all' ? filteredEvents : filteredEvents} |
|
|
|
replies={[]} |
|
|
|
replies={[]} |
|
|
|
/> |
|
|
|
/> |
|
|
|
{(allRelays.length > 1 || relaySets.length > 0) && ( |
|
|
|
{/* Removed relay selection dropdown */} |
|
|
|
<DropdownMenu> |
|
|
|
|
|
|
|
<DropdownMenuTrigger asChild> |
|
|
|
|
|
|
|
<Button variant="outline" className="h-10 text-sm"> |
|
|
|
|
|
|
|
{selectedRelay ? ( |
|
|
|
|
|
|
|
relaySets.find(set => set.id === selectedRelay)?.name ||
|
|
|
|
|
|
|
|
selectedRelay.replace('wss://', '').replace('ws://', '') |
|
|
|
|
|
|
|
) : ( |
|
|
|
|
|
|
|
'All Relays' |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</Button> |
|
|
|
|
|
|
|
</DropdownMenuTrigger> |
|
|
|
|
|
|
|
<DropdownMenuContent> |
|
|
|
|
|
|
|
<DropdownMenuItem onClick={() => setSelectedRelay(null)}> |
|
|
|
|
|
|
|
All Relays |
|
|
|
|
|
|
|
</DropdownMenuItem> |
|
|
|
|
|
|
|
<DropdownMenuSeparator /> |
|
|
|
|
|
|
|
{relaySets.map(relaySet => ( |
|
|
|
|
|
|
|
<DropdownMenuItem
|
|
|
|
|
|
|
|
key={relaySet.id}
|
|
|
|
|
|
|
|
onClick={() => setSelectedRelay(relaySet.id)} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{relaySet.name} |
|
|
|
|
|
|
|
</DropdownMenuItem> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
{allRelays.map(relay => ( |
|
|
|
|
|
|
|
<DropdownMenuItem
|
|
|
|
|
|
|
|
key={relay}
|
|
|
|
|
|
|
|
onClick={() => setSelectedRelay(relay)} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{relay.replace('wss://', '').replace('ws://', '')} |
|
|
|
|
|
|
|
</DropdownMenuItem> |
|
|
|
|
|
|
|
))} |
|
|
|
|
|
|
|
</DropdownMenuContent> |
|
|
|
|
|
|
|
</DropdownMenu> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div className="flex gap-1 items-center"> |
|
|
|
<div className="flex gap-1 items-center"> |
|
|
|
<Button |
|
|
|
<Button |
|
|
|
@ -910,6 +866,9 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
<Button variant="outline" onClick={fetchAllEvents}> |
|
|
|
<Button variant="outline" onClick={fetchAllEvents}> |
|
|
|
{t('Refresh')} |
|
|
|
{t('Refresh')} |
|
|
|
</Button> |
|
|
|
</Button> |
|
|
|
|
|
|
|
<Button variant="outline" onClick={resetFetchState}> |
|
|
|
|
|
|
|
Reset Fetch |
|
|
|
|
|
|
|
</Button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</CardContent> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
</Card> |
|
|
|
@ -994,8 +953,7 @@ const DiscussionsPage = forwardRef((_, ref) => { |
|
|
|
<CreateThreadDialog |
|
|
|
<CreateThreadDialog |
|
|
|
topic={selectedTopic} |
|
|
|
topic={selectedTopic} |
|
|
|
availableRelays={allRelays} |
|
|
|
availableRelays={allRelays} |
|
|
|
relaySets={relaySets} |
|
|
|
relaySets={[]} |
|
|
|
selectedRelay={selectedRelay} |
|
|
|
|
|
|
|
onClose={() => setShowCreateThread(false)} |
|
|
|
onClose={() => setShowCreateThread(false)} |
|
|
|
onThreadCreated={handleThreadCreated} |
|
|
|
onThreadCreated={handleThreadCreated} |
|
|
|
/> |
|
|
|
/> |
|
|
|
|