feat: sync relay sets

This commit is contained in:
codytseng
2025-01-07 23:26:05 +08:00
parent 4343765aba
commit 7bd5b915eb
38 changed files with 1069 additions and 686 deletions

View File

@@ -1,9 +1,20 @@
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
import client from '@/services/client.service'
import storage from '@/services/storage.service'
import { TFeedType } from '@/types'
import { createContext, useContext, useState } from 'react'
import { Filter } from 'nostr-tools'
import { createContext, useContext, useEffect, useState } from 'react'
import { useNostr } from './NostrProvider'
import { useRelaySets } from './RelaySetsProvider'
type TFeedContext = {
feedType: TFeedType
setFeedType: (feedType: TFeedType) => void
relayUrls: string[]
temporaryRelayUrls: string[]
filter: Filter
isReady: boolean
activeRelaySetId: string | null
switchFeed: (feedType: TFeedType, options?: { activeRelaySetId?: string }) => Promise<void>
}
const FeedContext = createContext<TFeedContext | undefined>(undefined)
@@ -17,7 +28,113 @@ export const useFeed = () => {
}
export function FeedProvider({ children }: { children: React.ReactNode }) {
const { pubkey } = useNostr()
const { relaySets } = useRelaySets()
const [feedType, setFeedType] = useState<TFeedType>('relays')
const [relayUrls, setRelayUrls] = useState<string[]>([])
const [temporaryRelayUrls, setTemporaryRelayUrls] = useState<string[]>([])
const [filter, setFilter] = useState<Filter>({})
const [isReady, setIsReady] = useState(false)
const [activeRelaySetId, setActiveRelaySetId] = useState<string | null>(() =>
storage.getActiveRelaySetId()
)
return <FeedContext.Provider value={{ feedType, setFeedType }}>{children}</FeedContext.Provider>
useEffect(() => {
const init = async () => {
// temporary relay urls from query params
const searchParams = new URLSearchParams(window.location.search)
const tempRelays = searchParams
.getAll('r')
.map((url) =>
!url.startsWith('ws://') && !url.startsWith('wss://') ? `wss://${url}` : url
)
.filter((url) => isWebsocketUrl(url))
.map((url) => normalizeUrl(url))
if (tempRelays.length) {
setTemporaryRelayUrls(tempRelays)
return await switchFeed('temporary')
}
await switchFeed('relays', { activeRelaySetId })
}
init()
}, [])
useEffect(() => {
if (feedType !== 'following') return
switchFeed('following')
}, [pubkey])
useEffect(() => {
if (feedType !== 'relays') return
const relaySet = relaySets.find((set) => set.id === activeRelaySetId)
if (!relaySet) return
setRelayUrls(relaySet.relayUrls)
}, [relaySets])
const switchFeed = async (
feedType: TFeedType,
options: { activeRelaySetId?: string | null } = {}
) => {
setIsReady(false)
if (feedType === 'relays') {
const relaySetId = options.activeRelaySetId ?? (relaySets.length > 0 ? relaySets[0].id : null)
if (!relaySetId) return
const relaySet =
relaySets.find((set) => set.id === options.activeRelaySetId) ??
(relaySets.length > 0 ? relaySets[0] : null)
if (relaySet) {
setFeedType(feedType)
setRelayUrls(relaySet.relayUrls)
setActiveRelaySetId(relaySet.id)
setFilter({})
setIsReady(true)
storage.setActiveRelaySetId(relaySet.id)
}
return
}
if (feedType === 'following') {
if (!pubkey) return
setFeedType(feedType)
setActiveRelaySetId(null)
const [relayList, followings] = await Promise.all([
client.fetchRelayList(pubkey),
client.fetchFollowings(pubkey)
])
setRelayUrls(relayList.read.slice(0, 4))
setFilter({ authors: followings.includes(pubkey) ? followings : [...followings, pubkey] })
setIsReady(true)
return
}
if (feedType === 'temporary') {
setFeedType(feedType)
setRelayUrls(temporaryRelayUrls)
setActiveRelaySetId(null)
setFilter({})
setIsReady(true)
return
}
setIsReady(true)
}
return (
<FeedContext.Provider
value={{
feedType,
relayUrls,
temporaryRelayUrls,
filter,
isReady,
activeRelaySetId,
switchFeed
}}
>
{children}
</FeedContext.Provider>
)
}