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.
82 lines
2.6 KiB
82 lines
2.6 KiB
import { ExtendedKind } from '@/constants' |
|
import { extractBadgeDefinitionMedia } from '@/lib/badge-definition-media' |
|
import { tagNameEquals } from '@/lib/tag' |
|
import { Event } from 'nostr-tools' |
|
|
|
/** Legacy NIP-58 profile badges addressable `d` tag value. */ |
|
export const LEGACY_PROFILE_BADGES_D_TAG = 'profile_badges' |
|
|
|
export type ProfileBadgeEntry = { |
|
definitionCoordinate: string |
|
awardEventId: string |
|
} |
|
|
|
export type ResolvedProfileBadge = { |
|
definitionCoordinate: string |
|
awardEventId: string |
|
name: string |
|
description?: string |
|
imageUrl?: string |
|
} |
|
|
|
/** Parse consecutive `a` / `e` pairs from a NIP-58 profile badges list event. */ |
|
export function parseProfileBadgeEntries(event: Event | undefined): ProfileBadgeEntry[] { |
|
if (!event) return [] |
|
const out: ProfileBadgeEntry[] = [] |
|
const tags = event.tags |
|
for (let i = 0; i < tags.length; i++) { |
|
const t = tags[i] |
|
if (t[0] !== 'a' || !t[1]?.trim()) continue |
|
const next = tags[i + 1] |
|
if (next?.[0] === 'e' && next[1]?.trim()) { |
|
out.push({ definitionCoordinate: t[1].trim(), awardEventId: next[1].trim() }) |
|
i++ |
|
} |
|
} |
|
return out |
|
} |
|
|
|
export function isNip58ProfileBadgesListEvent(event: Event): boolean { |
|
if (event.kind === ExtendedKind.PROFILE_BADGES_LIST) return true |
|
if (event.kind !== ExtendedKind.PROFILE_BADGES) return false |
|
const d = event.tags.find(tagNameEquals('d'))?.[1]?.trim() |
|
return d === LEGACY_PROFILE_BADGES_D_TAG |
|
} |
|
|
|
export function parseAddressableCoordinate( |
|
coordinate: string |
|
): { kind: number; pubkey: string; d: string } | null { |
|
const trimmed = coordinate.trim() |
|
const idx1 = trimmed.indexOf(':') |
|
if (idx1 < 0) return null |
|
const idx2 = trimmed.indexOf(':', idx1 + 1) |
|
if (idx2 < 0) return null |
|
const kind = parseInt(trimmed.slice(0, idx1), 10) |
|
if (!Number.isFinite(kind)) return null |
|
return { |
|
kind, |
|
pubkey: trimmed.slice(idx1 + 1, idx2), |
|
d: trimmed.slice(idx2 + 1) |
|
} |
|
} |
|
|
|
export function resolveBadgeDisplayFromDefinition( |
|
entry: ProfileBadgeEntry, |
|
defEvent: Event | undefined |
|
): ResolvedProfileBadge { |
|
const parsed = parseAddressableCoordinate(entry.definitionCoordinate) |
|
const fallbackName = parsed?.d || entry.definitionCoordinate |
|
const name = |
|
defEvent?.tags.find(tagNameEquals('name'))?.[1]?.trim() || |
|
defEvent?.tags.find(tagNameEquals('d'))?.[1]?.trim() || |
|
fallbackName |
|
const description = defEvent?.tags.find(tagNameEquals('description'))?.[1]?.trim() |
|
const media = extractBadgeDefinitionMedia(defEvent) |
|
return { |
|
definitionCoordinate: entry.definitionCoordinate, |
|
awardEventId: entry.awardEventId, |
|
name, |
|
description: description || undefined, |
|
imageUrl: media.image ?? media.thumb |
|
} |
|
}
|
|
|