flotilla/src/app/reactions.ts
2026-07-29 15:52:13 -07:00

53 lines
1.9 KiB
TypeScript

import {removeUndefined} from "@welshman/lib"
import type {EventContent, TrustedEvent} from "@welshman/util"
import {deletes, reactions, relays, wraps} from "@app/core"
export type ReactionTarget = {
url?: string
h?: string
// Where to publish, when it isn't just the space's own relay.
urls?: string[]
}
// A reaction has to travel the same way as the event it's about — protected on relays that
// enforce NIP-70, and tagged into the room the event lives in.
export const publishReaction = async (
event: TrustedEvent,
{content, tags}: EventContent,
{url, h, urls}: ReactionTarget,
) => {
const protect = url ? await relays.get().hasNip(url, 70) : false
const command = await reactions.get().react(event, content, writer => {
writer.addTags(...tags).setProtected(protect)
if (url && h) {
writer.setRoom(url, h)
}
})
return command.publishToRelays(urls ?? removeUndefined([url]))
}
export const retractReaction = async (reaction: TrustedEvent, {url, urls}: ReactionTarget) => {
const protect = url ? await relays.get().hasNip(url, 70) : false
const command = await deletes.get().deleteEvent(reaction, writer => writer.setProtected(protect))
return command.publishToRelays(urls ?? removeUndefined([url]))
}
// Chat reactions go out gift-wrapped to the conversation rather than to a relay.
export const publishWrappedReaction = async (
event: TrustedEvent,
{content, tags}: EventContent,
pubkeys: string[],
) => {
const command = await reactions.get().react(event, content, writer => writer.addTags(...tags))
return wraps.get().publish({event: command.event, recipients: pubkeys, pow: 16})
}
export const retractWrappedReaction = async (reaction: TrustedEvent, pubkeys: string[]) => {
const command = await deletes.get().deleteEvent(reaction)
return wraps.get().publish({event: command.event, recipients: pubkeys, pow: 16})
}