{
+ if (!content) return "";
- // Handle quote format and convert to small gray bars like Jumble
- const patterns = [
- /> QUOTED: ([^•]*?) • LINK:\s*\n((?:nostr:)?nevent[^\s]*)/g,
- /> QUOTED: ([^\n]*?)\n> LINK: ((?:nostr:)?nevent[^\s]*)/g,
- /> QUOTED: ([^•]*?) • LINK:\s*((?:nostr:)?nevent[^\s]*)/g,
- /> QUOTED: ([^•]*?) • LINK: ((?:nostr:)?nevent[^\s]*)/g, // Without optional whitespace
- ];
+ let parsedContent = await parseBasicmarkup(content);
- for (const pattern of patterns) {
- const beforeReplace = rendered;
- rendered = rendered.replace(pattern, (match, quotedText, neventUrl) => {
- const encodedUrl = neventUrl.replace(/'/g, ''');
- const cleanQuotedText = quotedText.trim();
- return `${cleanQuotedText}
`;
- });
- if (beforeReplace !== rendered) {
- break;
+ return parsedContent;
+ }
+
+ function renderQuotedContent(message: NDKEvent): string {
+ const qTags = message.getMatchingTags("q");
+ if (qTags.length === 0) return "";
+
+ const qTag = qTags[0];
+ const nevent = qTag[1];
+
+ // Extract event ID from nevent
+ let eventId = '';
+ try {
+ const decoded = nip19.decode(nevent);
+ if (decoded.type === 'nevent' && decoded.data.id) {
+ eventId = decoded.data.id;
+ }
+ } catch (error) {
+ // If decode fails, try to extract hex ID directly
+ const hexMatch = nevent.match(/[a-f0-9]{64}/i);
+ if (hexMatch) {
+ eventId = hexMatch[0];
+ }
+ }
+
+ if (eventId) {
+ // Find the quoted message in our public messages
+ const quotedMessage = publicMessages.find(msg => msg.id === eventId);
+ if (quotedMessage) {
+ const quotedContent = quotedMessage.content ? quotedMessage.content.slice(0, 200) : "No content";
+ return `${quotedContent}
`;
}
}
- return rendered;
+ return "";
}
function getNotificationType(event: NDKEvent): string {
@@ -226,35 +242,59 @@
goto(`/events?id=${nevent}`);
}
- function jumpToMessageInFeed(nevent: string) {
+ function jumpToMessageInFeed(eventIdOrNevent: string) {
// Switch to public messages tab and scroll to the specific message
notificationMode = "public-messages";
// Try to find and scroll to the specific message
setTimeout(() => {
- try {
- // Decode the nevent to get the event ID
- const decoded = nip19.decode(nevent);
- if (decoded.type === 'nevent' && decoded.data.id) {
- const eventId = decoded.data.id;
+ let eventId = eventIdOrNevent;
+
+ // If it's a nevent URL, try to extract the event ID
+ if (eventIdOrNevent.startsWith('nostr:nevent') || eventIdOrNevent.startsWith('nevent')) {
+ try {
+ const decoded = nip19.decode(eventIdOrNevent);
+ if (decoded.type === 'nevent' && decoded.data.id) {
+ eventId = decoded.data.id;
+ }
+ } catch (error) {
+ // If decode fails, try to extract hex ID directly
+ const hexMatch = eventIdOrNevent.match(/[a-f0-9]{64}/i);
+ if (hexMatch) {
+ eventId = hexMatch[0];
+ } else {
+ console.warn('Failed to extract event ID from nevent:', eventIdOrNevent);
+ return;
+ }
+ }
+ }
+
+ // Find the message in our public messages
+ const targetMessage = publicMessages.find(msg => msg.id === eventId);
+ if (targetMessage) {
+ // Try to find the element in the DOM
+ const element = document.querySelector(`[data-event-id="${eventId}"]`);
+ if (element) {
+ // Check if element is in viewport
+ const rect = element.getBoundingClientRect();
+ const isInView = (
+ rect.top >= 0 &&
+ rect.left >= 0 &&
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
+ );
- // Find the message in our public messages
- const targetMessage = publicMessages.find(msg => msg.id === eventId);
- if (targetMessage) {
- // Try to scroll to the element if it exists in the DOM
- const element = document.querySelector(`[data-event-id="${eventId}"]`);
- if (element) {
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
- // Briefly highlight the message
- element.classList.add('ring-2', 'ring-blue-500');
- setTimeout(() => {
- element.classList.remove('ring-2', 'ring-blue-500');
- }, 2000);
- }
+ // Only scroll if not in view
+ if (!isInView) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
+
+ // ALWAYS highlight the message in blue
+ element.classList.add('ring-2', 'ring-blue-500');
+ setTimeout(() => {
+ element.classList.remove('ring-2', 'ring-blue-500');
+ }, 2000);
}
- } catch (error) {
- console.warn('Failed to jump to message:', error);
}
}, 100);
}
@@ -1020,9 +1060,18 @@
{/if}
+ {#if message.getMatchingTags("q").length > 0}
+
+ {@html renderQuotedContent(message)}
+
+ {/if}
{#if message.content}
- {@html truncateRenderedContent(renderContentWithLinks(message.content), 300)}
+ {#await parseContent(message.content) then parsedContent}
+ {@html parsedContent}
+ {:catch}
+ {@html message.content}
+ {/await}
{/if}
@@ -1136,7 +1185,11 @@
Replying to:
- {@html renderContentWithLinks(quotedContent)}
+ {#await parseContent(quotedContent) then parsedContent}
+ {@html parsedContent}
+ {:catch}
+ {@html quotedContent}
+ {/await}
{/if}
diff --git a/src/lib/utils/kind24_utils.ts b/src/lib/utils/kind24_utils.ts
index 9d1271e..cb43e57 100644
--- a/src/lib/utils/kind24_utils.ts
+++ b/src/lib/utils/kind24_utils.ts
@@ -151,18 +151,8 @@ export async function createKind24Reply(
return { success: false, error: "No relays available for publishing" };
}
- // Build content with quoted message if replying
- let finalContent = content;
- if (originalEvent) {
- // Use multiple relays for better discoverability
- const nevent = nip19.neventEncode({
- id: originalEvent.id,
- relays: prioritizedRelays.slice(0, 3) // Use first 3 relays
- });
- const quotedContent = originalEvent.content ? originalEvent.content.slice(0, 200) : "No content";
- // Use a more visible quote format with a clickable link
- finalContent = `> QUOTED: ${quotedContent}\n> LINK: ${nevent}\n\n${content}`;
- }
+ // Use the content as-is, quoted content is handled via q tag
+ const finalContent = content;
// Build tags for the kind 24 event
const tags: string[][] = [