diff --git a/src/element/chat-message.tsx b/src/element/chat-message.tsx
index 365e7ec..3ce52db 100644
--- a/src/element/chat-message.tsx
+++ b/src/element/chat-message.tsx
@@ -14,7 +14,7 @@ import { EmojiPicker } from "./emoji-picker";
import { Icon } from "./icon";
import { Emoji } from "./emoji";
import { Profile } from "./profile";
-import { Markdown } from "./markdown";
+import { Text } from "element/text";
import { SendZapsDialog } from "./send-zap";
import { findTag } from "../utils";
import type { EmojiPack } from "../hooks/emoji";
@@ -152,12 +152,7 @@ export function ChatMessage({
pubkey={ev.pubkey}
profile={profile}
/>
-
+
{(hasReactions || hasZaps) && (
{hasZaps && (
diff --git a/src/element/live-chat.css b/src/element/live-chat.css
index c789a60..02924fc 100644
--- a/src/element/live-chat.css
+++ b/src/element/live-chat.css
@@ -357,12 +357,3 @@
height: 18px;
border-radius: unset;
}
-
-.message .markdown {
- font-size: 14px;
- line-height: normal;
-}
-
-.message .markdown .emoji {
- width: unset;
-}
diff --git a/src/element/markdown.tsx b/src/element/markdown.tsx
index 3511b33..c867686 100644
--- a/src/element/markdown.tsx
+++ b/src/element/markdown.tsx
@@ -1,204 +1,20 @@
import "./markdown.css";
import { createElement } from "react";
-import { parseNostrLink } from "@snort/system";
import { useMemo } from "react";
import ReactMarkdown from "react-markdown";
-import { Address } from "element/Address";
-import { Event } from "element/Event";
-import { Mention } from "element/mention";
-import { Emoji } from "element/emoji";
import { HyperText } from "element/hypertext";
-
-const MentionRegex = /(#\[\d+\])/gi;
-const NostrPrefixRegex = /^nostr:/;
-const EmojiRegex = /:([\w-]+):/g;
-
-function extractEmoji(fragments: Fragment[], tags: string[][]) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(EmojiRegex).map((i) => {
- const t = tags.find((a) => a[0] === "emoji" && a[1] === i);
- if (t) {
- return
;
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractMentions(fragments, tags) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(MentionRegex).map((match) => {
- const matchTag = match.match(/#\[(\d+)\]/);
- if (matchTag && matchTag.length === 2) {
- const idx = parseInt(matchTag[1]);
- const ref = tags?.find((a, i) => i === idx);
- if (ref) {
- switch (ref[0]) {
- case "p": {
- return
;
- }
- case "a": {
- return
;
- }
- default:
- // todo: e and t mentions
- return ref[1];
- }
- }
- return null;
- } else {
- return match;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractNprofiles(fragments) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/(nostr:nprofile1[a-z0-9]+)/g).map((i) => {
- if (i.startsWith("nostr:nprofile1")) {
- try {
- const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
- return
;
- } catch (error) {
- return i;
- }
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractNpubs(fragments) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/(nostr:npub1[a-z0-9]+)/g).map((i) => {
- if (i.startsWith("nostr:npub1")) {
- try {
- const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
- return
;
- } catch (error) {
- return i;
- }
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractNevents(fragments) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/(nostr:nevent1[a-z0-9]+)/g).map((i) => {
- if (i.startsWith("nostr:nevent1")) {
- try {
- const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
- return
;
- } catch (error) {
- return i;
- }
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractNaddrs(fragments) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/(nostr:naddr1[a-z0-9]+)/g).map((i) => {
- if (i.startsWith("nostr:naddr1")) {
- try {
- const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
- return
;
- } catch (error) {
- console.error(error);
- return i;
- }
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function extractNoteIds(fragments) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/(nostr:note1[a-z0-9]+)/g).map((i) => {
- if (i.startsWith("nostr:note1")) {
- try {
- const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
- return
;
- } catch (error) {
- return i;
- }
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
-
-function transformText(ps, tags) {
- let fragments = extractMentions(ps, tags);
- fragments = extractNprofiles(fragments);
- fragments = extractNevents(fragments);
- fragments = extractNaddrs(fragments);
- fragments = extractNoteIds(fragments);
- fragments = extractNpubs(fragments);
- fragments = extractEmoji(fragments, tags);
-
- return fragments;
-}
+import { transformText } from "element/text";
interface MarkdownProps {
content: string;
tags?: string[];
- enableParagraphs?: booleam;
}
export function Markdown({
content,
tags = [],
- enableParagraphs = true,
element = "div",
}: MarkdownProps) {
const components = useMemo(() => {
@@ -208,17 +24,12 @@ export function Markdown({
},
td: ({ children }) =>
children &&
{transformText(children, tags)} | ,
- p: ({ children }) =>
- enableParagraphs ? (
-
{transformText(children, tags)}
- ) : (
- transformText(children, tags)
- ),
+ p: ({ children }) =>
{transformText(children, tags)}
,
a: (props) => {
return
{props.children};
},
};
- }, [tags, enableParagraphs]);
+ }, [tags]);
return createElement(
element,
{ className: "markdown" },
diff --git a/src/element/text.tsx b/src/element/text.tsx
index 56070f3..e419156 100644
--- a/src/element/text.tsx
+++ b/src/element/text.tsx
@@ -1,32 +1,18 @@
import { useMemo, type ReactNode } from "react";
-import { validateNostrLink } from "@snort/system";
+
+import { parseNostrLink, validateNostrLink } from "@snort/system";
+
+import { Address } from "element/Address";
+import { Event } from "element/Event";
+import { Mention } from "element/mention";
+import { Emoji } from "element/emoji";
+import { HyperText } from "element/hypertext";
import { splitByUrl } from "utils";
-import { Emoji } from "./emoji";
-import { HyperText } from "./hypertext";
type Fragment = string | ReactNode;
-function transformText(fragments: Fragment[], tags: string[][]) {
- return extractLinks(extractEmoji(fragments, tags));
-}
-
-function extractEmoji(fragments: Fragment[], tags: string[][]) {
- return fragments
- .map((f) => {
- if (typeof f === "string") {
- return f.split(/:([\w-]+):/g).map((i) => {
- const t = tags.find((a) => a[0] === "emoji" && a[1] === i);
- if (t) {
- return
;
- } else {
- return i;
- }
- });
- }
- return f;
- })
- .flat();
-}
+const NostrPrefixRegex = /^nostr:/;
+const EmojiRegex = /:([\w-]+):/g;
function extractLinks(fragments: Fragment[]) {
return fragments
@@ -74,6 +60,147 @@ function extractLinks(fragments: Fragment[]) {
.flat();
}
+function extractEmoji(fragments: Fragment[], tags: string[][]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(EmojiRegex).map((i) => {
+ const t = tags.find((a) => a[0] === "emoji" && a[1] === i);
+ if (t) {
+ return
;
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+function extractNprofiles(fragments: Fragment[]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(/(nostr:nprofile1[a-z0-9]+)/g).map((i) => {
+ if (i.startsWith("nostr:nprofile1")) {
+ try {
+ const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
+ return
;
+ } catch (error) {
+ return i;
+ }
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+function extractNpubs(fragments: Fragment[]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(/(nostr:npub1[a-z0-9]+)/g).map((i) => {
+ if (i.startsWith("nostr:npub1")) {
+ try {
+ const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
+ return
;
+ } catch (error) {
+ return i;
+ }
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+function extractNevents(fragments: Fragment[]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(/(nostr:nevent1[a-z0-9]+)/g).map((i) => {
+ if (i.startsWith("nostr:nevent1")) {
+ try {
+ const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
+ return
;
+ } catch (error) {
+ return i;
+ }
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+function extractNaddrs(fragments: Fragment[]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(/(nostr:naddr1[a-z0-9]+)/g).map((i) => {
+ if (i.startsWith("nostr:naddr1")) {
+ try {
+ const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
+ return
;
+ } catch (error) {
+ console.error(error);
+ return i;
+ }
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+function extractNoteIds(fragments: Fragment[]) {
+ return fragments
+ .map((f) => {
+ if (typeof f === "string") {
+ return f.split(/(nostr:note1[a-z0-9]+)/g).map((i) => {
+ if (i.startsWith("nostr:note1")) {
+ try {
+ const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
+ return
;
+ } catch (error) {
+ return i;
+ }
+ } else {
+ return i;
+ }
+ });
+ }
+ return f;
+ })
+ .flat();
+}
+
+export function transformText(ps: Fragment[], tags: Array
) {
+ let fragments = extractEmoji(ps, tags);
+ fragments = extractNprofiles(fragments);
+ fragments = extractNevents(fragments);
+ fragments = extractNaddrs(fragments);
+ fragments = extractNoteIds(fragments);
+ fragments = extractNpubs(fragments);
+ fragments = extractLinks(fragments);
+
+ return fragments;
+}
+
export function Text({ content, tags }: { content: string; tags: string[][] }) {
// todo: RTL langugage support
const element = useMemo(() => {