From ee788cffb08675d6f1d779f7fd0f2a3d2d665df3 Mon Sep 17 00:00:00 2001 From: Gigi Date: Sun, 5 Oct 2025 09:17:07 +0100 Subject: [PATCH] feat: add caching for nostr-native articles - Add localStorage caching for kind:30023 articles (same as web articles) - Cache TTL: 7 days - Cache key prefix: article_cache_ - Add bypassCache parameter to fetchArticleByNaddr() - Log cache hits and misses for debugging - Gracefully handle storage errors Articles are now cached locally after first fetch, making subsequent loads instant and reducing relay queries. --- .cursor/rules/highlights-nip-and-docs.mdc | 8 ++- dist/index.html | 2 +- src/services/articleService.ts | 67 ++++++++++++++++++++++- 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/.cursor/rules/highlights-nip-and-docs.mdc b/.cursor/rules/highlights-nip-and-docs.mdc index 3dca9096..5a6580ea 100644 --- a/.cursor/rules/highlights-nip-and-docs.mdc +++ b/.cursor/rules/highlights-nip-and-docs.mdc @@ -1,3 +1,9 @@ --- -alwaysApply: true +description: nostr highlights spec and docs +alwaysApply: false --- + +Here's the spec for nostr-native highlights: + +- https://github.com/nostr-protocol/nips/blob/master/84.md +- https://nostrbook.dev/kinds/9802 \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 8f71a20a..6af47799 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,7 +5,7 @@ Boris - Nostr Bookmarks - + diff --git a/src/services/articleService.ts b/src/services/articleService.ts index b6e71997..7e8b95ee 100644 --- a/src/services/articleService.ts +++ b/src/services/articleService.ts @@ -20,14 +20,72 @@ export interface ArticleContent { event: NostrEvent } +interface CachedArticle { + content: ArticleContent + timestamp: number +} + +const CACHE_TTL = 7 * 24 * 60 * 60 * 1000 // 7 days in milliseconds +const CACHE_PREFIX = 'article_cache_' + +function getCacheKey(naddr: string): string { + return `${CACHE_PREFIX}${naddr}` +} + +function getFromCache(naddr: string): ArticleContent | null { + try { + const cacheKey = getCacheKey(naddr) + const cached = localStorage.getItem(cacheKey) + if (!cached) return null + + const { content, timestamp }: CachedArticle = JSON.parse(cached) + const age = Date.now() - timestamp + + if (age > CACHE_TTL) { + localStorage.removeItem(cacheKey) + return null + } + + console.log('📦 Loaded article from cache:', naddr) + return content + } catch { + return null + } +} + +function saveToCache(naddr: string, content: ArticleContent): void { + try { + const cacheKey = getCacheKey(naddr) + const cached: CachedArticle = { + content, + timestamp: Date.now() + } + localStorage.setItem(cacheKey, JSON.stringify(cached)) + console.log('💾 Saved article to cache:', naddr) + } catch (err) { + console.warn('Failed to cache article:', err) + // Silently fail if storage is full or unavailable + } +} + /** * Fetches a Nostr long-form article (NIP-23) by naddr + * @param relayPool - The relay pool to query + * @param naddr - The article's naddr + * @param bypassCache - If true, skip cache and fetch fresh from relays */ export async function fetchArticleByNaddr( relayPool: RelayPool, - naddr: string + naddr: string, + bypassCache = false ): Promise { try { + // Check cache first unless bypassed + if (!bypassCache) { + const cached = getFromCache(naddr) + if (cached) return cached + } + // Decode the naddr const decoded = nip19.decode(naddr) @@ -74,7 +132,7 @@ export async function fetchArticleByNaddr( const published = getArticlePublished(article) const summary = getArticleSummary(article) - return { + const content: ArticleContent = { title, markdown: article.content, image, @@ -83,6 +141,11 @@ export async function fetchArticleByNaddr( author: article.pubkey, event: article } + + // Save to cache before returning + saveToCache(naddr, content) + + return content } catch (err) { console.error('Failed to fetch article:', err) throw err