You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

167 lines
5.4 KiB

import { createInterestListDraftEvent } from '@/lib/draft-event'
import { normalizeTopic } from '@/lib/discussion-topics'
import client from '@/services/client.service'
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { useNostr } from './NostrProvider'
type TInterestListContext = {
subscribedTopics: Set<string>
changing: boolean
isSubscribed: (topic: string) => boolean
subscribe: (topic: string) => Promise<void>
unsubscribe: (topic: string) => Promise<void>
getSubscribedTopics: () => string[]
}
const InterestListContext = createContext<TInterestListContext | undefined>(undefined)
export const useInterestList = () => {
const context = useContext(InterestListContext)
if (!context) {
throw new Error('useInterestList must be used within an InterestListProvider')
}
return context
}
export function InterestListProvider({ children }: { children: React.ReactNode }) {
const { t } = useTranslation()
const { pubkey: accountPubkey, interestListEvent, publish, updateInterestListEvent } = useNostr()
const [topics, setTopics] = useState<string[]>([])
const subscribedTopics = useMemo(() => new Set(topics), [topics])
const [changing, setChanging] = useState(false)
useEffect(() => {
const updateTopics = () => {
if (!interestListEvent) {
setTopics([])
return
}
// Extract t-tags from the interest list
const topicTags = interestListEvent.tags
.filter(tag => tag[0] === 't' && tag[1])
.map(tag => normalizeTopic(tag[1]))
setTopics(topicTags)
}
updateTopics()
}, [interestListEvent])
const getSubscribedTopics = useCallback(() => {
return Array.from(subscribedTopics)
}, [subscribedTopics])
const isSubscribed = useCallback(
(topic: string): boolean => {
return subscribedTopics.has(normalizeTopic(topic))
},
[subscribedTopics]
)
const publishNewInterestListEvent = async (newTopics: string[]) => {
const newInterestListEvent = createInterestListDraftEvent(newTopics)
const publishedEvent = await publish(newInterestListEvent)
return publishedEvent
}
const subscribe = async (topic: string) => {
console.log('[InterestListProvider] subscribe called:', { topic, accountPubkey, changing })
if (!accountPubkey || changing) return
const normalizedTopic = normalizeTopic(topic)
if (subscribedTopics.has(normalizedTopic)) {
console.log('[InterestListProvider] Already subscribed to topic')
return
}
setChanging(true)
try {
console.log('[InterestListProvider] Fetching existing interest list event')
const interestListEvent = await client.fetchInterestListEvent(accountPubkey)
console.log('[InterestListProvider] Existing interest list event:', interestListEvent)
const currentTopics = interestListEvent
? interestListEvent.tags
.filter(tag => tag[0] === 't' && tag[1])
.map(tag => normalizeTopic(tag[1]))
: []
console.log('[InterestListProvider] Current topics:', currentTopics)
if (currentTopics.includes(normalizedTopic)) {
console.log('[InterestListProvider] Already subscribed to topic (from event)')
return
}
const newTopics = [...currentTopics, normalizedTopic]
console.log('[InterestListProvider] Creating new interest list with topics:', newTopics)
const newInterestListEvent = await publishNewInterestListEvent(newTopics)
console.log('[InterestListProvider] Published new interest list event:', newInterestListEvent)
await updateInterestListEvent(newInterestListEvent)
console.log('[InterestListProvider] Updated interest list event in state')
toast.success(t('Subscribed to topic'))
} catch (error) {
console.error('Failed to subscribe to topic:', error)
toast.error(t('Failed to subscribe to topic') + ': ' + (error as Error).message)
} finally {
setChanging(false)
}
}
const unsubscribe = async (topic: string) => {
if (!accountPubkey || changing) return
const normalizedTopic = normalizeTopic(topic)
if (!subscribedTopics.has(normalizedTopic)) {
return
}
setChanging(true)
try {
const interestListEvent = await client.fetchInterestListEvent(accountPubkey)
if (!interestListEvent) return
const currentTopics = interestListEvent.tags
.filter(tag => tag[0] === 't' && tag[1])
.map(tag => normalizeTopic(tag[1]))
const newTopics = currentTopics.filter(t => t !== normalizedTopic)
if (newTopics.length === currentTopics.length) {
// Topic wasn't in the list
return
}
const newInterestListEvent = await publishNewInterestListEvent(newTopics)
await updateInterestListEvent(newInterestListEvent)
toast.success(t('Unsubscribed from topic'))
} catch (error) {
console.error('Failed to unsubscribe from topic:', error)
toast.error(t('Failed to unsubscribe from topic') + ': ' + (error as Error).message)
} finally {
setChanging(false)
}
}
return (
<InterestListContext.Provider
value={{
subscribedTopics,
changing,
isSubscribed,
subscribe,
unsubscribe,
getSubscribedTopics
}}
>
{children}
</InterestListContext.Provider>
)
}