Files
nostrudel/src/helpers/nostr/post.ts
hzrd149 d0a510261e rebuild event tag reference helpers
DMs: refocus input after sending
2024-01-01 15:35:38 -06:00

133 lines
4.5 KiB
TypeScript

import { DraftNostrEvent, NostrEvent, Tag } from "../../types/nostr-event";
import { getMatchEmoji, getMatchHashtag } from "../regexp";
import { getReferences } from "./events";
import { getPubkeyFromDecodeResult, safeDecode } from "../nip19";
import { Emoji } from "../../providers/global/emoji-provider";
import { EventSplit } from "./zaps";
import { unique } from "../array";
import relayHintService from "../../services/event-relay-hint";
function addTag(tags: Tag[], tag: Tag, overwrite = false) {
if (tags.some((t) => t[0] === tag[0] && t[1] === tag[1])) {
if (overwrite) {
return tags.map((t) => {
if (t[0] === tag[0] && t[1] === tag[1]) return tag;
return t;
});
}
return tags;
}
return [...tags, tag];
}
function AddEtag(tags: Tag[], eventId: string, relayHint?: string, type?: string, overwrite = false) {
const hint = relayHint || relayHintService.getEventPointerRelayHint(eventId) || "";
const tag = type ? ["e", eventId, hint, type] : ["e", eventId, hint];
if (tags.some((t) => t[0] === tag[0] && t[1] === tag[1] && t[3] === tag[3])) {
if (overwrite) {
return tags.map((t) => {
if (t[0] === tag[0] && t[1] === tag[1]) return tag;
return t;
});
}
return tags;
}
return [...tags, tag];
}
/** adds the "root" and "reply" E tags */
export function addReplyTags(draft: DraftNostrEvent, replyTo: NostrEvent) {
const updated: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
const refs = getReferences(replyTo);
const rootId = refs.root?.e?.id ?? replyTo.id;
const rootRelayHint = refs.root?.e?.relays?.[0];
const replyId = replyTo.id;
const replyRelayHint = relayHintService.getEventPointerRelayHint(replyId);
updated.tags = AddEtag(updated.tags, rootId, rootRelayHint, "root", true);
updated.tags = AddEtag(updated.tags, replyId, replyRelayHint, "reply", true);
return updated;
}
/** ensure a list of pubkeys are present on an event */
export function ensureNotifyPubkeys(draft: DraftNostrEvent, pubkeys: string[]) {
const updated: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
for (const pubkey of pubkeys) {
updated.tags = addTag(updated.tags, ["p", pubkey], false);
}
return updated;
}
export function correctContentMentions(content: string) {
return content.replace(/(\s|^)(?:@)?(npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58})/gi, "$1nostr:$2");
}
export function getContentMentions(content: string) {
const matched = content.matchAll(/nostr:(npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58})/gi);
return unique(
Array.from(matched)
.map((m) => {
const parsed = safeDecode(m[1]);
return parsed && getPubkeyFromDecodeResult(parsed);
})
.filter(Boolean) as string[],
);
}
export function ensureNotifyContentMentions(draft: DraftNostrEvent) {
const mentions = getContentMentions(draft.content);
return mentions.length > 0 ? ensureNotifyPubkeys(draft, mentions) : draft;
}
export function createHashtagTags(draft: DraftNostrEvent) {
const updatedDraft: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
// create tags for all occurrences of #hashtag
const matches = updatedDraft.content.matchAll(getMatchHashtag());
for (const [_, space, hashtag] of matches) {
const lower = hashtag.toLocaleLowerCase();
if (!updatedDraft.tags.find((t) => t[0] === "t" && t[1] === lower)) {
updatedDraft.tags.push(["t", lower]);
}
}
return updatedDraft;
}
export function createEmojiTags(draft: DraftNostrEvent, emojis: Emoji[]) {
const updatedDraft: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
// create tags for all occurrences of #hashtag
const matches = updatedDraft.content.matchAll(getMatchEmoji());
for (const [_, name] of matches) {
const emoji = emojis.find((e) => e.name === name);
if (emoji?.url) {
updatedDraft.tags = addTag(updatedDraft.tags, ["emoji", emoji.name, emoji.url]);
}
}
return updatedDraft;
}
export function setZapSplit(draft: DraftNostrEvent, split: EventSplit) {
const updatedDraft: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
// TODO: get best input relay for user
const zapTags = split.map((p) => ["zap", p.pubkey, "", String(p.percent * 100)]);
updatedDraft.tags.push(...zapTags);
return updatedDraft;
}
export function finalizeNote(draft: DraftNostrEvent) {
let updated: DraftNostrEvent = { ...draft, tags: Array.from(draft.tags) };
updated.content = correctContentMentions(updated.content);
updated = createHashtagTags(updated);
return updated;
}