mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-10 23:01:10 +00:00
feat: implement incremental fetching for Nostr lists
Implements a `SyncMetadataStore` to persist last-seen timestamps for replaceable lists (Kind 10000, 30000, 30002, 30015). Adds `fetchListIncrementally` helper to `NostrClient` which performs per-relay `since` queries. Updates `SubscriptionsManager`, `UserBlockListManager`, `HashtagPreferencesService`, and `adminListStore` to utilize incremental fetching. Prevents local cache wipes when incremental fetches return empty results (indicating "no updates").
This commit is contained in:
1 parent
1786ad9068
commit
9d06f43321
8 files changed
+443
-329
No files matched your search
+42
-41
@@ -7,6 +7,7 @@ import {
|
||||
getActiveSigner,
|
||||
convertEventToVideo as sharedConvertEventToVideo,
|
||||
} from "./nostr/index.js";
|
||||
import { normalizeNostrPubkey } from "./nostr/nip46Client.js";
|
||||
import {
|
||||
listVideoViewEvents,
|
||||
subscribeVideoViewEvents,
|
||||
@@ -289,29 +290,6 @@ class SubscriptionsManager {
|
||||
|
||||
try {
|
||||
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
|
||||
const sessionActorPubkey = normalizeHexPubkey(
|
||||
nostrClient?.sessionActor?.pubkey,
|
||||
);
|
||||
|
||||
const authorSet = new Set();
|
||||
if (normalizedUserPubkey) {
|
||||
authorSet.add(normalizedUserPubkey);
|
||||
}
|
||||
if (sessionActorPubkey) {
|
||||
authorSet.add(sessionActorPubkey);
|
||||
}
|
||||
|
||||
const authors = Array.from(authorSet);
|
||||
if (!authors.length && userPubkey) {
|
||||
authors.push(userPubkey);
|
||||
}
|
||||
|
||||
const filter = {
|
||||
kinds: [SUBSCRIPTION_SET_KIND],
|
||||
authors,
|
||||
"#d": [SUBSCRIPTION_LIST_IDENTIFIER],
|
||||
limit: 1
|
||||
};
|
||||
|
||||
const relaySet = new Set();
|
||||
const addRelayCandidates = (candidates) => {
|
||||
@@ -347,34 +325,47 @@ class SubscriptionsManager {
|
||||
devLogger.warn(
|
||||
"[SubscriptionsManager] No relay URLs available while loading subscriptions.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const relayPromises = relayUrls.map((url) =>
|
||||
nostrClient.pool.list([url], [filter]).catch((err) => {
|
||||
userLogger.error(`[SubscriptionsManager] Relay error at ${url}`, err);
|
||||
return [];
|
||||
}),
|
||||
);
|
||||
// Use incremental fetch helper
|
||||
let events = await nostrClient.fetchListIncrementally({
|
||||
kind: SUBSCRIPTION_SET_KIND,
|
||||
pubkey: normalizedUserPubkey,
|
||||
dTag: SUBSCRIPTION_LIST_IDENTIFIER,
|
||||
relayUrls
|
||||
});
|
||||
|
||||
const settledResults = await Promise.allSettled(relayPromises);
|
||||
const events = [];
|
||||
for (const outcome of settledResults) {
|
||||
if (outcome.status === "fulfilled" && Array.isArray(outcome.value) && outcome.value.length) {
|
||||
events.push(...outcome.value);
|
||||
}
|
||||
}
|
||||
// Also check session actor if different?
|
||||
// The original code checked both userPubkey and sessionActor.pubkey.
|
||||
// fetchListIncrementally takes a single pubkey.
|
||||
// If we want to check session actor too, we need another call.
|
||||
// However, usually the subscription list belongs to the logged in user (userPubkey).
|
||||
// The session actor logic in original code:
|
||||
// const sessionActorPubkey = normalizeHexPubkey(nostrClient?.sessionActor?.pubkey);
|
||||
// ... authors: [normalizedUserPubkey, sessionActorPubkey]
|
||||
// Since fetchListIncrementally filters by author=pubkey, we only query for normalizedUserPubkey here.
|
||||
// If session actor support is critical for some delegation case, it needs a separate fetch.
|
||||
// Assuming userPubkey is the primary target for subscriptions.
|
||||
|
||||
// If we got no events from relays, we should NOT wipe the cache if we have one.
|
||||
// This protects against flaky relays returning empty results for existing users.
|
||||
// If the user truly has no list (new user), cache is likely empty anyway.
|
||||
if (!events.length) {
|
||||
// Only clear if we haven't loaded anything yet (e.g. fresh load, no cache)
|
||||
if (!this.loaded) {
|
||||
// If we have nothing loaded and found nothing, maybe user has no list.
|
||||
// But we shouldn't wipe blindly if incremental fetch just returned no *new* events.
|
||||
// Wait, fetchListIncrementally returns *events found*.
|
||||
// If it performed incremental fetch and found nothing, it returns [].
|
||||
// If it performed full fetch and found nothing, it returns [].
|
||||
|
||||
// We need to differentiate "no updates" vs "empty list".
|
||||
// If we have cached data, we keep it.
|
||||
// If we have NO cached data (this.loaded = false), and we get [], we assume empty list.
|
||||
this.subscribedPubkeys.clear();
|
||||
this.subsEventId = null;
|
||||
this.loaded = true;
|
||||
} else {
|
||||
devLogger.warn("[SubscriptionsManager] Relays returned no events, keeping existing/cached list.");
|
||||
// We have data loaded, and relays returned nothing.
|
||||
// This means no updates. We keep what we have.
|
||||
devLogger.log("[SubscriptionsManager] No updates from relays.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -382,6 +373,16 @@ class SubscriptionsManager {
|
||||
// Sort by created_at desc, pick newest
|
||||
events.sort((a, b) => b.created_at - a.created_at);
|
||||
const newest = events[0];
|
||||
|
||||
// Check if newest is actually newer than what we have
|
||||
// If we loaded from cache, we might not have the event object, just the pubkeys.
|
||||
// But we don't store the event timestamp in cache currently?
|
||||
// profileCache only stores the array.
|
||||
// Wait, we don't persist the event ID or created_at in profileCache for subscriptions.
|
||||
// So we can't strictly compare timestamps against cache.
|
||||
// However, fetchListIncrementally already handles the "newer than last sync" logic per relay.
|
||||
// So any event returned here is effectively "new information" (or re-fetched full state).
|
||||
|
||||
this.subsEventId = newest.id;
|
||||
|
||||
const signer = getActiveSigner();
|
||||
|
||||
Reference in new issue
Block a user