diff --git a/package-lock.json b/package-lock.json index 91b3701..cd20a34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@vueuse/head": "^2.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", "fuse.js": "^7.0.0", "lucide-vue-next": "^0.474.0", "nostr-tools": "^2.10.4", @@ -4338,6 +4339,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", diff --git a/package.json b/package.json index 04068ae..fe12e39 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@vueuse/head": "^2.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", "fuse.js": "^7.0.0", "lucide-vue-next": "^0.474.0", "nostr-tools": "^2.10.4", diff --git a/src/components/nostr/NostrFeed.vue b/src/components/nostr/NostrFeed.vue new file mode 100644 index 0000000..9c36a54 --- /dev/null +++ b/src/components/nostr/NostrFeed.vue @@ -0,0 +1,96 @@ + + + + + + + Nostr Feed + Latest notes from the nostr network + + + + + + + + + {{ error.message }} + + + + No notes found + + + + + + + {{ note.pubkey.slice(0, 8) }}... + {{ formatDate(note.created_at) }} + + {{ note.content }} + + {{ note.replyCount }} replies + {{ note.reactionCount }} reactions + + + + + + + + + Refresh + + + + + \ No newline at end of file diff --git a/src/lib/nostr/client.ts b/src/lib/nostr/client.ts index dca0462..7f30922 100644 --- a/src/lib/nostr/client.ts +++ b/src/lib/nostr/client.ts @@ -1,9 +1,15 @@ -import { SimplePool, getPublicKey, nip19 } from 'nostr-tools' +import { SimplePool, type Filter, type Event } from 'nostr-tools' export interface NostrClientConfig { relays: string[] } +export interface NostrNote extends Event { + // Add any additional note-specific fields we want to track + replyCount?: number + reactionCount?: number +} + export class NostrClient { private pool: SimplePool private relays: string[] @@ -41,4 +47,80 @@ export class NostrClient { this.pool.close(this.relays) this._isConnected = false } + + async fetchNotes(options: { + limit?: number + since?: number // Unix timestamp in seconds + } = {}): Promise { + const { limit = 20, since = Math.floor((Date.now() - 24 * 60 * 60 * 1000) / 1000) } = options + + const filter: Filter = { + kinds: [1], // Regular notes + since, + limit + } + + try { + // Get events from all relays + const events = await Promise.all( + this.relays.map(async (relay) => { + try { + return await this.pool.querySync([relay], filter) + } catch (error) { + console.warn(`Failed to fetch from relay ${relay}:`, error) + return [] + } + }) + ) + + // Flatten and deduplicate events by ID + const uniqueEvents = Array.from( + new Map( + events.flat().map(event => [event.id, event]) + ).values() + ) + + return uniqueEvents + .sort((a: Event, b: Event) => b.created_at - a.created_at) // Sort by newest first + .map((event: Event): NostrNote => ({ + ...event, + replyCount: 0, // We'll implement this later + reactionCount: 0 // We'll implement this later + })) + } catch (error) { + console.error('Failed to fetch notes:', error) + throw error + } + } + + // Subscribe to new notes in real-time + subscribeToNotes(onNote: (note: NostrNote) => void): () => void { + const filters = [{ + kinds: [1], + since: Math.floor(Date.now() / 1000) + }] + + // Subscribe to each relay individually + const unsubscribes = this.relays.map(relay => { + const sub = this.pool.subscribeMany( + [relay], + filters, + { + onevent: (event: Event) => { + onNote({ + ...event, + replyCount: 0, + reactionCount: 0 + }) + } + } + ) + return () => sub.close() + }) + + // Return a function that unsubscribes from all relays + return () => { + unsubscribes.forEach(unsub => unsub()) + } + } } \ No newline at end of file diff --git a/src/pages/Home.vue b/src/pages/Home.vue index 32efb40..d34c9e0 100644 --- a/src/pages/Home.vue +++ b/src/pages/Home.vue @@ -1,10 +1,12 @@ - + + +
{{ note.content }}