8 changed files with 393 additions and 52 deletions
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
/** |
||||
* In-memory session cache for profile accordion fetches (per viewed profile pubkey). |
||||
* Survives collapsing/reopening the accordion; cleared on full page reload. |
||||
*/ |
||||
|
||||
import type { TProfileZap } from '@/hooks/useProfileInteractions' |
||||
import type { TProfileBadge } from '@/hooks/useProfileBadges' |
||||
import type { TProfileFollowPack } from '@/hooks/useProfileFollowPacks' |
||||
import type { Event } from 'nostr-tools' |
||||
|
||||
export type ProfileAccordionInteractionsSnapshot = { |
||||
zaps: TProfileZap[] |
||||
reactions: Event[] |
||||
comments: Event[] |
||||
} |
||||
|
||||
type Entry = { |
||||
relayUrls?: string[] |
||||
/** Fingerprint of relays used for interaction/badge/pack slices */ |
||||
relayUrlsKey?: string |
||||
interactions?: ProfileAccordionInteractionsSnapshot |
||||
badges?: TProfileBadge[] |
||||
followPacks?: TProfileFollowPack[] |
||||
/** viewer hex pubkey → reports */ |
||||
reportsByViewer?: Record<string, Event[]> |
||||
} |
||||
|
||||
const store = new Map<string, Entry>() |
||||
|
||||
export function profileAccordionRelayUrlsKey(urls: string[]): string { |
||||
if (urls.length === 0) return '' |
||||
return [...urls].sort().join('|') |
||||
} |
||||
|
||||
function getEntry(pubkey: string): Entry { |
||||
let e = store.get(pubkey) |
||||
if (!e) { |
||||
e = {} |
||||
store.set(pubkey, e) |
||||
} |
||||
return e |
||||
} |
||||
|
||||
export function profileAccordionGetCachedRelayUrls(pubkey: string): string[] | undefined { |
||||
const urls = getEntry(pubkey).relayUrls |
||||
return urls?.length ? urls : undefined |
||||
} |
||||
|
||||
export function profileAccordionSetRelayUrls(pubkey: string, urls: string[]): void { |
||||
const e = getEntry(pubkey) |
||||
const key = profileAccordionRelayUrlsKey(urls) |
||||
if (e.relayUrlsKey && e.relayUrlsKey !== key) { |
||||
delete e.interactions |
||||
delete e.badges |
||||
delete e.followPacks |
||||
} |
||||
e.relayUrls = urls |
||||
e.relayUrlsKey = key |
||||
} |
||||
|
||||
export function profileAccordionGetCachedInteractions( |
||||
pubkey: string, |
||||
relayKey: string |
||||
): ProfileAccordionInteractionsSnapshot | undefined { |
||||
const e = store.get(pubkey) |
||||
if (!e?.interactions || e.relayUrlsKey !== relayKey) return undefined |
||||
return e.interactions |
||||
} |
||||
|
||||
export function profileAccordionSetInteractions( |
||||
pubkey: string, |
||||
relayKey: string, |
||||
data: ProfileAccordionInteractionsSnapshot |
||||
): void { |
||||
const e = getEntry(pubkey) |
||||
e.relayUrlsKey = relayKey |
||||
e.interactions = data |
||||
} |
||||
|
||||
export function profileAccordionGetCachedBadges(pubkey: string, relayKey: string): TProfileBadge[] | undefined { |
||||
const e = store.get(pubkey) |
||||
if (!e?.badges || e.relayUrlsKey !== relayKey) return undefined |
||||
return e.badges |
||||
} |
||||
|
||||
export function profileAccordionSetBadges(pubkey: string, relayKey: string, badges: TProfileBadge[]): void { |
||||
const e = getEntry(pubkey) |
||||
e.relayUrlsKey = relayKey |
||||
e.badges = badges |
||||
} |
||||
|
||||
export function profileAccordionGetCachedFollowPacks( |
||||
pubkey: string, |
||||
relayKey: string |
||||
): TProfileFollowPack[] | undefined { |
||||
const e = store.get(pubkey) |
||||
if (!e?.followPacks || e.relayUrlsKey !== relayKey) return undefined |
||||
return e.followPacks |
||||
} |
||||
|
||||
export function profileAccordionSetFollowPacks( |
||||
pubkey: string, |
||||
relayKey: string, |
||||
packs: TProfileFollowPack[] |
||||
): void { |
||||
const e = getEntry(pubkey) |
||||
e.relayUrlsKey = relayKey |
||||
e.followPacks = packs |
||||
} |
||||
|
||||
export function profileAccordionGetCachedReports(profilePubkey: string, viewerPubkey: string): Event[] | undefined { |
||||
return getEntry(profilePubkey).reportsByViewer?.[viewerPubkey] |
||||
} |
||||
|
||||
export function profileAccordionSetReports( |
||||
profilePubkey: string, |
||||
viewerPubkey: string, |
||||
reports: Event[] |
||||
): void { |
||||
const e = getEntry(profilePubkey) |
||||
if (!e.reportsByViewer) e.reportsByViewer = {} |
||||
e.reportsByViewer[viewerPubkey] = reports |
||||
} |
||||
|
||||
export type ProfileAccordionCacheSlice = |
||||
| 'relayUrls' |
||||
| 'interactions' |
||||
| 'badges' |
||||
| 'followPacks' |
||||
| 'reports' |
||||
| 'all' |
||||
|
||||
export function profileAccordionInvalidate(pubkey: string, slice: ProfileAccordionCacheSlice = 'all'): void { |
||||
if (slice === 'all') { |
||||
store.delete(pubkey) |
||||
return |
||||
} |
||||
const e = store.get(pubkey) |
||||
if (!e) return |
||||
switch (slice) { |
||||
case 'relayUrls': |
||||
delete e.relayUrls |
||||
delete e.relayUrlsKey |
||||
delete e.interactions |
||||
delete e.badges |
||||
delete e.followPacks |
||||
break |
||||
case 'interactions': |
||||
delete e.interactions |
||||
break |
||||
case 'badges': |
||||
delete e.badges |
||||
break |
||||
case 'followPacks': |
||||
delete e.followPacks |
||||
break |
||||
case 'reports': |
||||
delete e.reportsByViewer |
||||
break |
||||
} |
||||
} |
||||
Loading…
Reference in new issue