Implement profile-scoped caching and signer invalidation logic

- Updated `profileCache.js` to support clearing runtime memory on signer change.
- Updated `NostrClient` to trigger `clearSignerRuntime` on `setActiveSigner`.
- Updated list services (`subscriptions`, `userBlocks`, `hashtagPreferences`, `watchHistory`) to listen for `runtimeCleared` and reload data.
- Updated `NostrClient` sync metadata methods to default to the active profile pubkey.
- Added `partition-updated` event emission to `profileCache`.
This commit is contained in:
google-labs-jules[bot] committed 2026-01-17 21:43:35 +00:00
1 parent 1f8dfab811
commit 3deb8d08f0
6 files changed
+50 -3

No files matched your search

+8 -3
View File
@@ -161,6 +161,7 @@ import {
summarizeRpcResultForLog,
summarizeRelayPublishResultsForLog,
} from "./nip46Client.js";
import { profileCache } from "../state/profileCache.js";
let activeSigner = null;
const activeSignerRegistry = new Map();
@@ -278,6 +279,7 @@ function setActiveSigner(signer) {
: "";
if (pubkey) {
activeSignerRegistry.set(pubkey, signer);
profileCache.clearSignerRuntime(pubkey);
}
}
@@ -3432,15 +3434,18 @@ export class NostrClient {
}
getSyncLastSeen(kind, pubkey, dTag, relayUrl) {
return this.syncMetadataStore.getLastSeen(kind, pubkey, dTag, relayUrl);
const effectivePubkey = pubkey || this.pubkey;
return this.syncMetadataStore.getLastSeen(kind, effectivePubkey, dTag, relayUrl);
}
updateSyncLastSeen(kind, pubkey, dTag, relayUrl, createdAt) {
this.syncMetadataStore.updateLastSeen(kind, pubkey, dTag, relayUrl, createdAt);
const effectivePubkey = pubkey || this.pubkey;
this.syncMetadataStore.updateLastSeen(kind, effectivePubkey, dTag, relayUrl, createdAt);
}
getPerRelaySyncLastSeen(kind, pubkey, dTag) {
return this.syncMetadataStore.getPerRelayLastSeen(kind, pubkey, dTag);
const effectivePubkey = pubkey || this.pubkey;
return this.syncMetadataStore.getPerRelayLastSeen(kind, effectivePubkey, dTag);
}
async fetchListIncrementally({ kind, pubkey, dTag, relayUrls, fetchFn } = {}) {
+15
View File
@@ -955,6 +955,21 @@ class WatchHistoryManager {
this.cacheTtlMs = 0;
this.fingerprints = new Map();
this.lastCreatedAt = 0;
profileCache.subscribe((event, detail) => {
if (event === "profileChanged") {
this.clear();
} else if (event === "runtimeCleared") {
const activePubkey = typeof this.deps.getActivePubkey === "function"
? this.deps.getActivePubkey()
: "";
const normalized = normalizeActorKey(detail.pubkey);
const active = normalizeActorKey(activePubkey);
if (normalized && active && normalized === active) {
this.cache.delete(normalized);
}
}
});
}
getCacheTtlMs() {
+5
View File
@@ -249,6 +249,11 @@ class HashtagPreferencesService {
profileCache.subscribe((event, detail) => {
if (event === "profileChanged") {
this.reset();
} else if (event === "runtimeCleared" && detail.pubkey === this.activePubkey) {
this.reset();
if (this.activePubkey) {
this.load(this.activePubkey);
}
}
});
}
+10
View File
@@ -168,6 +168,7 @@ class ProfileCache {
// But if services manage runtime state, `profileCache` acts as the persistence layer.
this.emit("update", { pubkey, section, data });
this.emit("partition-updated", { pubkey, key: section });
}
// Runtime memory cache methods (for decrypted data, if services want to use it)
@@ -197,6 +198,15 @@ class ProfileCache {
}
}
clearSignerRuntime(pubkey) {
const normalized = this.normalizeHexPubkey(pubkey);
if (!normalized) {
return;
}
this.clearMemoryCache(normalized);
this.emit("runtimeCleared", { pubkey: normalized });
}
subscribe(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
+5
View File
@@ -246,6 +246,11 @@ class SubscriptionsManager {
this.reset();
// Optionally reload immediately if we have a pubkey?
// Usually UI triggers reload via showSubscriptionVideos
} else if (event === "runtimeCleared" && detail.pubkey === this.currentUserPubkey) {
this.reset();
if (this.currentUserPubkey) {
this.loadSubscriptions(this.currentUserPubkey);
}
}
});
}
+7
View File
@@ -633,6 +633,11 @@ class UserBlockListManager {
profileCache.subscribe((event, detail) => {
if (event === "profileChanged") {
this.reset();
} else if (event === "runtimeCleared" && detail.pubkey === this.activePubkey) {
this.reset();
if (this.activePubkey) {
this.loadBlocks(this.activePubkey);
}
}
});
}
@@ -678,6 +683,8 @@ class UserBlockListManager {
async loadBlocks(userPubkey, options = {}) {
const normalized = normalizeHex(userPubkey);
this.activePubkey = normalized;
if (!normalized) {
this.reset();
this.loaded = true;