mirror of
https://github.com/dergigi/boris.git
synced 2026-01-04 15:34:21 +01:00
feat: enable highlight creation from external URLs
- Update createHighlight service to accept both NostrEvent and URL string as source - Modify Bookmarks component to support highlighting on /r/* paths - Add fetchHighlightsForUrl import for refreshing URL-based highlights - Extract context from reader content (markdown/html) for external URLs - Automatically use 'r' tag for external URLs via HighlightBlueprint
This commit is contained in:
@@ -7,7 +7,7 @@ import { Bookmark } from '../types/bookmarks'
|
||||
import { Highlight } from '../types/highlights'
|
||||
import { BookmarkList } from './BookmarkList'
|
||||
import { fetchBookmarks } from '../services/bookmarkService'
|
||||
import { fetchHighlights, fetchHighlightsForArticle } from '../services/highlightService'
|
||||
import { fetchHighlights, fetchHighlightsForArticle, fetchHighlightsForUrl } from '../services/highlightService'
|
||||
import { fetchContacts } from '../services/contactService'
|
||||
import ContentPanel from './ContentPanel'
|
||||
import { HighlightsPanel } from './HighlightsPanel'
|
||||
@@ -225,15 +225,23 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
|
||||
const handleHighlightCreated = async () => {
|
||||
// Refresh highlights after creating a new one
|
||||
if (!relayPool || !currentArticleCoordinate) return
|
||||
if (!relayPool) return
|
||||
|
||||
try {
|
||||
const newHighlights = await fetchHighlightsForArticle(
|
||||
relayPool,
|
||||
currentArticleCoordinate,
|
||||
currentArticleEventId
|
||||
)
|
||||
setHighlights(newHighlights)
|
||||
// Refresh based on what we're currently viewing
|
||||
if (currentArticleCoordinate) {
|
||||
// Viewing a nostr article - fetch by article coordinate
|
||||
const newHighlights = await fetchHighlightsForArticle(
|
||||
relayPool,
|
||||
currentArticleCoordinate,
|
||||
currentArticleEventId
|
||||
)
|
||||
setHighlights(newHighlights)
|
||||
} else if (selectedUrl && !selectedUrl.startsWith('nostr:')) {
|
||||
// Viewing an external URL - fetch by URL
|
||||
const newHighlights = await fetchHighlightsForUrl(relayPool, selectedUrl)
|
||||
setHighlights(newHighlights)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to refresh highlights:', err)
|
||||
}
|
||||
@@ -248,17 +256,32 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
}, [])
|
||||
|
||||
const handleCreateHighlight = useCallback(async (text: string) => {
|
||||
if (!activeAccount || !relayPool || !currentArticle) {
|
||||
if (!activeAccount || !relayPool) {
|
||||
console.error('Missing requirements for highlight creation')
|
||||
return
|
||||
}
|
||||
|
||||
// Need either a nostr article or an external URL
|
||||
if (!currentArticle && !selectedUrl) {
|
||||
console.error('No source available for highlight creation')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Determine the source: prefer currentArticle (for nostr content), fallback to selectedUrl (for external URLs)
|
||||
const source = currentArticle || selectedUrl!
|
||||
|
||||
// For context extraction, use article content or reader content
|
||||
const contentForContext = currentArticle
|
||||
? currentArticle.content
|
||||
: readerContent?.markdown || readerContent?.html
|
||||
|
||||
await createHighlight(
|
||||
text,
|
||||
currentArticle,
|
||||
source,
|
||||
activeAccount,
|
||||
relayPool
|
||||
relayPool,
|
||||
contentForContext
|
||||
)
|
||||
|
||||
console.log('✅ Highlight created successfully!')
|
||||
@@ -269,7 +292,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
||||
} catch (error) {
|
||||
console.error('Failed to create highlight:', error)
|
||||
}
|
||||
}, [activeAccount, relayPool, currentArticle, handleHighlightCreated])
|
||||
}, [activeAccount, relayPool, currentArticle, selectedUrl, readerContent, handleHighlightCreated])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -8,32 +8,45 @@ import { RELAYS } from '../config/relays'
|
||||
|
||||
/**
|
||||
* Creates and publishes a highlight event (NIP-84)
|
||||
* Supports both nostr-native articles and external URLs
|
||||
*/
|
||||
export async function createHighlight(
|
||||
selectedText: string,
|
||||
article: NostrEvent | null,
|
||||
source: NostrEvent | string,
|
||||
account: IAccount,
|
||||
relayPool: RelayPool,
|
||||
contentForContext?: string,
|
||||
comment?: string
|
||||
): Promise<void> {
|
||||
if (!selectedText || !article) {
|
||||
if (!selectedText || !source) {
|
||||
throw new Error('Missing required data to create highlight')
|
||||
}
|
||||
|
||||
// Create EventFactory with the account as signer
|
||||
const factory = new EventFactory({ signer: account })
|
||||
|
||||
// Parse article coordinate to get address pointer
|
||||
const addressPointer = parseArticleCoordinate(article)
|
||||
let blueprintSource: NostrEvent | AddressPointer | string
|
||||
let context: string | undefined
|
||||
|
||||
// Extract context (previous and next sentences from the same paragraph)
|
||||
const context = extractContext(selectedText, article.content)
|
||||
// Handle NostrEvent (article) source
|
||||
if (typeof source === 'object' && 'kind' in source) {
|
||||
blueprintSource = parseArticleCoordinate(source)
|
||||
context = extractContext(selectedText, source.content)
|
||||
}
|
||||
// Handle URL string source
|
||||
else {
|
||||
blueprintSource = source
|
||||
// Try to extract context from provided content if available
|
||||
if (contentForContext) {
|
||||
context = extractContext(selectedText, contentForContext)
|
||||
}
|
||||
}
|
||||
|
||||
// Create highlight event using the blueprint
|
||||
const highlightEvent = await factory.create(
|
||||
HighlightBlueprint,
|
||||
selectedText,
|
||||
addressPointer,
|
||||
blueprintSource,
|
||||
context ? { comment, context } : comment ? { comment } : undefined
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user