Load zap receipts for loaded photo events

This commit is contained in:
2026-08-20 13:14:15 -06:00
parent 2869ed6677
commit 20bac8ae31
+119 -1
View File
@@ -38,6 +38,7 @@ export default class NostrDataService extends Service {
@tracked placePhotos = [];
@tracked myContributionEvents = [];
@tracked profiles = {};
@tracked zapReceipts = {};
_profileSub = null;
_mailboxesSub = null;
@@ -46,6 +47,11 @@ export default class NostrDataService extends Service {
_contributionsSub = null;
_profileModelSubs = new Map();
_zapReceiptsSub = null;
_zapReceiptsNetworkSub = null;
_zapRefreshTimer = null;
_lastPhotoIds = new Set();
_requestSub = null;
_cachePromise = null;
_currentPlaceEntityId = null;
@@ -87,7 +93,8 @@ export default class NostrDataService extends Service {
e.kind === 5 ||
e.kind === 10002 ||
e.kind === 10063 ||
e.kind === 360
e.kind === 360 ||
e.kind === 9735
);
if (toCache.length > 0) {
@@ -247,7 +254,12 @@ export default class NostrDataService extends Service {
this._photosSub = null;
}
this._cleanupZapReceiptSubs();
this._clearZapRefreshTimer();
this.placePhotos = [];
this.zapReceipts = {};
this._lastPhotoIds = new Set();
this._clearProfileSubs();
this._currentPlaceEntityId = entityId;
@@ -267,6 +279,7 @@ export default class NostrDataService extends Service {
this.placePhotos = events;
const pubkeys = [...new Set(events.map((e) => e.pubkey))];
this.loadProfiles(pubkeys);
this._scheduleZapReceiptRefresh(events);
});
try {
@@ -500,6 +513,109 @@ export default class NostrDataService extends Service {
}
}
_scheduleZapReceiptRefresh(events) {
const newIds = new Set(events.map((e) => e.id));
let changed = false;
if (newIds.size !== this._lastPhotoIds.size) {
changed = true;
} else {
for (const id of newIds) {
if (!this._lastPhotoIds.has(id)) {
changed = true;
break;
}
}
}
if (!changed) return;
this._lastPhotoIds = newIds;
this._clearZapRefreshTimer();
this._zapRefreshTimer = setTimeout(() => {
this._zapRefreshTimer = null;
this._refreshZapReceiptSubscription([...newIds]);
}, 100);
}
_clearZapRefreshTimer() {
if (this._zapRefreshTimer) {
clearTimeout(this._zapRefreshTimer);
this._zapRefreshTimer = null;
}
}
_refreshZapReceiptSubscription(photoIds) {
this._cleanupZapReceiptSubs();
if (!photoIds || photoIds.length === 0) return;
// Batch IDs into filters of <=100 to stay under relay REQ limits
const BATCH_SIZE = 100;
const filters = [];
for (let i = 0; i < photoIds.length; i += BATCH_SIZE) {
filters.push({
kinds: [9735],
'#e': photoIds.slice(i, i + BATCH_SIZE),
});
}
// Reactive local query — emits whenever matching events are in the store
this._zapReceiptsSub = this.store.timeline(filters).subscribe((events) => {
this._updateZapReceipts(events);
});
// Load from IDB cache (fire-and-forget — store.add triggers timeline)
this._cachePromise
.then(() => this.cache.query(filters))
.then((cachedEvents) => {
if (cachedEvents && cachedEvents.length > 0) {
for (const event of cachedEvents) {
this.store.add(event);
}
}
})
.catch((e) => {
console.warn('[nostr-data] Failed to read zap receipts from cache', e);
});
// Network request (fire-and-forget)
this._zapReceiptsNetworkSub = this.nostrRelay.pool
.request(this.activeReadRelays, filters)
.subscribe({
next: (event) => {
this.store.add(event);
},
error: (err) => {
console.error('[nostr-data] Error fetching zap receipts:', err);
},
});
}
_updateZapReceipts(events) {
const grouped = {};
for (const receipt of events) {
for (const tag of receipt.tags) {
if (tag[0] === 'e' && tag[1]) {
if (!grouped[tag[1]]) grouped[tag[1]] = [];
grouped[tag[1]].push(receipt);
}
}
}
this.zapReceipts = { ...this.zapReceipts, ...grouped };
}
_cleanupZapReceiptSubs() {
if (this._zapReceiptsSub) {
this._zapReceiptsSub.unsubscribe();
this._zapReceiptsSub = null;
}
if (this._zapReceiptsNetworkSub) {
this._zapReceiptsNetworkSub.unsubscribe();
this._zapReceiptsNetworkSub = null;
}
}
_cleanupSubscriptions() {
if (this._requestSub) {
this._requestSub.unsubscribe();
@@ -525,6 +641,8 @@ export default class NostrDataService extends Service {
this._contributionsSub.unsubscribe();
this._contributionsSub = null;
}
this._cleanupZapReceiptSubs();
this._clearZapRefreshTimer();
}
willDestroy() {