mirror of
https://github.com/block/buzz.git
synced 2026-11-05 17:01:35 +00:00
fix(sdk): resolve multi-word display names and add NIP-27 nostr:npub mention extraction (#905)
Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -5,16 +5,25 @@ export function escapeRegExp(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
const NEVER_MATCH = /(?!)/gi;
|
||||
|
||||
/**
|
||||
* Build a regex that matches a given prefix followed by known multi-word names
|
||||
* (longest-first to avoid partial matches). When known names are provided,
|
||||
* only those names are matched — no generic fallback. When no names are
|
||||
* available, falls back to prefix + \S+ for backwards compatibility (e.g.
|
||||
* old messages without proper p-tags, or while profiles are loading).
|
||||
* only those names are matched — no generic fallback.
|
||||
*
|
||||
* When no names are available:
|
||||
* - If `options.fallbackToGeneric` is true, falls back to `prefix + \S+` so
|
||||
* that patterns like `#channel` still render while channel names are loading
|
||||
* asynchronously (used by remarkChannelLinks).
|
||||
* - Otherwise returns a never-matching regex, preventing arbitrary `@word`
|
||||
* patterns from being highlighted as valid mentions when no p-tags are
|
||||
* present (used by remarkMentions / buildMentionPattern).
|
||||
*/
|
||||
export function buildPrefixPattern(
|
||||
prefix: string,
|
||||
knownNames: string[],
|
||||
options?: { fallbackToGeneric?: boolean },
|
||||
): RegExp {
|
||||
const sorted = [...new Set(knownNames)]
|
||||
.filter((name) => name.trim().length > 0)
|
||||
@@ -23,7 +32,10 @@ export function buildPrefixPattern(
|
||||
const escapedPrefix = escapeRegExp(prefix);
|
||||
|
||||
if (sorted.length === 0) {
|
||||
return new RegExp(`${escapedPrefix}\\S+`, "gi");
|
||||
if (options?.fallbackToGeneric) {
|
||||
return new RegExp(`${escapedPrefix}\\S+`, "gi");
|
||||
}
|
||||
return NEVER_MATCH;
|
||||
}
|
||||
|
||||
const nameAlternatives = sorted.map((name) => escapeRegExp(name)).join("|");
|
||||
@@ -32,8 +44,10 @@ export function buildPrefixPattern(
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a regex that matches @mentions, trying known multi-word names first
|
||||
* (longest-first to avoid partial matches), then falling back to @\S+.
|
||||
* Build a regex that matches @mentions for known multi-word names
|
||||
* (longest-first to avoid partial matches). When no known names are provided,
|
||||
* returns a never-matching regex — @word patterns are not highlighted unless
|
||||
* they correspond to an actual p-tagged member.
|
||||
*/
|
||||
export function buildMentionPattern(mentionNames: string[]): RegExp {
|
||||
return buildPrefixPattern("@", mentionNames);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Remark plugin that detects #channel-name patterns in text nodes and wraps them
|
||||
* in custom HAST `channel-link` elements for styled rendering via react-markdown.
|
||||
*
|
||||
* When `channelNames` is provided, multi-word channel names (e.g. "my channel")
|
||||
* are matched first (longest-first to avoid partial matches), then the plugin
|
||||
* falls back to the generic `#\S+` pattern for unknown channels.
|
||||
* Known channel names are matched longest-first to avoid partial matches. When
|
||||
* no known names are provided, falls back to `#\S+` so that channel links still
|
||||
* render while the channel list is loading asynchronously.
|
||||
*/
|
||||
|
||||
import { createRemarkPrefixPlugin } from "./createRemarkPrefixPlugin";
|
||||
@@ -17,7 +17,9 @@ type RemarkChannelLinksOptions = {
|
||||
export default function remarkChannelLinks(
|
||||
options?: RemarkChannelLinksOptions,
|
||||
) {
|
||||
const channelPattern = buildPrefixPattern("#", options?.channelNames ?? []);
|
||||
const channelPattern = buildPrefixPattern("#", options?.channelNames ?? [], {
|
||||
fallbackToGeneric: true,
|
||||
});
|
||||
|
||||
return createRemarkPrefixPlugin(channelPattern, (matchText) => {
|
||||
const channelName = matchText.slice(1);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* Remark plugin that detects @mention patterns in text nodes and wraps them
|
||||
* in custom HAST `mention` elements for styled rendering via react-markdown.
|
||||
*
|
||||
* When `mentionNames` is provided, multi-word display names (e.g. "John Doe")
|
||||
* are matched first (longest-first to avoid partial matches), then the plugin
|
||||
* falls back to the generic `@\S+` pattern for unknown mentions.
|
||||
* Only p-tagged member names are highlighted — multi-word display names
|
||||
* (e.g. "John Doe") are matched longest-first to avoid partial matches.
|
||||
* When no known names are provided, nothing is highlighted.
|
||||
*/
|
||||
|
||||
import { createRemarkPrefixPlugin } from "./createRemarkPrefixPlugin";
|
||||
|
||||
Reference in New Issue
Block a user