diff --git a/components/UnlockAccountModal.tsx b/components/UnlockAccountModal.tsx
index 221267f..209e0a3 100644
--- a/components/UnlockAccountModal.tsx
+++ b/components/UnlockAccountModal.tsx
@@ -213,6 +213,7 @@ function UnlockAccountForm({
{
void handlePaste()
}}
diff --git a/hooks/useArticles.ts b/hooks/useArticles.ts
index e846a73..e3df55c 100644
--- a/hooks/useArticles.ts
+++ b/hooks/useArticles.ts
@@ -29,25 +29,47 @@ export function useArticles(searchQuery: string = '', filters: ArticleFilters |
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) {
+ // Display authors immediately (with existing totalSponsoring if available)
+ if (authors.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 newAuthors = authors.filter((a) => !existingIds.has(a.id))
const merged = [...prev, ...newAuthors].sort((a, b) => b.createdAt - a.createdAt)
hasArticlesRef.current = merged.length > 0
return merged
})
+ setLoading(false)
+ }
+
+ // Calculate totalSponsoring asynchronously from cache (non-blocking)
+ // Only update authors that don't have totalSponsoring yet
+ const authorsNeedingSponsoring = authors.filter(
+ (author) => author.isPresentation && author.pubkey && author.totalSponsoring === undefined
+ )
+
+ if (authorsNeedingSponsoring.length > 0) {
+ // Load sponsoring from cache in parallel (fast, no network)
+ const sponsoringPromises = authorsNeedingSponsoring.map(async (author) => {
+ if (author.pubkey) {
+ const totalSponsoring = await getAuthorSponsoring(author.pubkey, true)
+ return { authorId: author.id, totalSponsoring }
+ }
+ return null
+ })
+
+ const sponsoringResults = await Promise.all(sponsoringPromises)
+
+ // Update articles with sponsoring amounts
+ setArticles((prev) =>
+ prev.map((article) => {
+ const sponsoringResult = sponsoringResults.find((r) => r?.authorId === article.id)
+ if (sponsoringResult && article.isPresentation) {
+ return { ...article, totalSponsoring: sponsoringResult.totalSponsoring }
+ }
+ return article
+ })
+ )
}
} catch (error) {
console.error('Error loading authors from cache:', error)
diff --git a/lib/alby.ts b/lib/alby.ts
index 97e8ab7..31c6619 100644
--- a/lib/alby.ts
+++ b/lib/alby.ts
@@ -47,7 +47,7 @@ export class AlbyService {
try {
await webln.enable()
- } catch (_error) {
+ } catch {
throw new Error('User rejected WebLN permission request')
}
}
@@ -175,7 +175,7 @@ export class AlbyService {
}
setTimeout(checkPayment, interval)
- } catch (_error) {
+ } catch {
resolve({ paid: false, paymentHash })
}
}
diff --git a/lib/nip95.ts b/lib/nip95.ts
index 3e1774c..9274264 100644
--- a/lib/nip95.ts
+++ b/lib/nip95.ts
@@ -88,7 +88,7 @@ async function tryUploadEndpoint(endpoint: string, formData: FormData, useProxy:
try {
const text = await response.text()
errorMessage = text || `HTTP ${response.status} ${response.statusText}`
- } catch (_e) {
+ } catch {
errorMessage = `HTTP ${response.status} ${response.statusText}`
}
throw new Error(errorMessage)
diff --git a/lib/nostr.ts b/lib/nostr.ts
index 645bff9..17a869f 100644
--- a/lib/nostr.ts
+++ b/lib/nostr.ts
@@ -37,7 +37,7 @@ class NostrService {
if (decoded.type === 'nsec' && typeof decoded.data === 'string') {
this.privateKey = decoded.data
}
- } catch (_e) {
+ } catch {
// Assume it's already a hex string
}
}
@@ -57,7 +57,7 @@ class NostrService {
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
this.publicKey = decoded.data
}
- } catch (_e) {
+ } catch {
// Assume it's already a hex string
}
}
diff --git a/lib/sponsoring.ts b/lib/sponsoring.ts
index ecc67bf..0fedb2e 100644
--- a/lib/sponsoring.ts
+++ b/lib/sponsoring.ts
@@ -1,17 +1,59 @@
import { getSponsoringByAuthor } from './sponsoringQueries'
+import { objectCache } from './objectCache'
import type { Article } from '@/types/nostr'
+import type { Sponsoring } from '@/types/nostr'
+
+/**
+ * Get total sponsoring for an author by their pubkey from IndexedDB cache
+ * This is much faster than network queries
+ * Returns both the amount and whether the cache was empty (to avoid duplicate calls)
+ */
+async function getAuthorSponsoringFromCache(pubkey: string): Promise<{ amount: number; cacheWasEmpty: boolean }> {
+ try {
+ const allSponsoring = await objectCache.getAll('sponsoring')
+ const sponsoringList = allSponsoring as Sponsoring[]
+
+ // If cache is empty, return immediately
+ if (sponsoringList.length === 0) {
+ return { amount: 0, cacheWasEmpty: true }
+ }
+
+ // Filter by author pubkey and sum amounts
+ const amount = sponsoringList
+ .filter((sponsoring) => sponsoring.authorPubkey === pubkey)
+ .reduce((total, sponsoring) => total + sponsoring.amount, 0)
+
+ return { amount, cacheWasEmpty: false }
+ } catch (error) {
+ console.error('Error calculating author sponsoring from cache:', error)
+ return { amount: 0, cacheWasEmpty: true }
+ }
+}
/**
* Get total sponsoring for an author by their pubkey
- * Calculates from cache (sponsoring queries) instead of tags
+ * First tries cache, then falls back to network query if needed
+ * If cache is empty, returns 0 immediately (very fast, no network request)
*/
-export async function getAuthorSponsoring(pubkey: string): Promise {
+export async function getAuthorSponsoring(pubkey: string, useCacheOnly: boolean = true): Promise {
try {
+ // Try cache first (much faster)
+ const { amount: cachedAmount, cacheWasEmpty } = await getAuthorSponsoringFromCache(pubkey)
+
+ // If cache is empty, return 0 immediately (no network request)
+ if (cacheWasEmpty) {
+ return 0
+ }
+
+ if (cachedAmount > 0 || useCacheOnly) {
+ return cachedAmount
+ }
+
+ // Fallback to network query if cache exists but cacheOnly is false
const sponsoringList = await getSponsoringByAuthor(pubkey, 5000)
- // Sum all sponsoring amounts for this author
return sponsoringList.reduce((total, sponsoring) => total + sponsoring.amount, 0)
} catch (error) {
- console.error('Error calculating author sponsoring from cache:', error)
+ console.error('Error calculating author sponsoring:', error)
return 0
}
}