import { BIG_RELAY_URLS } from '@/constants' import { isWebsocketUrl, normalizeUrl } from '@/lib/url' import storage from '@/services/storage.service' import { TFeedType } from '@/types' import { Filter } from 'nostr-tools' import { createContext, useContext, useEffect, useState } from 'react' import { useNostr } from './NostrProvider' import { useRelaySets } from './RelaySetsProvider' type TFeedContext = { feedType: TFeedType relayUrls: string[] temporaryRelayUrls: string[] filter: Filter isReady: boolean activeRelaySetId: string | null switchFeed: ( feedType: TFeedType, options?: { activeRelaySetId?: string; pubkey?: string } ) => Promise } const FeedContext = createContext(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, getRelayList, getFollowings } = useNostr() const { relaySets } = useRelaySets() const [feedType, setFeedType] = useState(storage.getFeedType()) const [relayUrls, setRelayUrls] = useState([]) const [temporaryRelayUrls, setTemporaryRelayUrls] = useState([]) const [filter, setFilter] = useState({}) const [isReady, setIsReady] = useState(false) const [activeRelaySetId, setActiveRelaySetId] = useState( storage.getActiveRelaySetId() ) useEffect(() => { const init = async () => { // temporary relay urls from query params const searchParams = new URLSearchParams(window.location.search) const temporaryRelayUrls = searchParams .getAll('r') .map((url) => normalizeUrl(url)) .filter((url) => isWebsocketUrl(url)) if (temporaryRelayUrls.length) { return await switchFeed('temporary', { temporaryRelayUrls }) } if (feedType === 'following') { return await switchFeed('following', { pubkey }) } else { console.log('activeRelaySetId', activeRelaySetId) await switchFeed('relays', { activeRelaySetId }) } } init() }, []) useEffect(() => { if (pubkey && feedType === 'following') { switchFeed('following', { pubkey }) } }, [feedType, pubkey]) const switchFeed = async ( feedType: TFeedType, options: { activeRelaySetId?: string | null temporaryRelayUrls?: string[] | null pubkey?: string | null } = {} ) => { setIsReady(false) if (feedType === 'relays') { const relaySetId = options.activeRelaySetId ?? (relaySets.length > 0 ? relaySets[0].id : null) if (!relaySetId) { return setIsReady(true) } 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({}) storage.setActiveRelaySetId(relaySet.id) storage.setFeedType(feedType) } return setIsReady(true) } if (feedType === 'following') { if (!options.pubkey) { return setIsReady(true) } setFeedType(feedType) setActiveRelaySetId(null) const [relayList, followings] = await Promise.all([ getRelayList(options.pubkey), getFollowings(options.pubkey) ]) setRelayUrls(relayList.read.concat(BIG_RELAY_URLS).slice(0, 4)) setFilter({ authors: followings.includes(options.pubkey) ? followings : [...followings, options.pubkey] }) storage.setFeedType(feedType) return setIsReady(true) } if (feedType === 'temporary') { const urls = options.temporaryRelayUrls ?? temporaryRelayUrls if (!urls.length) { return setIsReady(true) } setFeedType(feedType) setTemporaryRelayUrls(urls) setRelayUrls(urls) setActiveRelaySetId(null) setFilter({}) return setIsReady(true) } setIsReady(true) } return ( {children} ) }