|
|
|
|
@ -112,7 +112,7 @@
@@ -112,7 +112,7 @@
|
|
|
|
|
const events = await nostrClient.fetchEvents( |
|
|
|
|
[{ kinds: [KIND.METADATA], authors: [pubkey], limit: 1 }], |
|
|
|
|
relays, |
|
|
|
|
{ useCache: true, cacheResults: true } |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true } |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (events.length > 0 && isMounted) { |
|
|
|
|
@ -128,7 +128,30 @@
@@ -128,7 +128,30 @@
|
|
|
|
|
async function loadWallComments(profileEventId: string) { |
|
|
|
|
if (!isMounted || !profileEventId) return; |
|
|
|
|
|
|
|
|
|
loadingWall = true; |
|
|
|
|
// Load from cache first (fast - instant display) |
|
|
|
|
try { |
|
|
|
|
const { getRecentCachedEvents } = await import('../../services/cache/event-cache.js'); |
|
|
|
|
const cachedComments = await getRecentCachedEvents([KIND.COMMENT], 60 * 60 * 1000, 100); // 1 hour cache |
|
|
|
|
|
|
|
|
|
// Filter to only comments that reference this profile event as root |
|
|
|
|
const filtered = cachedComments.filter(comment => { |
|
|
|
|
const kTag = comment.tags.find(t => t[0] === 'K' && t[1] === '0'); |
|
|
|
|
const eTag = comment.tags.find(t => t[0] === 'E' && t[1] === profileEventId); |
|
|
|
|
return kTag && eTag; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (filtered.length > 0 && isMounted) { |
|
|
|
|
wallComments = filtered.sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
loadingWall = false; // Show cached content immediately |
|
|
|
|
} else { |
|
|
|
|
loadingWall = true; // Only show loading if no cache |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
console.debug('Error loading cached wall comments:', error); |
|
|
|
|
loadingWall = true; // Show loading if cache check fails |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Stream fresh data from relays (progressive enhancement) |
|
|
|
|
try { |
|
|
|
|
// Fetch kind 1111 comments that reference this kind 0 event |
|
|
|
|
// NIP-22 format: K="0", E=profileEventId |
|
|
|
|
@ -143,7 +166,25 @@
@@ -143,7 +166,25 @@
|
|
|
|
|
} |
|
|
|
|
], |
|
|
|
|
relays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ |
|
|
|
|
useCache: 'cache-first', // Already shown cache above, now stream updates |
|
|
|
|
cacheResults: true, |
|
|
|
|
timeout: config.mediumTimeout, |
|
|
|
|
onUpdate: (newComments) => { |
|
|
|
|
if (!isMounted) return; |
|
|
|
|
// Merge with existing comments |
|
|
|
|
const commentMap = new Map(wallComments.map(c => [c.id, c])); |
|
|
|
|
for (const comment of newComments) { |
|
|
|
|
const kTag = comment.tags.find(t => t[0] === 'K' && t[1] === '0'); |
|
|
|
|
const eTag = comment.tags.find(t => t[0] === 'E' && t[1] === profileEventId); |
|
|
|
|
if (kTag && eTag) { |
|
|
|
|
commentMap.set(comment.id, comment); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
wallComments = Array.from(commentMap.values()).sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
loadingWall = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isMounted) return; |
|
|
|
|
@ -177,7 +218,7 @@
@@ -177,7 +218,7 @@
|
|
|
|
|
const pinLists = await nostrClient.fetchEvents( |
|
|
|
|
[{ kinds: [KIND.PIN_LIST], authors: [pubkey], limit: 1 }], |
|
|
|
|
profileRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isMounted || pinLists.length === 0) { |
|
|
|
|
@ -198,11 +239,24 @@
@@ -198,11 +239,24 @@
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fetch the actual pinned events |
|
|
|
|
// Fetch the actual pinned events with cache-first and streaming |
|
|
|
|
const fetchPromise = nostrClient.fetchEvents( |
|
|
|
|
[{ ids: Array.from(pinnedIds), limit: config.feedLimit }], |
|
|
|
|
profileRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ |
|
|
|
|
useCache: 'cache-first', // Load from cache first |
|
|
|
|
cacheResults: true, |
|
|
|
|
timeout: config.mediumTimeout, |
|
|
|
|
onUpdate: (newPins) => { |
|
|
|
|
if (!isMounted) return; |
|
|
|
|
// Merge with existing pins |
|
|
|
|
const pinMap = new Map(pins.map(p => [p.id, p])); |
|
|
|
|
for (const pin of newPins) { |
|
|
|
|
pinMap.set(pin.id, pin); |
|
|
|
|
} |
|
|
|
|
pins = Array.from(pinMap.values()).sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
activeFetchPromises.add(fetchPromise); |
|
|
|
|
const pinnedEvents = await fetchPromise; |
|
|
|
|
@ -227,7 +281,7 @@
@@ -227,7 +281,7 @@
|
|
|
|
|
const bookmarkLists = await nostrClient.fetchEvents( |
|
|
|
|
[{ kinds: [KIND.BOOKMARKS], authors: [pubkey], limit: 400 }], |
|
|
|
|
profileRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isMounted || bookmarkLists.length === 0) { |
|
|
|
|
@ -256,6 +310,26 @@
@@ -256,6 +310,26 @@
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Load from cache first (fast - instant display) |
|
|
|
|
try { |
|
|
|
|
const { getEvent } = await import('../../services/cache/event-cache.js'); |
|
|
|
|
const cachedBookmarks: NostrEvent[] = []; |
|
|
|
|
for (const id of bookmarkedIds) { |
|
|
|
|
const cached = await getEvent(id); |
|
|
|
|
if (cached) { |
|
|
|
|
cachedBookmarks.push(cached); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (cachedBookmarks.length > 0 && isMounted) { |
|
|
|
|
bookmarks = cachedBookmarks.sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
loadingBookmarks = false; // Show cached content immediately |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
console.debug('Error loading cached bookmarks:', error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Stream fresh data from relays (progressive enhancement) |
|
|
|
|
// Fetch the actual bookmarked events in batches |
|
|
|
|
const batchSize = 100; |
|
|
|
|
const allBookmarkedEvents: NostrEvent[] = []; |
|
|
|
|
@ -266,7 +340,21 @@
@@ -266,7 +340,21 @@
|
|
|
|
|
const fetchPromise = nostrClient.fetchEvents( |
|
|
|
|
[{ ids: batch, limit: batch.length }], |
|
|
|
|
profileRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ |
|
|
|
|
useCache: 'cache-first', // Already shown cache above, now stream updates |
|
|
|
|
cacheResults: true, |
|
|
|
|
timeout: config.mediumTimeout, |
|
|
|
|
onUpdate: (newBookmarks) => { |
|
|
|
|
if (!isMounted) return; |
|
|
|
|
// Merge with existing bookmarks |
|
|
|
|
const bookmarkMap = new Map(bookmarks.map(b => [b.id, b])); |
|
|
|
|
for (const bookmark of newBookmarks) { |
|
|
|
|
bookmarkMap.set(bookmark.id, bookmark); |
|
|
|
|
} |
|
|
|
|
bookmarks = Array.from(bookmarkMap.values()).sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
loadingBookmarks = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
activeFetchPromises.add(fetchPromise); |
|
|
|
|
const batchEvents = await fetchPromise; |
|
|
|
|
@ -301,14 +389,14 @@
@@ -301,14 +389,14 @@
|
|
|
|
|
const userPosts = await nostrClient.fetchEvents( |
|
|
|
|
[{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [pubkey], limit: 100 }], |
|
|
|
|
notificationRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isMounted) return; |
|
|
|
|
|
|
|
|
|
const userPostIds = new Set(userPosts.map(p => p.id)); |
|
|
|
|
|
|
|
|
|
// Fetch notifications: replies, mentions, reactions, zaps |
|
|
|
|
// Fetch notifications: replies, mentions, reactions, zaps with cache-first and streaming |
|
|
|
|
const notificationEvents = await nostrClient.fetchEvents( |
|
|
|
|
[ |
|
|
|
|
{ kinds: [KIND.SHORT_TEXT_NOTE], '#e': Array.from(userPostIds).slice(0, 50), limit: 100 }, // Replies to user's posts |
|
|
|
|
@ -317,7 +405,20 @@
@@ -317,7 +405,20 @@
|
|
|
|
|
{ kinds: [KIND.ZAP_RECEIPT], '#p': [pubkey], limit: 100 } // Zaps |
|
|
|
|
], |
|
|
|
|
notificationRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ |
|
|
|
|
useCache: 'cache-first', // Load from cache first |
|
|
|
|
cacheResults: true, |
|
|
|
|
timeout: config.mediumTimeout, |
|
|
|
|
onUpdate: (newNotifications) => { |
|
|
|
|
if (!isMounted) return; |
|
|
|
|
// Merge with existing notifications |
|
|
|
|
const notificationMap = new Map(notifications.map(n => [n.id, n])); |
|
|
|
|
for (const notification of newNotifications) { |
|
|
|
|
notificationMap.set(notification.id, notification); |
|
|
|
|
} |
|
|
|
|
notifications = Array.from(notificationMap.values()).sort((a, b) => b.created_at - a.created_at); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isMounted) return; |
|
|
|
|
@ -352,7 +453,7 @@
@@ -352,7 +453,7 @@
|
|
|
|
|
const fetchPromise1 = nostrClient.fetchEvents( |
|
|
|
|
[{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [currentUserPubkey], limit: config.mediumBatchLimit }], |
|
|
|
|
interactionRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.shortTimeout } // Short timeout for cache |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true, timeout: config.shortTimeout } // Short timeout for cache |
|
|
|
|
); |
|
|
|
|
activeFetchPromises.add(fetchPromise1); |
|
|
|
|
const currentUserPosts = await fetchPromise1; |
|
|
|
|
@ -368,14 +469,14 @@
@@ -368,14 +469,14 @@
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Fetch interactions with timeout to prevent blocking |
|
|
|
|
// Fetch interactions with cache-first and streaming |
|
|
|
|
const fetchPromise2 = nostrClient.fetchEvents( |
|
|
|
|
[ |
|
|
|
|
{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [profilePubkey], '#e': Array.from(currentUserPostIds).slice(0, config.smallBatchLimit), limit: config.smallBatchLimit }, // Limit IDs to avoid huge queries |
|
|
|
|
{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [profilePubkey], '#p': [currentUserPubkey], limit: config.smallBatchLimit } |
|
|
|
|
], |
|
|
|
|
interactionRelays, |
|
|
|
|
{ useCache: true, cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
{ useCache: 'cache-first', cacheResults: true, timeout: config.mediumTimeout } |
|
|
|
|
); |
|
|
|
|
activeFetchPromises.add(fetchPromise2); |
|
|
|
|
const interactionEvents = await Promise.race([ |
|
|
|
|
|