Handle NIP-07 permission failures when loading watch history

This commit is contained in:
thePR0M3TH3AN committed 2025-10-15 22:51:52 -04:00
1 parent deffbf7670
commit 26b9bc9dc7
2 files changed
+66 -3

No files matched your search

+5 -1
View File
@@ -285,7 +285,11 @@ export default class WatchHistoryController {
? latest.map(normalizeEntry).filter(Boolean)
: [];
} catch (error) {
this.showError("Failed to load watch history. Please try again.");
const message =
error?.code === "watch-history-extension-permission-denied"
? error.message
: "Failed to load watch history. Please try again.";
this.showError(message);
if (error && typeof error === "object") {
error.handled = true;
}
+61 -2
View File
@@ -5,6 +5,7 @@ import {
normalizePointerInput,
pointerKey,
normalizeActorKey,
requestDefaultExtensionPermissions,
} from "./nostr.js";
import {
WATCH_HISTORY_CACHE_TTL_MS,
@@ -98,6 +99,56 @@ function getSessionActorKey() {
return normalizeActorKey(nostrClient?.sessionActor?.pubkey);
}
function shouldUseExtensionForHistory(actorKey) {
const normalizedActor = normalizeActorKey(actorKey);
if (!normalizedActor) {
return false;
}
const loggedActor = normalizeActorKey(nostrClient?.pubkey);
if (!loggedActor || loggedActor !== normalizedActor) {
return false;
}
if (typeof window === "undefined") {
return false;
}
const extension = window.nostr;
if (!extension || !extension.nip04) {
return false;
}
return typeof extension.nip04.decrypt === "function";
}
async function ensureWatchHistoryExtensionPermissions(actorKey) {
if (!shouldUseExtensionForHistory(actorKey)) {
return { ok: true };
}
const permissionResult = await requestDefaultExtensionPermissions();
if (permissionResult.ok) {
return { ok: true };
}
const message =
"Approve your NIP-07 extension to decrypt encrypted watch history.";
const error = new Error(message);
error.code = "watch-history-extension-permission-denied";
error.cause = permissionResult.error;
userLogger.warn(
"[watchHistoryService] Extension denied decrypt permission required for watch history.",
{
actor: normalizeActorKey(actorKey) || null,
error: permissionResult.error,
},
);
return { ok: false, error };
}
function resolveEffectiveActorKey(actorInput) {
const supplied =
typeof actorInput === "string" && actorInput.trim()
@@ -1252,8 +1303,16 @@ function scheduleWatchHistoryRefresh(actorKey, cacheEntry = {}) {
return cacheEntry.promise;
}
const promise = nostrClient
.resolveWatchHistory(actorKey, { forceRefresh: true })
const promise = Promise.resolve()
.then(async () => {
const permissionResult = await ensureWatchHistoryExtensionPermissions(
actorKey,
);
if (!permissionResult.ok) {
throw permissionResult.error;
}
return nostrClient.resolveWatchHistory(actorKey, { forceRefresh: true });
})
.then((resolvedItems) => updateFingerprintCache(actorKey, resolvedItems))
.then(() => {
const latest = state.fingerprintCache.get(actorKey);