Expand description
NIP-30 / NIP-51 custom emoji + pack support.
Phase 1 (read path):
- Parse kind 30030 emoji sets and kind 10030 user emoji lists.
- Fetch a user’s subscribed packs from relays, persist them locally.
- Expose a flat
EmojiPackAPI to the frontend for picker rendering.
Spec: https://nips.nostr.com/30, https://nips.nostr.com/51.
Metadata interop: NIP-51 standardises title / image / description
tags on kind 30030. Ditto and Nostria emit non-standard name /
picture / about instead. Vector reads both with spec preference
and (eventually) dual-writes both on publish.
Structs§
- Emoji
Pack - Pack
Address - Parsed NIP-19 / NIP-51 set address.
- Pack
Emoji
Enums§
- Pack
Fetch Outcome - What one refresh sweep learned about a subscribed pack.
Constants§
- KIND_
EMOJI_ SET - NIP-51 kind for an “Emoji set” (parameterised replaceable).
- MAX_
EMOJIS_ PER_ PACK - Base (free, tier-0) emojis-per-own-pack cap. Shared packs received from the network may exceed this — only what we author is capped, and the frontend truncates oversized received packs at display time.
- MAX_
EQUIPPED_ PACKS - Base (free, tier-0) equipped-pack cap. Named const for the frontend mirror.
- PACK_
STATUS_ ACTIVE - Pack is live on its relays (or hasn’t been judged otherwise).
- PACK_
STATUS_ MISSING - No tombstone, but the pack has been absent across enough clean sweeps of
live relays (see the gauntlet in
apply_pack_health) that it’s considered gone. - PACK_
STATUS_ REVOKED - A deterministic tombstone was seen: the author replaced the pack with an EMPTY kind 30030 (Vector’s own delete flow) or published a kind-5 deletion naming it.
Functions§
- apply_
pack_ health - Apply one refresh sweep’s verdict to a pack’s persisted health. Returns
truewhen the pack’s STATUS changed (caller emits a UI refresh). - clear_
nip65_ cache - Drop every cached relay list (account swap — entries hold contact-graph metadata from the prior session, mirroring the inbox-relay cache’s swap hygiene).
- decrypt_
subscribed_ addresses - Decrypt + parse a kind 10030 event’s encrypted subscription list.
- delete_
own_ pack - Tombstone one of the user’s own packs by publishing an empty kind
30030 with just the
dtag (relays replace the prior payload), drop the local subscription, and republish kind 10030. - effective_
display_ cap - Per-pack emoji cap mirrored from the frontend’s
MAX_DISPLAY_EMOJIS_PER_PACK(applyBadgeLimits). The send resolver caps each subscribed pack to this many items so its~Ndisambiguation of duplicate shortcodes matches exactly what the picker displayed — otherwise a large pack’s hidden tail (the picker only shows the first N) would enter the candidate set and shift the indices. - effective_
max_ emojis_ per_ pack - Per-pack emoji authoring cap for the current account, scaled by effective tier.
- effective_
max_ equipped_ packs - In-app equipped-pack cap for the current account, scaled by effective tier. Gate the in-app subscribe/create action on this — never the load/display path.
- fetch_
pack_ by_ naddr - Preview-only fetch by naddr — resolves + parses but never touches local DB. Lets the UI render a “Pack Preview” card without committing to a subscription.
- fetch_
subscribed_ packs - Fetch the user’s kind 10030 list, resolve every referenced pack, and persist the result locally. Session-guarded against an account swap landing the new account’s pack list in account A’s DB.
- get_
or_ fetch_ theme_ pack - Resolve a theme pack cache-first: return the locally-persisted copy
instantly when present (and refresh it in the background), otherwise fetch
live, persist, and return. Theme packs are pinned by the active theme, not
subscribed —
save_packpersists their data without a subscription row, so they survive restarts (no per-session relay round-trip) yet never occupy an equip slot or land in the kind-10030 list. ReturnsNoneif uncached and the live fetch finds nothing. - get_
theme_ slot_ anchor - Return the theme-slot anchor as a NADDR (the pack the theme slot renders
immediately after), or
""when the slot is at the top. Degrades to top on a malformed stored anchor rather than breaking the picker. - load_
all_ packs - Load every locally-cached pack the user is currently subscribed to (plus their own packs, which always count). Hydrated with items. Cached non-subscribed pack rows stay in the DB so historic reactions still resolve their image URLs — they’re just hidden from the picker.
- load_
cached_ pack - Load a single cached pack by its raw
kind:pubkey:identifieraddr, regardless of subscription status. Used by the theme-pack path: a theme pack is persisted viasave_pack(so it loads instantly across sessions) but never gets a subscription row, soload_all_packsrightly hides it. - load_
subscriptions - naddr_
from_ addr - Encode a pack
addr(kind:pubkey:identifier) into a NIP-19naddr1...bech32 string. Used by the share-pack flow to put a portable reference on the user’s clipboard. - parse_
naddr - Decode a NIP-19
naddr1...into aPackAddress. Rejects coordinates that don’t point at kind 30030 so a malformed paste can’t pull in an unrelated replaceable event. - parse_
pack_ address - Parse a
kind:pubkey-hex:d-tagaddress as found in kind 10030atags. Rejects anything that isn’t kind 30030 — we don’t want a malformed list pulling in random replaceable events. - parse_
pack_ from_ event - Parse a kind 30030 event into an EmojiPack. Returns
Noneif the event is missing adtag or has zero valid emoji rows. - publish_
emoji_ list - Publish a kind 10030 “Emojis” list containing every subscribed pack.
- publish_
pack - Publish (or replace) one of the user’s own packs as a kind 30030 event, persist it locally, and add it to the subscription list so the picker surfaces it immediately. SessionGuard-gated so a mid- network account swap can’t push account A’s pack signed by B’s key.
- refresh_
subscribed_ packs - Convenience entry point that grabs the client + my_pubkey internally
and runs the full subscribed-packs refresh. Intended for the boot
path; in-app commands pass an explicit
SessionGuardvia the lower helper to make the safety contract visible at every call site. - reorder_
emoji_ packs - Persist a user-defined display order for the equipped packs (including the theme slot) and republish kind 10030 so it syncs across devices.
- republish_
emoji_ list_ debounced - Coalesce rapid subscribe/unsubscribe taps into one network publish.
Captures
SessionGuardBEFORE the spawn boundary so a mid-debounce account swap can’t sign account A’s pack list with account B’s key. - resolve_
outbound_ emoji_ tags - Scan
contentfor:shortcode:patterns and resolve them against the user’s currently-subscribed packs (plus the active theme pack). Returns deduped emoji tags in first-match order. Used by the send pipeline to attach NIP-30 emoji tags so recipients without the pack subscribed still render. - save_
pack - save_
subscriptions - set_
theme_ emoji_ tags - Register (or clear, with an empty vec) the active theme pack’s emoji so the send resolver can tag them even though they aren’t a DB subscription.
- subscribe_
pack - Subscribe to a pack by naddr: fetch the pack, persist it + the subscription, then schedule a debounced republish of kind 10030. Returns the hydrated pack on success.
- unsubscribe_
pack - Unsubscribe locally and republish kind 10030 without the pack.
The pack row itself stays in
emoji_packs(caller may still want to render old reactions); only the subscription link is dropped.