mirror of
https://github.com/dergigi/boris.git
synced 2026-01-06 00:14:48 +01:00
feat: integrate applesauce-content for proper content parsing and rendering
- Install applesauce-content package for content parsing - Use getParsedContent() to parse nostr content according to applesauce patterns - Create proper TypeScript interfaces for ParsedNode and ParsedContent - Add renderParsedContent() component to render parsed content with proper styling - Handle mentions, links, and text content with appropriate styling - Add CSS styles for nostr-mention and nostr-link classes - Follow applesauce-content documentation patterns for content rendering - Maintain type safety with proper interfaces instead of 'any' types This follows the applesauce-content documentation exactly as shown in the examples, providing proper content parsing and rendering.
This commit is contained in:
@@ -2,8 +2,22 @@ import React, { useState, useEffect } from 'react'
|
||||
import { Hooks } from 'applesauce-react'
|
||||
import { useEventModel } from 'applesauce-react/hooks'
|
||||
import { Models } from 'applesauce-core'
|
||||
import { getParsedContent } from 'applesauce-content/text'
|
||||
import { NostrEvent } from 'nostr-tools'
|
||||
|
||||
interface ParsedNode {
|
||||
type: string
|
||||
value?: string
|
||||
url?: string
|
||||
encoded?: string
|
||||
children?: ParsedNode[]
|
||||
}
|
||||
|
||||
interface ParsedContent {
|
||||
type: string
|
||||
children: ParsedNode[]
|
||||
}
|
||||
|
||||
interface Bookmark {
|
||||
id: string
|
||||
title: string
|
||||
@@ -15,6 +29,7 @@ interface Bookmark {
|
||||
eventReferences?: string[]
|
||||
articleReferences?: string[]
|
||||
urlReferences?: string[]
|
||||
parsedContent?: ParsedContent
|
||||
}
|
||||
|
||||
interface BookmarksProps {
|
||||
@@ -100,6 +115,9 @@ const Bookmarks: React.FC<BookmarksProps> = ({ addressLoader, onLogout }) => {
|
||||
const articleTags = event.tags.filter((tag: string[]) => tag[0] === 'a')
|
||||
const urlTags = event.tags.filter((tag: string[]) => tag[0] === 'r')
|
||||
|
||||
// Use applesauce-content to parse the content properly
|
||||
const parsedContent = event.content ? getParsedContent(event.content) as ParsedContent : undefined
|
||||
|
||||
// Get the title from content or use a default
|
||||
const title = event.content || `Bookmark List (${eventTags.length + articleTags.length + urlTags.length} items)`
|
||||
|
||||
@@ -110,6 +128,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ addressLoader, onLogout }) => {
|
||||
content: event.content,
|
||||
created_at: event.created_at,
|
||||
tags: event.tags,
|
||||
parsedContent: parsedContent,
|
||||
// Add metadata about the bookmark list
|
||||
bookmarkCount: eventTags.length + articleTags.length + urlTags.length,
|
||||
eventReferences: eventTags.map(tag => tag[1]),
|
||||
@@ -126,6 +145,67 @@ const Bookmarks: React.FC<BookmarksProps> = ({ addressLoader, onLogout }) => {
|
||||
return new Date(timestamp * 1000).toLocaleDateString()
|
||||
}
|
||||
|
||||
// Component to render parsed content using applesauce-content
|
||||
const renderParsedContent = (parsedContent: ParsedContent) => {
|
||||
if (!parsedContent || !parsedContent.children) {
|
||||
return null
|
||||
}
|
||||
|
||||
const renderNode = (node: ParsedNode, index: number): React.ReactNode => {
|
||||
if (node.type === 'text') {
|
||||
return <span key={index}>{node.value}</span>
|
||||
}
|
||||
|
||||
if (node.type === 'mention') {
|
||||
return (
|
||||
<a
|
||||
key={index}
|
||||
href={`nostr:${node.encoded}`}
|
||||
className="nostr-mention"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{node.encoded}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
if (node.type === 'link') {
|
||||
return (
|
||||
<a
|
||||
key={index}
|
||||
href={node.url}
|
||||
className="nostr-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{node.url}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
return (
|
||||
<span key={index}>
|
||||
{node.children.map((child: ParsedNode, childIndex: number) =>
|
||||
renderNode(child, childIndex)
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="parsed-content">
|
||||
{parsedContent.children.map((node: ParsedNode, index: number) =>
|
||||
renderNode(node, index)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const formatUserDisplay = () => {
|
||||
if (!activeAccount) return 'Unknown User'
|
||||
|
||||
@@ -217,7 +297,11 @@ const Bookmarks: React.FC<BookmarksProps> = ({ addressLoader, onLogout }) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{bookmark.content && (
|
||||
{bookmark.parsedContent ? (
|
||||
<div className="bookmark-content">
|
||||
{renderParsedContent(bookmark.parsedContent)}
|
||||
</div>
|
||||
) : bookmark.content && (
|
||||
<p className="bookmark-content">{bookmark.content}</p>
|
||||
)}
|
||||
<div className="bookmark-meta">
|
||||
|
||||
@@ -181,6 +181,36 @@ body {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.parsed-content {
|
||||
margin: 1rem 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.nostr-mention {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nostr-mention:hover {
|
||||
background: #e9ecef;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nostr-link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.nostr-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
|
||||
Reference in New Issue
Block a user