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.
 
 
 

118 lines
4.4 KiB

import { buildATag, buildETag, createBookmarkDraftEvent } from '@/lib/draft-event'
import { getReplaceableCoordinateFromEvent, isReplaceableEvent } from '@/lib/event'
import { normalizeUrl } from '@/lib/url'
import { BIG_RELAY_URLS, FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS } from '@/constants'
import client from '@/services/client.service'
import { Event } from 'nostr-tools'
import { createContext, useCallback, useContext } from 'react'
import { useNostr } from './NostrProvider'
import { useFavoriteRelays } from './FavoriteRelaysProvider'
type TBookmarksContext = {
addBookmark: (event: Event) => Promise<void>
removeBookmark: (event: Event) => Promise<void>
}
const BookmarksContext = createContext<TBookmarksContext | undefined>(undefined)
export const useBookmarks = () => {
const context = useContext(BookmarksContext)
if (!context) {
throw new Error('useBookmarks must be used within a BookmarksProvider')
}
return context
}
export function BookmarksProvider({ children }: { children: React.ReactNode }) {
const { pubkey: accountPubkey, publish, updateBookmarkListEvent } = useNostr()
const { favoriteRelays } = useFavoriteRelays()
// Build comprehensive relay list for publishing (same as ProfileFeed)
const buildComprehensiveRelayList = useCallback(async () => {
const myRelayList = accountPubkey ? await client.fetchRelayList(accountPubkey) : { write: [], read: [] }
const allRelays = [
...(myRelayList.read || []), // User's inboxes (kind 10002)
...(myRelayList.write || []), // User's outboxes (kind 10002)
...(favoriteRelays || []), // User's favorite relays (kind 10012)
...BIG_RELAY_URLS, // Big relays
...FAST_READ_RELAY_URLS, // Fast read relays
...FAST_WRITE_RELAY_URLS // Fast write relays
]
const normalizedRelays = allRelays
.map(url => normalizeUrl(url))
.filter((url): url is string => !!url)
return Array.from(new Set(normalizedRelays))
}, [accountPubkey, favoriteRelays])
const addBookmark = async (event: Event) => {
if (!accountPubkey) return
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
const currentTags = bookmarkListEvent?.tags || []
const isReplaceable = isReplaceableEvent(event.kind)
const eventKey = isReplaceable ? getReplaceableCoordinateFromEvent(event) : event.id
if (
currentTags.some((tag) =>
isReplaceable
? tag[0] === 'a' && tag[1] === eventKey
: tag[0] === 'e' && tag[1] === eventKey
)
) {
return
}
const newBookmarkDraftEvent = createBookmarkDraftEvent(
[...currentTags, isReplaceable ? buildATag(event) : buildETag(event.id, event.pubkey)],
bookmarkListEvent?.content
)
// Use the same comprehensive relay list as pins for publishing
const comprehensiveRelays = await buildComprehensiveRelayList()
console.log('[BookmarksProvider] Publishing to comprehensive relays:', comprehensiveRelays)
const newBookmarkEvent = await publish(newBookmarkDraftEvent, {
specifiedRelayUrls: comprehensiveRelays
})
await updateBookmarkListEvent(newBookmarkEvent)
}
const removeBookmark = async (event: Event) => {
if (!accountPubkey) return
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
if (!bookmarkListEvent) return
const isReplaceable = isReplaceableEvent(event.kind)
const eventKey = isReplaceable ? getReplaceableCoordinateFromEvent(event) : event.id
const newTags = bookmarkListEvent.tags.filter((tag) =>
isReplaceable ? tag[0] !== 'a' || tag[1] !== eventKey : tag[0] !== 'e' || tag[1] !== eventKey
)
if (newTags.length === bookmarkListEvent.tags.length) return
const newBookmarkDraftEvent = createBookmarkDraftEvent(newTags, bookmarkListEvent.content)
// Use the same comprehensive relay list as pins for publishing
const comprehensiveRelays = await buildComprehensiveRelayList()
console.log('[BookmarksProvider] Publishing to comprehensive relays:', comprehensiveRelays)
const newBookmarkEvent = await publish(newBookmarkDraftEvent, {
specifiedRelayUrls: comprehensiveRelays
})
await updateBookmarkListEvent(newBookmarkEvent)
}
return (
<BookmarksContext.Provider
value={{
addBookmark,
removeBookmark
}}
>
{children}
</BookmarksContext.Provider>
)
}