mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-04 01:31:13 +02:00
refactor: Rename renderTextWithLinkedTags to renderTextWithLinks for clarity and combine hashtag and nostr reference handling
This commit is contained in:
@@ -13,7 +13,7 @@ import type { Event as NostrEvent } from "nostr-tools"
|
||||
import ZapButton from "./ZapButton"
|
||||
import Image from "next/image"
|
||||
import CardOptionsDropdown from "./CardOptionsDropdown"
|
||||
import { renderTextWithLinkedTags } from "@/utils/textUtils"
|
||||
import { renderTextWithLinkedTags, renderTextWithLinks } from "@/utils/textUtils"
|
||||
|
||||
// Function to extract all images from a kind 20 event's imeta tags
|
||||
const extractImagesFromEvent = (tags: string[][]): string[] => {
|
||||
@@ -169,7 +169,7 @@ const KIND20Card: React.FC<KIND20CardProps> = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="break-word overflow-hidden">{renderTextWithLinkedTags(text, tags)}</div>
|
||||
<div className="break-word overflow-hidden">{renderTextWithLinks(text, tags, { [pubkey]: userData || {} })}</div>
|
||||
<hr className="my-4" />
|
||||
<div className="space-x-4 flex justify-between items-start">
|
||||
<div className="flex space-x-4">
|
||||
|
||||
@@ -30,7 +30,7 @@ import Link from 'next/link';
|
||||
import { Event as NostrEvent } from "nostr-tools";
|
||||
import ZapButton from './ZapButton';
|
||||
import CardOptionsDropdown from './CardOptionsDropdown';
|
||||
import { renderTextWithLinkedTags } from '@/utils/textUtils';
|
||||
import { renderTextWithLinkedTags, renderTextWithLinks } from '@/utils/textUtils';
|
||||
|
||||
interface NoteCardProps {
|
||||
pubkey: string;
|
||||
@@ -143,7 +143,7 @@ const NoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tags, event,
|
||||
}
|
||||
<br />
|
||||
<div className='break-word overflow-hidden'>
|
||||
{renderTextWithLinkedTags(textWithoutImage, tags)}
|
||||
{renderTextWithLinks(textWithoutImage, tags, { [pubkey]: userData || {} })}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
|
||||
/**
|
||||
* Renders text content with hyperlinked hashtags
|
||||
@@ -54,3 +55,119 @@ export function renderTextWithLinkedTags(content: string, eventTags: string[][])
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace nostr:npub references with @username in content
|
||||
* @param content The text content that may contain nostr:npub references
|
||||
* @param eventTags The tags array from a Nostr event
|
||||
* @param userData Optional object containing profile data for referenced users
|
||||
* @returns Text with replaced nostr:npub references
|
||||
*/
|
||||
export function replaceNostrReferences(
|
||||
content: string,
|
||||
eventTags: string[][],
|
||||
userData?: Record<string, { name?: string; display_name?: string; username?: string; }>
|
||||
): ReactNode[] {
|
||||
if (!content) return [];
|
||||
|
||||
// Extract all pubkey references from the event tags (p tags contain referenced pubkeys)
|
||||
const pubkeyRefs = eventTags
|
||||
.filter((tag) => tag[0] === "p")
|
||||
.map((tag) => ({ pubkey: tag[1], relay: tag.length > 2 ? tag[2] : undefined, petname: tag.length > 3 ? tag[3] : undefined }));
|
||||
|
||||
// Find nostr:npub and nostr:nprofile references in the content
|
||||
const nostrRegex = /nostr:(npub1[a-z0-9]+|nprofile1[a-z0-9]+)/g;
|
||||
let lastIndex = 0;
|
||||
const result: ReactNode[] = [];
|
||||
let match;
|
||||
|
||||
while ((match = nostrRegex.exec(content)) !== null) {
|
||||
const fullRef = match[0]; // nostr:npub1...
|
||||
const matchIndex = match.index;
|
||||
|
||||
// Add text before the reference
|
||||
if (matchIndex > lastIndex) {
|
||||
result.push(content.substring(lastIndex, matchIndex));
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract the identifier (remove "nostr:" prefix)
|
||||
const nostrId = fullRef.substring(6);
|
||||
let pubkey: string;
|
||||
let profileRoute = nostrId;
|
||||
|
||||
// Decode npub or nprofile to get the pubkey
|
||||
if (nostrId.startsWith('npub1')) {
|
||||
const { data } = nip19.decode(nostrId);
|
||||
pubkey = data as string;
|
||||
} else if (nostrId.startsWith('nprofile1')) {
|
||||
const { data } = nip19.decode(nostrId);
|
||||
pubkey = (data as { pubkey: string }).pubkey;
|
||||
// Still use the nprofile as the route to preserve relay information
|
||||
profileRoute = nostrId;
|
||||
} else {
|
||||
throw new Error('Unsupported nostr ID type');
|
||||
}
|
||||
|
||||
// Find if we have any profile data for this pubkey
|
||||
const pubkeyData = pubkeyRefs.find(ref => ref.pubkey === pubkey);
|
||||
const displayName = pubkeyData?.petname ||
|
||||
(userData && userData[pubkey] &&
|
||||
(userData[pubkey].username ||
|
||||
userData[pubkey].display_name ||
|
||||
userData[pubkey].name)) ||
|
||||
nostrId.substring(0, 8) + '...';
|
||||
|
||||
// Create a link for the user reference
|
||||
result.push(
|
||||
<Link href={`/profile/${profileRoute}`} key={`${nostrId}-${matchIndex}`} className="text-blue-500 hover:underline">
|
||||
@{displayName}
|
||||
</Link>
|
||||
);
|
||||
} catch (error) {
|
||||
// If there's an error parsing the npub, just include it as-is
|
||||
result.push(fullRef);
|
||||
}
|
||||
|
||||
lastIndex = matchIndex + fullRef.length;
|
||||
}
|
||||
|
||||
// Add any remaining text
|
||||
if (lastIndex < content.length) {
|
||||
result.push(content.substring(lastIndex));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines hashtag and nostr reference handling in one function
|
||||
* @param content The text content to process
|
||||
* @param eventTags The tags array from a Nostr event
|
||||
* @param userData Optional object containing profile data for referenced users
|
||||
* @returns Processed content with links
|
||||
*/
|
||||
export function renderTextWithLinks(
|
||||
content: string,
|
||||
eventTags: string[][],
|
||||
userData?: Record<string, { name?: string; display_name?: string; username?: string; }>
|
||||
): ReactNode[] {
|
||||
if (!content) return [];
|
||||
|
||||
// First replace hashtags
|
||||
const withHashtags = renderTextWithLinkedTags(content, eventTags);
|
||||
|
||||
// Then handle nostr references for each text segment
|
||||
const result: ReactNode[] = [];
|
||||
|
||||
for (const item of withHashtags) {
|
||||
if (typeof item === 'string' && (item.includes('nostr:npub') || item.includes('nostr:nprofile'))) {
|
||||
const withReferences = replaceNostrReferences(item, eventTags, userData);
|
||||
result.push(...withReferences);
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user