From d46327ec298f7a0310b9464f77b82056341660c0 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Tue, 11 Jul 2023 07:56:51 -0500 Subject: [PATCH] new post hashtags --- .changeset/curly-cats-grin.md | 5 +++++ .changeset/tall-rice-promise.md | 5 +++++ src/components/embed-types/nostr.tsx | 15 +++++++++++---- src/components/post-modal/index.tsx | 11 ++++++++++- src/helpers/embeds.ts | 14 ++++++++++++-- src/helpers/regexp.ts | 1 + 6 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 .changeset/curly-cats-grin.md create mode 100644 .changeset/tall-rice-promise.md diff --git a/.changeset/curly-cats-grin.md b/.changeset/curly-cats-grin.md new file mode 100644 index 000000000..a7dec0212 --- /dev/null +++ b/.changeset/curly-cats-grin.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Support hashtags in new post modal diff --git a/.changeset/tall-rice-promise.md b/.changeset/tall-rice-promise.md new file mode 100644 index 000000000..579a8d9b5 --- /dev/null +++ b/.changeset/tall-rice-promise.md @@ -0,0 +1,5 @@ +--- +"nostrudel": patch +--- + +Fix bug with non-english hashtags not showing diff --git a/src/components/embed-types/nostr.tsx b/src/components/embed-types/nostr.tsx index 9f30866d0..f0a36b0f9 100644 --- a/src/components/embed-types/nostr.tsx +++ b/src/components/embed-types/nostr.tsx @@ -6,7 +6,7 @@ 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"; +import { matchHashtag, matchNostrLink } from "../../helpers/regexp"; // nostr:nevent1qqsthg2qlxp9l7egtwa92t8lusm7pjknmjwa75ctrrpcjyulr9754fqpz3mhxue69uhhyetvv9ujuerpd46hxtnfduq36amnwvaz7tmwdaehgu3dwp6kytnhv4kxcmmjv3jhytnwv46q2qg5q9 // nostr:nevent1qqsq3wc73lqxd70lg43m5rul57d4mhcanttjat56e30yx5zla48qzlspz9mhxue69uhkummnw3e82efwvdhk6qgdwaehxw309ahx7uewd3hkcq5hsum @@ -68,14 +68,21 @@ export function embedNostrHashtags(content: EmbedableContent, event: NostrEvent return embedJSX(content, { name: "nostr-hashtag", - regexp: /#(\w+)/i, + regexp: matchHashtag, + getLocation: (match) => { + if (match.index === undefined) throw new Error("match dose not have index"); + + const start = match.index + match[1].length; + const end = start + 1 + match[2].length; + return { start, end }; + }, render: (match) => { - const hashtag = match[1].toLowerCase(); + const hashtag = match[2].toLowerCase(); if (hashtags.includes(hashtag)) { return ( - #{match[1]} + #{match[2]} ); } diff --git a/src/components/post-modal/index.tsx b/src/components/post-modal/index.tsx index 353254835..4fe2f0ab2 100644 --- a/src/components/post-modal/index.tsx +++ b/src/components/post-modal/index.tsx @@ -18,7 +18,7 @@ import { useList } from "react-use"; import { nostrPostAction, PostResult } from "../../classes/nostr-post-action"; import { normalizeToHex } from "../../helpers/nip19"; import { getReferences } from "../../helpers/nostr-event"; -import { mentionNpubOrNote } from "../../helpers/regexp"; +import { matchHashtag, mentionNpubOrNote } from "../../helpers/regexp"; import { useWriteRelayUrls } from "../../hooks/use-client-relays"; import { useIsMobile } from "../../hooks/use-is-mobile"; import { useSigningContext } from "../../providers/signing-provider"; @@ -60,6 +60,15 @@ function finalizeNote(draft: DraftNostrEvent) { updatedDraft.content = c.slice(0, match.index) + `#[${index}]` + c.slice(match.index + match[0].length); } + // replace all uses of #hashtag + const matches = updatedDraft.content.matchAll(new RegExp(matchHashtag, "gi")); + 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; } diff --git a/src/helpers/embeds.ts b/src/helpers/embeds.ts index 69436c3cb..01e8ef41d 100644 --- a/src/helpers/embeds.ts +++ b/src/helpers/embeds.ts @@ -5,8 +5,17 @@ export type EmbedType = { regexp: RegExp; render: (match: RegExpMatchArray) => JSX.Element | string | null; name: string; + getLocation?: (match: RegExpMatchArray) => { start: number; end: number }; }; +function defaultGetLocation(match: RegExpMatchArray) { + if (match.index === undefined) throw new Error("match dose not have index"); + return { + start: match.index, + end: match.index + match[0].length, + }; +} + export function embedJSX(content: EmbedableContent, embed: EmbedType): EmbedableContent { return content .map((subContent, i) => { @@ -14,8 +23,9 @@ export function embedJSX(content: EmbedableContent, embed: EmbedType): Embedable const match = subContent.match(embed.regexp); if (match && match.index !== undefined) { - const before = subContent.slice(0, match.index); - const after = subContent.slice(match.index + match[0].length, subContent.length); + const { start, end } = (embed.getLocation || defaultGetLocation)(match); + const before = subContent.slice(0, start); + const after = subContent.slice(end, subContent.length); let embedRender = embed.render(match); if (embedRender === null) return subContent; diff --git a/src/helpers/regexp.ts b/src/helpers/regexp.ts index 1b2dd98a5..b8bdd98a4 100644 --- a/src/helpers/regexp.ts +++ b/src/helpers/regexp.ts @@ -3,3 +3,4 @@ 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; +export const matchHashtag = /(^|\s)#([^\s#]+)/i;