handle quote notes with nostr: links and e tags

This commit is contained in:
hzrd149 2023-06-09 08:58:52 -04:00
parent b4878c5fce
commit 3939f6636c
6 changed files with 76 additions and 9 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": patch
---
Correctly handle quote notes with nostr: links and e tags

View File

@ -8,7 +8,7 @@ describe("Profile view", () => {
cy.contains("https://rsshub.app/pixiv/user/7569500@rsslay.nostr.moe");
});
it("should load a rss feed fiatjef", () => {
it("should load PABLOF7z", () => {
cy.visit("#/u/npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqutajft");
cy.contains("npub1l2vyh...3afqutajft");

17
cypress/e2e/thread.cy.ts Normal file
View File

@ -0,0 +1,17 @@
describe("Thread", () => {
it("should handle quote notes with e tags correctly", () => {
cy.visit(
"#/n/nevent1qqsx2lnyuke6vmsrz9fdrd6uwjy0g0e9l6menfgdj5truugkh9qmkkgpzpmhxue69uhkummnw3ezuamfdejszrthwden5te0dehhxtnvdakqgc9md6"
);
// find first note
cy.get(".chakra-card")
.first()
.within(() => {
// get quoted note
cy.get(".chakra-card").within(() => {
cy.contains(/looking for people to send money/);
});
});
});
});

View File

@ -6,13 +6,14 @@ import { UserLink } from "../user-link";
import { EventPointer, ProfilePointer } from "nostr-tools/lib/nip19";
import { Link } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { matchNostrLink } from "../../helpers/regexp";
// nostr:nevent1qqsthg2qlxp9l7egtwa92t8lusm7pjknmjwa75ctrrpcjyulr9754fqpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq36amnwvaz7tmwdaehgu3dwp6kytnhv4kxcmmjv3jhytnwv46q2qg5q9
// nostr:nevent1qqsq3wc73lqxd70lg43m5rul57d4mhcanttjat56e30yx5zla48qzlspz9mhxue69uhkummnw3e82efwvdhk6qgdwaehxw309ahx7uewd3hkcq5hsum
export function embedNostrLinks(content: EmbedableContent) {
return embedJSX(content, {
name: "nostr-link",
regexp: /(nostr:|@)?((npub|note|nprofile|nevent)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/i,
regexp: matchNostrLink,
render: (match) => {
try {
const decoded = nip19.decode(match[2]);

View File

@ -3,26 +3,69 @@ import { getEventRelays } from "../services/event-relays";
import { DraftNostrEvent, isETag, isPTag, NostrEvent, RTag, Tag } from "../types/nostr-event";
import { RelayConfig, RelayMode } from "../classes/relay";
import accountService from "../services/account";
import { Kind } from "nostr-tools";
import { Kind, nip19 } from "nostr-tools";
import { matchNostrLink } from "./regexp";
export function isReply(event: NostrEvent | DraftNostrEvent) {
return event.kind === 1 && !!getReferences(event).replyId;
}
export function isRepost(event: NostrEvent | DraftNostrEvent) {
return event.kind === 6;
const match = event.content.match(matchNostrLink);
return event.kind === 6 || (match && match[0].length === event.content.length);
}
export function truncatedId(id: string, keep = 6) {
return id.substring(0, keep) + "..." + id.substring(id.length - keep);
}
export function getContentTagRefs(content: string) {
return Array.from(content.matchAll(/#\[(\d+)\]/gi)).map((m) => parseInt(m[1]));
/**
* returns an array of tag indexes that are referenced in the content
* either with the legacy #[0] syntax or nostr:xxxxx links
*/
export function getContentTagRefs(content: string, tags: Tag[]) {
const indexes = new Set();
Array.from(content.matchAll(/#\[(\d+)\]/gi)).forEach((m) => indexes.add(parseInt(m[1])));
const linkMatches = Array.from(content.matchAll(new RegExp(matchNostrLink, "gi")));
for (const [_, _prefix, link] of linkMatches) {
try {
const decoded = nip19.decode(link);
let type: string;
let id: string;
switch (decoded.type) {
case "npub":
id = decoded.data;
type = "p";
break;
case "nprofile":
id = decoded.data.pubkey;
type = "p";
break;
case "note":
id = decoded.data;
type = "e";
break;
case "nevent":
id = decoded.data.id;
type = "e";
break;
}
let t = tags.find((t) => t[0] === type && t[1] === id);
if (t) {
let index = tags.indexOf(t);
indexes.add(index);
}
} catch (e) {}
}
return Array.from(indexes);
}
export function filterTagsByContentRefs(content: string, tags: Tag[], referenced = true) {
const contentTagRefs = getContentTagRefs(content);
const contentTagRefs = getContentTagRefs(content, tags);
const newTags: Tag[] = [];
for (let i = 0; i < tags.length; i++) {
@ -40,7 +83,7 @@ export function getReferences(event: NostrEvent | DraftNostrEvent) {
const events = eTags.map((t) => t[1]);
const pubkeys = pTags.map((t) => t[1]);
const contentTagRefs = getContentTagRefs(event.content);
const contentTagRefs = getContentTagRefs(event.content, event.tags);
let replyId = eTags.find((t) => t[3] === "reply")?.[1];
let rootId = eTags.find((t) => t[3] === "root")?.[1];
@ -134,7 +177,6 @@ export function buildQuoteRepost(event: NostrEvent): DraftNostrEvent {
return {
kind: Kind.Text,
// TODO: be smarter about picking relay
tags,
content: "#[0]",
created_at: moment().unix(),

View File

@ -1,3 +1,5 @@
export const mentionNpubOrNote = /@?((npub1|note1)[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58})/gi;
export const matchImageUrls =
/https?:\/\/([\dA-z\.-]+\.[A-z\.]{2,12})((?:\/[\+~%\/\.\w\-_]*)?\.(?:svg|gif|png|jpg|jpeg|webp|avif))(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/i;
export const matchNostrLink = /(nostr:|@)?((npub|note|nprofile|nevent)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/i;