diff --git a/dist/index.html b/dist/index.html
index dc5b4fa9..6849bf82 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -5,7 +5,7 @@
Boris - Nostr Bookmarks
-
+
diff --git a/src/components/Bookmarks.tsx b/src/components/Bookmarks.tsx
index b0d4666f..e2ac0845 100644
--- a/src/components/Bookmarks.tsx
+++ b/src/components/Bookmarks.tsx
@@ -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, fetchHighlightsForUrl } from '../services/highlightService'
+import { fetchHighlights, fetchHighlightsForArticle } from '../services/highlightService'
import { fetchContacts } from '../services/contactService'
import ContentPanel from './ContentPanel'
import { HighlightsPanel } from './HighlightsPanel'
@@ -20,7 +20,7 @@ import { useExternalUrlLoader } from '../hooks/useExternalUrlLoader'
import { loadContent, BookmarkReference } from '../utils/contentLoader'
import { HighlightVisibility } from './HighlightsPanel'
import { HighlightButton, HighlightButtonRef } from './HighlightButton'
-import { createHighlight } from '../services/highlightCreationService'
+import { createHighlight, eventToHighlight } from '../services/highlightCreationService'
import { useRef, useCallback } from 'react'
import { NostrEvent } from 'nostr-tools'
export type ViewMode = 'compact' | 'cards' | 'large'
@@ -223,30 +223,6 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => {
}
}
- const handleHighlightCreated = async () => {
- // Refresh highlights after creating a new one
- if (!relayPool) return
-
- try {
- // 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)
- }
- }
-
const handleTextSelection = useCallback((text: string) => {
highlightButtonRef.current?.updateSelection(text)
}, [])
@@ -276,7 +252,8 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => {
? currentArticle.content
: readerContent?.markdown || readerContent?.html
- await createHighlight(
+ // Create and publish the highlight
+ const signedEvent = await createHighlight(
text,
source,
activeAccount,
@@ -287,12 +264,13 @@ const Bookmarks: React.FC = ({ relayPool, onLogout }) => {
console.log('✅ Highlight created successfully!')
highlightButtonRef.current?.clearSelection()
- // Trigger refresh of highlights
- handleHighlightCreated()
+ // Immediately add the highlight to the UI (optimistic update)
+ const newHighlight = eventToHighlight(signedEvent)
+ setHighlights(prev => [newHighlight, ...prev])
} catch (error) {
console.error('Failed to create highlight:', error)
}
- }, [activeAccount, relayPool, currentArticle, selectedUrl, readerContent, handleHighlightCreated])
+ }, [activeAccount, relayPool, currentArticle, selectedUrl, readerContent])
return (
<>
diff --git a/src/services/highlightCreationService.ts b/src/services/highlightCreationService.ts
index 2afd0a5a..279c974a 100644
--- a/src/services/highlightCreationService.ts
+++ b/src/services/highlightCreationService.ts
@@ -5,10 +5,21 @@ import { IAccount } from 'applesauce-accounts'
import { AddressPointer } from 'nostr-tools/nip19'
import { NostrEvent } from 'nostr-tools'
import { RELAYS } from '../config/relays'
+import { Highlight } from '../types/highlights'
+import {
+ getHighlightText,
+ getHighlightContext,
+ getHighlightComment,
+ getHighlightSourceEventPointer,
+ getHighlightSourceAddressPointer,
+ getHighlightSourceUrl,
+ getHighlightAttributions
+} from 'applesauce-core/helpers'
/**
* Creates and publishes a highlight event (NIP-84)
* Supports both nostr-native articles and external URLs
+ * Returns the signed event for immediate UI updates
*/
export async function createHighlight(
selectedText: string,
@@ -17,7 +28,7 @@ export async function createHighlight(
relayPool: RelayPool,
contentForContext?: string,
comment?: string
-): Promise {
+): Promise {
if (!selectedText || !source) {
throw new Error('Missing required data to create highlight')
}
@@ -65,6 +76,9 @@ export async function createHighlight(
await relayPool.publish(RELAYS, signedEvent)
console.log('✅ Highlight published to', RELAYS.length, 'relays (including local):', signedEvent)
+
+ // Return the signed event for immediate UI updates
+ return signedEvent
}
/**
@@ -159,3 +173,33 @@ function extractContext(selectedText: string, articleContent: string): string |
return contextParts.length > 1 ? contextParts.join(' ') : undefined
}
+/**
+ * Converts a NostrEvent to a Highlight object for immediate UI display
+ */
+export function eventToHighlight(event: NostrEvent): Highlight {
+ const highlightText = getHighlightText(event)
+ const context = getHighlightContext(event)
+ const comment = getHighlightComment(event)
+ const sourceEventPointer = getHighlightSourceEventPointer(event)
+ const sourceAddressPointer = getHighlightSourceAddressPointer(event)
+ const sourceUrl = getHighlightSourceUrl(event)
+ const attributions = getHighlightAttributions(event)
+
+ const author = attributions.find(a => a.role === 'author')?.pubkey
+ const eventReference = sourceEventPointer?.id ||
+ (sourceAddressPointer ? `${sourceAddressPointer.kind}:${sourceAddressPointer.pubkey}:${sourceAddressPointer.identifier}` : undefined)
+
+ return {
+ id: event.id,
+ pubkey: event.pubkey,
+ created_at: event.created_at,
+ content: highlightText,
+ tags: event.tags,
+ eventReference,
+ urlReference: sourceUrl,
+ author,
+ context,
+ comment
+ }
+}
+