Add ensureLoaded helper for subscriptions and align error UX

This commit is contained in:
thePR0M3TH3AN committed 2025-10-26 14:16:51 -04:00
1 parent 0fb1c36fe1
commit 078c8788b3
3 files changed
+70 -10

No files matched your search

+9 -1
View File
@@ -3448,7 +3448,7 @@ export async function initChannelProfileView() {
// 3) Load subscription state when logged in, but always render the toggle UI
if (app?.pubkey) {
subscriptionsTask = subscriptions
.loadSubscriptions(app.pubkey)
.ensureLoaded(app.pubkey)
.catch((error) => {
userLogger.error("Failed to load subscriptions for channel view:", error);
})
@@ -3833,6 +3833,14 @@ function renderSubscribeButton(channelHex) {
renderSubscribeButton(channelHex);
} catch (err) {
userLogger.error("Failed to update subscription:", err);
const permissionErrorCodes = new Set([
"extension-permission-denied",
"nip04-missing",
]);
const message = permissionErrorCodes.has(err?.code)
? "Your Nostr extension must support NIP-04 to manage private lists."
: "Failed to update your subscriptions. Please try again.";
currentApp?.showError?.(message);
}
});
}
+39 -9
View File
@@ -132,6 +132,7 @@ class SubscriptionsManager {
this.subscribedPubkeys = new Set();
this.subsEventId = null;
this.loaded = false;
this.loadingPromise = null;
this.subscriptionListView = null;
this.lastRunOptions = null;
this.lastResult = null;
@@ -297,6 +298,35 @@ class SubscriptionsManager {
}
}
async ensureLoaded(actorHex) {
if (this.loaded) {
return;
}
const normalizedActor = normalizeHexPubkey(actorHex) || actorHex;
if (!normalizedActor) {
return;
}
if (this.loadingPromise) {
try {
await this.loadingPromise;
} catch (error) {
throw error;
}
return;
}
const loader = this.loadSubscriptions(normalizedActor);
this.loadingPromise = loader;
try {
await loader;
} finally {
this.loadingPromise = null;
}
}
isSubscribed(channelHex) {
const normalized = normalizeHexPubkey(channelHex);
if (!normalized) {
@@ -409,6 +439,7 @@ class SubscriptionsManager {
if (!userPubkey) {
throw new Error("No user pubkey => cannot addChannel.");
}
await this.ensureLoaded(userPubkey);
if (!normalizedChannel) {
devLogger.warn("Attempted to subscribe to invalid pubkey", channelHex);
return;
@@ -432,6 +463,7 @@ class SubscriptionsManager {
if (!userPubkey) {
throw new Error("No user pubkey => cannot removeChannel.");
}
await this.ensureLoaded(userPubkey);
if (!normalizedChannel) {
devLogger.warn("Attempted to remove invalid pubkey from subscriptions", channelHex);
return;
@@ -627,15 +659,13 @@ class SubscriptionsManager {
return null;
}
if (!this.loaded) {
try {
await this.loadSubscriptions(userPubkey);
} catch (error) {
userLogger.error(
"[SubscriptionsManager] Failed to load subscriptions while rendering feed:",
error,
);
}
try {
await this.ensureLoaded(userPubkey);
} catch (error) {
userLogger.error(
"[SubscriptionsManager] Failed to load subscriptions while rendering feed:",
error,
);
}
const channelHexes = this.getSubscribedAuthors();
+22
View File
@@ -540,6 +540,28 @@ test(
},
);
test("ensureLoaded memoizes concurrent loads", async () => {
const SubscriptionsManager = subscriptions.constructor;
const manager = new SubscriptionsManager();
let loadCount = 0;
manager.loadSubscriptions = async () => {
loadCount += 1;
await new Promise((resolve) => setTimeout(resolve, 0));
manager.loaded = true;
};
const actorHex = "f".repeat(64);
await Promise.all([
manager.ensureLoaded(actorHex),
manager.ensureLoaded(actorHex),
]);
assert.equal(loadCount, 1, "loadSubscriptions should only run once");
assert.equal(manager.loaded, true, "manager should be marked as loaded");
});
test(
"publishSubscriptionList succeeds with direct signer without requesting extension permissions",
async () => {