diff --git a/hooks/useArticles.ts b/hooks/useArticles.ts index 713f2ca..183bca9 100644 --- a/hooks/useArticles.ts +++ b/hooks/useArticles.ts @@ -4,6 +4,8 @@ import type { Article } from '@/types/nostr' import { applyFiltersAndSort } from '@/lib/articleFiltering' import type { ArticleFilters } from '@/components/ArticleFilters' import { t } from '@/lib/i18n' +import { objectCache } from '@/lib/objectCache' +import { getAuthorSponsoring } from '@/lib/sponsoring' export function useArticles(searchQuery: string = '', filters: ArticleFilters | null = null): { articles: Article[] @@ -21,6 +23,39 @@ export function useArticles(searchQuery: string = '', filters: ArticleFilters | setLoading(true) setError(null) + // Load authors from cache first + const loadAuthorsFromCache = async (): Promise => { + try { + const cachedAuthors = await objectCache.getAll('author') + const authors = cachedAuthors as Article[] + + // Calculate totalSponsoring for each author + const authorsWithSponsoring = await Promise.all( + authors.map(async (author) => { + if (author.isPresentation && author.pubkey) { + author.totalSponsoring = await getAuthorSponsoring(author.pubkey) + } + return author + }) + ) + + if (authorsWithSponsoring.length > 0) { + setArticles((prev) => { + // Merge with existing articles, avoiding duplicates + const existingIds = new Set(prev.map((a) => a.id)) + const newAuthors = authorsWithSponsoring.filter((a) => !existingIds.has(a.id)) + const merged = [...prev, ...newAuthors].sort((a, b) => b.createdAt - a.createdAt) + hasArticlesRef.current = merged.length > 0 + return merged + }) + } + } catch (error) { + console.error('Error loading authors from cache:', error) + } + } + + void loadAuthorsFromCache() + const unsubscribe = nostrService.subscribeToArticles( (article) => { setArticles((prev) => { diff --git a/lib/articlePublisherHelpersPresentation.ts b/lib/articlePublisherHelpersPresentation.ts index a4b81c0..5137f88 100644 --- a/lib/articlePublisherHelpersPresentation.ts +++ b/lib/articlePublisherHelpersPresentation.ts @@ -17,7 +17,12 @@ export async function buildPresentationEvent( category: 'sciencefiction' | 'research' = 'sciencefiction', version: number = 0, index: number = 0 -) { +): Promise<{ + kind: 1 + created_at: number + tags: string[][] + content: string +}> { // Extract presentation and contentDescription from draft.content // Format: "${presentation}\n\n---\n\nDescription du contenu :\n${contentDescription}" const separator = '\n\n---\n\nDescription du contenu :\n' @@ -243,7 +248,7 @@ export async function fetchAuthorPresentationFromPool( }, ] - return new Promise((resolve) => { + return new Promise((resolve) => { let resolved = false const relayUrl = getPrimaryRelaySync() const { createSubscription } = require('@/types/nostr-tools-extended') @@ -251,7 +256,7 @@ export async function fetchAuthorPresentationFromPool( const events: Event[] = [] - const finalize = async (value: import('@/types/nostr').AuthorPresentationArticle | null) => { + const finalize = async (value: import('@/types/nostr').AuthorPresentationArticle | null): Promise => { if (resolved) { return } @@ -275,7 +280,7 @@ export async function fetchAuthorPresentationFromPool( resolve(value) } - sub.on('event', (event: Event) => { + sub.on('event', (event: Event): void => { // Collect all events first const tags = extractTagsFromEvent(event) if (tags.type === 'author' && !tags.hidden) { @@ -283,7 +288,7 @@ export async function fetchAuthorPresentationFromPool( } }) - sub.on('eose', async () => { + sub.on('eose', async (): Promise => { // Get the latest version from all collected events const latestEvent = getLatestVersion(events) if (latestEvent) { @@ -295,7 +300,7 @@ export async function fetchAuthorPresentationFromPool( } await finalize(null) }) - setTimeout(async () => { + setTimeout(async (): Promise => { // Get the latest version from all collected events const latestEvent = getLatestVersion(events) if (latestEvent) { diff --git a/lib/articlePublisherHelpersVerification.ts b/lib/articlePublisherHelpersVerification.ts index 3811156..e5d00ce 100644 --- a/lib/articlePublisherHelpersVerification.ts +++ b/lib/articlePublisherHelpersVerification.ts @@ -3,7 +3,14 @@ import { getPrimaryRelaySync } from './config' import type { Event } from 'nostr-tools' import { createSubscription } from '@/types/nostr-tools-extended' -export function createMessageVerificationFilters(messageEventId: string, authorPubkey: string, recipientPubkey: string, articleId: string) { +export function createMessageVerificationFilters(messageEventId: string, authorPubkey: string, recipientPubkey: string, articleId: string): Array<{ + kinds: number[] + ids: string[] + authors: string[] + '#p': string[] + '#e': string[] + limit: number +}> { return [ { kinds: [4], @@ -42,11 +49,11 @@ export function setupMessageVerificationHandlers( finalize: (value: boolean) => void, isResolved: () => boolean ): void { - sub.on('event', (event: Event) => { + sub.on('event', (event: Event): void => { handleMessageVerificationEvent(event, articleId, recipientPubkey, authorPubkey, finalize) }) - sub.on('eose', () => { + sub.on('eose', (): void => { console.warn('Private message not found on relay after EOSE', { messageEventId, articleId, @@ -75,7 +82,7 @@ function createMessageVerificationSubscription( authorPubkey: string, recipientPubkey: string, articleId: string -) { +): ReturnType { const filters = createMessageVerificationFilters(messageEventId, authorPubkey, recipientPubkey, articleId) const relayUrl = getPrimaryRelaySync() return createSubscription(pool, [relayUrl], filters) @@ -88,10 +95,10 @@ function createVerificationPromise( recipientPubkey: string, authorPubkey: string ): Promise { - return new Promise((resolve) => { + return new Promise((resolve) => { let resolved = false - const finalize = (value: boolean) => { + const finalize = (value: boolean): void => { if (resolved) { return } diff --git a/lib/objectCache.ts b/lib/objectCache.ts index 1ef98d7..18456ff 100644 --- a/lib/objectCache.ts +++ b/lib/objectCache.ts @@ -275,6 +275,42 @@ class ObjectCacheService { } } + /** + * Get all objects of a type from cache (non-hidden only) + */ + async getAll(objectType: ObjectType): Promise { + try { + const db = await this.initDB(objectType) + const transaction = db.transaction(['objects'], 'readonly') + const store = transaction.objectStore('objects') + + return new Promise((resolve, reject) => { + const request = store.openCursor() + const objects: unknown[] = [] + + request.onsuccess = (event: Event): void => { + const cursor = (event.target as IDBRequest).result + if (cursor) { + const obj = cursor.value as CachedObject + if (!obj.hidden) { + objects.push(obj.parsed) + } + cursor.continue() + } else { + resolve(objects) + } + } + + request.onerror = (): void => { + reject(request.error) + } + }) + } catch (error) { + console.error(`Error retrieving all ${objectType} objects from cache:`, error) + return [] + } + } + /** * Clear cache for an object type */