169 lines
4.5 KiB
TypeScript
169 lines
4.5 KiB
TypeScript
import { getRelaySetFromEvent } from '@/lib/event-metadata'
|
|
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
|
import indexedDb from '@/services/indexed-db.service'
|
|
import storage from '@/services/local-storage.service'
|
|
import { TFeedInfo, TFeedType } from '@/types'
|
|
import { kinds } from 'nostr-tools'
|
|
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
|
import { useFavoriteRelays } from './FavoriteRelaysProvider'
|
|
import { useNostr } from './NostrProvider'
|
|
|
|
type TFeedContext = {
|
|
feedInfo: TFeedInfo
|
|
relayUrls: string[]
|
|
isReady: boolean
|
|
switchFeed: (
|
|
feedType: TFeedType | null,
|
|
options?: { activeRelaySetId?: string; pubkey?: string; relay?: string | null }
|
|
) => Promise<void>
|
|
}
|
|
|
|
const FeedContext = createContext<TFeedContext | undefined>(undefined)
|
|
|
|
export const useFeed = () => {
|
|
const context = useContext(FeedContext)
|
|
if (!context) {
|
|
throw new Error('useFeed must be used within a FeedProvider')
|
|
}
|
|
return context
|
|
}
|
|
|
|
export function FeedProvider({ children }: { children: React.ReactNode }) {
|
|
const { pubkey, isInitialized } = useNostr()
|
|
const { relaySets } = useFavoriteRelays()
|
|
const [relayUrls, setRelayUrls] = useState<string[]>([])
|
|
const [isReady, setIsReady] = useState(false)
|
|
const [feedInfo, setFeedInfo] = useState<TFeedInfo>(null)
|
|
const feedInfoRef = useRef<TFeedInfo>(feedInfo)
|
|
|
|
useEffect(() => {
|
|
const init = async () => {
|
|
if (!isInitialized) {
|
|
return
|
|
}
|
|
|
|
let feedInfo: TFeedInfo = null
|
|
if (pubkey) {
|
|
const storedFeedInfo = storage.getFeedInfo(pubkey)
|
|
if (storedFeedInfo) {
|
|
feedInfo = storedFeedInfo
|
|
} else {
|
|
feedInfo = { feedType: 'following' }
|
|
}
|
|
}
|
|
|
|
if (!feedInfo) {
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
|
|
if (feedInfo.feedType === 'relays') {
|
|
return await switchFeed('relays', { activeRelaySetId: feedInfo.id })
|
|
}
|
|
|
|
if (feedInfo.feedType === 'relay') {
|
|
return await switchFeed('relay', { relay: feedInfo.id })
|
|
}
|
|
|
|
// update following feed if pubkey changes
|
|
if (feedInfo.feedType === 'following' && pubkey) {
|
|
return await switchFeed('following', { pubkey })
|
|
}
|
|
}
|
|
|
|
init()
|
|
}, [pubkey, isInitialized])
|
|
|
|
const switchFeed = async (
|
|
feedType: TFeedType | null,
|
|
options: {
|
|
activeRelaySetId?: string | null
|
|
pubkey?: string | null
|
|
relay?: string | null
|
|
} = {}
|
|
) => {
|
|
if (!feedType) {
|
|
setFeedInfo(null)
|
|
feedInfoRef.current = null
|
|
setRelayUrls([])
|
|
return
|
|
}
|
|
|
|
setIsReady(false)
|
|
if (feedType === 'relay') {
|
|
const normalizedUrl = normalizeUrl(options.relay ?? '')
|
|
if (!normalizedUrl || !isWebsocketUrl(normalizedUrl)) {
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
|
|
const newFeedInfo = { feedType, id: normalizedUrl }
|
|
setFeedInfo(newFeedInfo)
|
|
feedInfoRef.current = newFeedInfo
|
|
setRelayUrls([normalizedUrl])
|
|
storage.setFeedInfo(newFeedInfo, pubkey)
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
if (feedType === 'relays') {
|
|
const relaySetId = options.activeRelaySetId ?? (relaySets.length > 0 ? relaySets[0].id : null)
|
|
if (!relaySetId || !pubkey) {
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
|
|
let relaySet =
|
|
relaySets.find((set) => set.id === relaySetId) ??
|
|
(relaySets.length > 0 ? relaySets[0] : null)
|
|
if (!relaySet) {
|
|
const storedRelaySetEvent = await indexedDb.getReplaceableEvent(
|
|
pubkey,
|
|
kinds.Relaysets,
|
|
relaySetId
|
|
)
|
|
if (storedRelaySetEvent) {
|
|
relaySet = getRelaySetFromEvent(storedRelaySetEvent)
|
|
}
|
|
}
|
|
if (relaySet) {
|
|
const newFeedInfo = { feedType, id: relaySet.id }
|
|
setFeedInfo(newFeedInfo)
|
|
feedInfoRef.current = newFeedInfo
|
|
setRelayUrls(relaySet.relayUrls)
|
|
storage.setFeedInfo(newFeedInfo, pubkey)
|
|
setIsReady(true)
|
|
}
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
if (feedType === 'following') {
|
|
if (!options.pubkey) {
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
const newFeedInfo = { feedType }
|
|
setFeedInfo(newFeedInfo)
|
|
feedInfoRef.current = newFeedInfo
|
|
storage.setFeedInfo(newFeedInfo, pubkey)
|
|
|
|
setRelayUrls([])
|
|
setIsReady(true)
|
|
return
|
|
}
|
|
setIsReady(true)
|
|
}
|
|
|
|
return (
|
|
<FeedContext.Provider
|
|
value={{
|
|
feedInfo,
|
|
relayUrls,
|
|
isReady,
|
|
switchFeed
|
|
}}
|
|
>
|
|
{children}
|
|
</FeedContext.Provider>
|
|
)
|
|
}
|