refactor: use proper applesauce-loaders for NIP-51 bookmark fetching

- Replace manual event fetching with applesauce Address Loader
- Use Loaders.createAddressLoader() for proper replaceable event loading
- Configure loader with eventStore, bufferTime, and relay hints
- Remove manual RxJS subscription handling in favor of applesauce patterns
- Add applesauce-loaders dependency for specialized loading methods
- Clean up unused imports and variables
- Follow applesauce best practices for event loading

This implements the proper applesauce pattern for fetching kind:10003
bookmark events as recommended in the applesauce-loaders documentation.
This commit is contained in:
Gigi
2025-10-02 07:29:37 +02:00
parent b245a11ade
commit b4ce082b91
6 changed files with 75 additions and 37 deletions

2
dist/index.html vendored
View File

@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Markr - Nostr Bookmarks</title>
<script type="module" crossorigin src="/assets/index-CmBYERzD.js"></script>
<script type="module" crossorigin src="/assets/index-yXpxThY1.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D18sZheu.css">
</head>
<body>

16
node_modules/.package-lock.json generated vendored
View File

@@ -1921,6 +1921,22 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-loaders": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-3.1.0.tgz",
"integrity": "sha512-9IY5RYqoXcIgAAdJNuMjMBr+CI85z1yj708C92UiP9YMQ4mrIIEvZbMmWLApBzRn5XsmmEa00a/iNlXpkRg9Sw==",
"license": "MIT",
"dependencies": {
"applesauce-core": "^3.1.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"rxjs": "^7.8.1"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-react": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-3.1.0.tgz",

17
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"applesauce-accounts": "^3.1.0",
"applesauce-core": "^3.1.0",
"applesauce-loaders": "^3.1.0",
"applesauce-react": "^3.1.0",
"applesauce-relay": "^3.1.0",
"nostr-tools": "^2.4.0",
@@ -1903,6 +1904,22 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-loaders": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-3.1.0.tgz",
"integrity": "sha512-9IY5RYqoXcIgAAdJNuMjMBr+CI85z1yj708C92UiP9YMQ4mrIIEvZbMmWLApBzRn5XsmmEa00a/iNlXpkRg9Sw==",
"license": "MIT",
"dependencies": {
"applesauce-core": "^3.1.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"rxjs": "^7.8.1"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-react": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-3.1.0.tgz",

View File

@@ -12,6 +12,7 @@
"dependencies": {
"applesauce-accounts": "^3.1.0",
"applesauce-core": "^3.1.0",
"applesauce-loaders": "^3.1.0",
"applesauce-react": "^3.1.0",
"applesauce-relay": "^3.1.0",
"nostr-tools": "^2.4.0",

View File

@@ -3,6 +3,7 @@ import { EventStoreProvider, AccountsProvider } from 'applesauce-react'
import { EventStore } from 'applesauce-core'
import { AccountManager } from 'applesauce-accounts'
import { RelayPool } from 'applesauce-relay'
import { Loaders } from 'applesauce-loaders'
import Login from './components/Login'
import Bookmarks from './components/Bookmarks'
@@ -10,6 +11,7 @@ function App() {
const [eventStore, setEventStore] = useState<EventStore | null>(null)
const [accountManager, setAccountManager] = useState<AccountManager | null>(null)
const [relayPool, setRelayPool] = useState<RelayPool | null>(null)
const [addressLoader, setAddressLoader] = useState<any>(null)
const [isAuthenticated, setIsAuthenticated] = useState(false)
useEffect(() => {
@@ -23,12 +25,21 @@ function App() {
pool.relay('wss://nos.lol')
pool.relay('wss://relay.snort.social')
// Create address loader for fetching replaceable events (like bookmarks)
const loader = Loaders.createAddressLoader(pool, {
eventStore: store,
bufferTime: 1000,
followRelayHints: true,
extraRelays: ['wss://relay.damus.io', 'wss://nos.lol', 'wss://relay.snort.social']
})
setEventStore(store)
setAccountManager(accounts)
setRelayPool(pool)
setAddressLoader(loader)
}, [])
if (!eventStore || !accountManager || !relayPool) {
if (!eventStore || !accountManager || !relayPool || !addressLoader) {
return <div>Loading...</div>
}
@@ -44,7 +55,10 @@ function App() {
{!isAuthenticated ? (
<Login onLogin={() => setIsAuthenticated(true)} />
) : (
<Bookmarks onLogout={() => setIsAuthenticated(false)} />
<Bookmarks
addressLoader={addressLoader}
onLogout={() => setIsAuthenticated(false)}
/>
)}
</div>
</AccountsProvider>

View File

@@ -1,7 +1,6 @@
import { useState, useEffect, useContext } from 'react'
import { EventStoreContext, Hooks } from 'applesauce-react'
import { useState, useEffect } from 'react'
import { Hooks } from 'applesauce-react'
import { NostrEvent } from 'nostr-tools'
import { takeUntil, timer } from 'rxjs'
interface Bookmark {
id: string
@@ -13,65 +12,56 @@ interface Bookmark {
}
interface BookmarksProps {
addressLoader: any
onLogout: () => void
}
const Bookmarks: React.FC<BookmarksProps> = ({ onLogout }) => {
const Bookmarks: React.FC<BookmarksProps> = ({ addressLoader, onLogout }) => {
const [bookmarks, setBookmarks] = useState<Bookmark[]>([])
const [loading, setLoading] = useState(true)
const eventStore = useContext(EventStoreContext)
const activeAccount = Hooks.useActiveAccount()
useEffect(() => {
if (eventStore && activeAccount) {
if (addressLoader && activeAccount) {
fetchBookmarks()
}
}, [eventStore, activeAccount])
}, [addressLoader, activeAccount])
const fetchBookmarks = async () => {
if (!eventStore || !activeAccount) return
if (!addressLoader || !activeAccount) return
try {
setLoading(true)
// Subscribe to bookmark events from relays
// According to NIP-51, we need kind 10003 events (bookmark lists)
const events: NostrEvent[] = []
// Use applesauce address loader to fetch bookmark lists (kind 10003)
// This is the proper way according to NIP-51 and applesauce documentation
const bookmarkList: Bookmark[] = []
const subscription = eventStore.filters([
{
kinds: [10003],
authors: [activeAccount.pubkey]
}
]).pipe(
takeUntil(timer(5000)) // Wait up to 5 seconds for events
).subscribe({
next: (event) => {
events.push(event)
const subscription = addressLoader({
kind: 10003, // Bookmark list according to NIP-51
pubkey: activeAccount.pubkey,
relays: ['wss://relay.damus.io', 'wss://nos.lol', 'wss://relay.snort.social']
}).subscribe({
next: (event: NostrEvent) => {
const bookmarkData = parseBookmarkEvent(event)
if (bookmarkData) {
bookmarkList.push(bookmarkData)
}
},
error: (error) => {
error: (error: any) => {
console.error('Error fetching bookmarks:', error)
setLoading(false)
},
complete: () => {
// Process collected events
const bookmarkList: Bookmark[] = []
for (const event of events) {
const bookmarkData = parseBookmarkEvent(event)
if (bookmarkData) {
bookmarkList.push(bookmarkData)
}
}
setBookmarks(bookmarkList)
setLoading(false)
}
})
// Clean up subscription after timeout
// Set timeout to prevent hanging
setTimeout(() => {
subscription.unsubscribe()
if (events.length === 0) {
if (bookmarkList.length === 0) {
setBookmarks([])
setLoading(false)
}