import { buildAccountListRelayUrlsForMerge } from '@/lib/account-list-relay-urls' import { createFollowListDraftEvent } from '@/lib/draft-event' import { publishFollowListPreimageToHistory } from '@/lib/follow-list-history' import { dedupePTagsAppendPubkey, fetchLatestReplaceableListEvent, removePubkeyFromPTags } from '@/lib/replaceable-list-latest' import { getPubkeysFromPTags } from '@/lib/tag' import client from '@/services/client.service' import { kinds } from 'nostr-tools' import { useMemo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useNostr } from './NostrProvider' import { useFavoriteRelays } from './FavoriteRelaysProvider' import { FollowListContext } from './follow-list-context' export function FollowListProvider({ children }: { children: React.ReactNode }) { const { t } = useTranslation() const { pubkey: accountPubkey, followListEvent, publish, updateFollowListEvent } = useNostr() const { favoriteRelays, blockedRelays } = useFavoriteRelays() const followings = useMemo( () => (followListEvent ? getPubkeysFromPTags(followListEvent.tags) : []), [followListEvent] ) const buildMergeRelays = useCallback(async () => { if (!accountPubkey) return [] as string[] return buildAccountListRelayUrlsForMerge({ accountPubkey, favoriteRelays: favoriteRelays ?? [], blockedRelays }) }, [accountPubkey, favoriteRelays, blockedRelays]) const mergeLatestFollowTags = async (): Promise<{ tags: string[][]; content: string | undefined } | null> => { if (!accountPubkey) return null const relays = await buildMergeRelays() let latest = (await fetchLatestReplaceableListEvent(accountPubkey, kinds.Contacts, relays)) ?? null if (!latest) { latest = (await client.fetchFollowListEvent(accountPubkey)) ?? null } if (!latest) { const result = confirm(t('FollowListNotFoundConfirmation')) if (!result) return null } return { tags: latest?.tags ?? [], content: latest?.content } } const follow = async (pubkey: string) => { if (!accountPubkey) return const base = await mergeLatestFollowTags() if (base === null) return await publishFollowListPreimageToHistory(publish, base.tags, base.content) const mergedTags = dedupePTagsAppendPubkey(base.tags, pubkey) const newFollowListDraftEvent = createFollowListDraftEvent(mergedTags, base.content) const newFollowListEvent = await publish(newFollowListDraftEvent) await updateFollowListEvent(newFollowListEvent) } const followMany = async (pubkeys: string[]) => { if (!accountPubkey) return const unique = [...new Set(pubkeys.map((p) => p.trim().toLowerCase()).filter(Boolean))] if (unique.length === 0) return const base = await mergeLatestFollowTags() if (base === null) return await publishFollowListPreimageToHistory(publish, base.tags, base.content) let mergedTags = base.tags for (const pk of unique) { mergedTags = dedupePTagsAppendPubkey(mergedTags, pk) } const newFollowListDraftEvent = createFollowListDraftEvent(mergedTags, base.content) const newFollowListEvent = await publish(newFollowListDraftEvent) await updateFollowListEvent(newFollowListEvent) } const unfollow = async (pubkey: string) => { if (!accountPubkey) return const relays = await buildMergeRelays() let latest = (await fetchLatestReplaceableListEvent(accountPubkey, kinds.Contacts, relays)) ?? null if (!latest) { latest = (await client.fetchFollowListEvent(accountPubkey)) ?? null } if (!latest) return await publishFollowListPreimageToHistory(publish, latest.tags, latest.content) const newFollowListDraftEvent = createFollowListDraftEvent( removePubkeyFromPTags(latest.tags, pubkey), latest.content ) const newFollowListEvent = await publish(newFollowListDraftEvent) await updateFollowListEvent(newFollowListEvent) } return ( {children} ) }