Preserve channel profile metadata and gate zap access

This commit is contained in:
thePR0M3TH3AN committed 2025-10-18 14:13:10 -04:00
1 parent e02fcf370a
commit a98705bb2f
2 files changed
+131 -25

No files matched your search

+127 -25
View File
@@ -113,6 +113,7 @@ function logZapError(stage, details = {}, error) {
}
let currentChannelProfileEvent = null;
let currentChannelProfileSnapshot = null;
let currentChannelProfileHasExplicitPayload = false;
let cachedZapButton = null;
let cachedChannelShareButton = null;
@@ -146,6 +147,33 @@ const PROFILE_EVENT_CACHE_TTL_MS = 5 * 60 * 1000;
const channelProfileMetadataCache = new Map();
function hasExplicitChannelProfilePayload(profile) {
return (
profile &&
typeof profile === "object" &&
Object.keys(profile).length > 0
);
}
function touchChannelProfileCacheEntry(pubkey) {
if (typeof pubkey !== "string" || !pubkey) {
return false;
}
const existing = channelProfileMetadataCache.get(pubkey);
if (!existing) {
return false;
}
channelProfileMetadataCache.set(pubkey, {
timestamp: Date.now(),
profile: { ...existing.profile },
event: existing.event ? { ...existing.event } : null
});
return true;
}
const SUPPORTED_BANNER_REFERRER_POLICIES = new Set([
"no-referrer",
"origin",
@@ -471,7 +499,8 @@ function setChannelZapVisibility(visible) {
typeof app?.isUserLoggedIn === "function"
? app.isUserLoggedIn()
: Boolean(app?.normalizeHexPubkey?.(app?.pubkey));
const shouldShow = !!visible && isLoggedIn;
const shouldShow = !!visible;
const requiresLogin = shouldShow && !isLoggedIn;
if (
shouldShow &&
@@ -482,7 +511,10 @@ function setChannelZapVisibility(visible) {
zapButton.toggleAttribute("hidden", !shouldShow);
zapButton.disabled = !shouldShow;
zapButton.setAttribute("aria-disabled", (!shouldShow).toString());
zapButton.setAttribute(
"aria-disabled",
(requiresLogin || !shouldShow).toString()
);
zapButton.setAttribute("aria-hidden", (!shouldShow).toString());
zapButton.setAttribute("aria-expanded", "false");
if (shouldShow) {
@@ -494,18 +526,24 @@ function setChannelZapVisibility(visible) {
clearZapReceipts();
setZapStatus("", "neutral");
}
if (requiresLogin) {
zapButton.dataset.requiresLogin = "true";
} else {
delete zapButton.dataset.requiresLogin;
}
closeZapControls();
if (controls) {
controls.setAttribute("aria-hidden", "true");
controls.hidden = true;
}
const shouldEnableInputs = shouldShow && !requiresLogin;
if (amountInput) {
amountInput.disabled = !shouldShow;
amountInput.disabled = !shouldEnableInputs;
}
if (sendButton) {
sendButton.disabled = !shouldShow;
sendButton.setAttribute("aria-hidden", (!shouldShow).toString());
if (shouldShow) {
sendButton.disabled = !shouldEnableInputs;
sendButton.setAttribute("aria-hidden", (!shouldEnableInputs).toString());
if (shouldEnableInputs) {
sendButton.removeAttribute("tabindex");
sendButton.removeAttribute("aria-busy");
delete sendButton.dataset.state;
@@ -527,12 +565,30 @@ function setChannelZapVisibility(visible) {
typeof settings?.nwcUri === "string" ? settings.nwcUri.trim() : "";
return uri.length > 0;
})();
setZapWalletPromptVisible(shouldShow && !hasWallet);
if (shouldShow) {
setZapWalletPromptVisible(shouldEnableInputs && !hasWallet);
if (shouldEnableInputs) {
setupZapWalletLink();
}
}
function isSessionActorWithoutLogin() {
const app = getApp();
const isLoggedIn =
typeof app?.isUserLoggedIn === "function"
? app.isUserLoggedIn()
: Boolean(app?.normalizeHexPubkey?.(app?.pubkey) || app?.pubkey);
if (isLoggedIn) {
return false;
}
const sessionPubkey =
typeof nostrClient?.sessionActor?.pubkey === "string"
? nostrClient.sessionActor.pubkey.trim()
: "";
return sessionPubkey.length > 0;
}
function getChannelShareButton() {
if (
cachedChannelShareButton &&
@@ -1677,6 +1733,13 @@ function handleZapButtonClick(event) {
return;
}
if (isSessionActorWithoutLogin()) {
const app = getApp();
const message = "Log in with your Nostr profile to send zaps.";
app?.showError?.(message);
return;
}
if (!zapPopover || zapPopoverTrigger !== zapButton) {
setupZapButton({ force: true });
}
@@ -2242,6 +2305,7 @@ export async function initChannelProfileView() {
currentChannelLightningAddress = "";
currentChannelProfileEvent = null;
currentChannelProfileSnapshot = null;
currentChannelProfileHasExplicitPayload = false;
setCachedPlatformLightningAddress("");
resetZapRetryState();
clearZapReceipts();
@@ -2707,7 +2771,30 @@ function normalizeChannelProfileMetadata(raw = {}) {
function rememberChannelProfile(pubkey, { profile = {}, event = null } = {}) {
if (typeof pubkey !== "string" || !pubkey) {
return;
return false;
}
const hasPayload = hasExplicitChannelProfilePayload(profile);
if (!hasPayload) {
touchChannelProfileCacheEntry(pubkey);
return false;
}
const incomingEventTimestamp =
typeof event?.created_at === "number" ? event.created_at : 0;
const existing = channelProfileMetadataCache.get(pubkey);
const existingEventTimestamp =
typeof existing?.event?.created_at === "number"
? existing.event.created_at
: 0;
if (
existingEventTimestamp &&
incomingEventTimestamp &&
incomingEventTimestamp < existingEventTimestamp
) {
touchChannelProfileCacheEntry(pubkey);
return false;
}
channelProfileMetadataCache.set(pubkey, {
@@ -2715,6 +2802,8 @@ function rememberChannelProfile(pubkey, { profile = {}, event = null } = {}) {
profile: normalizeChannelProfileMetadata(profile),
event: event ? { ...event } : null
});
return true;
}
function getCachedChannelProfile(pubkey) {
@@ -3189,9 +3278,7 @@ function applyChannelProfileMetadata({
const normalized = normalizeChannelProfileMetadata(profile);
const hasExplicitProfilePayload =
profile &&
typeof profile === "object" &&
Object.keys(profile).length > 0;
hasExplicitChannelProfilePayload(profile);
const incomingEventTimestamp =
typeof event?.created_at === "number" ? event.created_at : 0;
const existingEventTimestamp =
@@ -3208,12 +3295,17 @@ function applyChannelProfileMetadata({
return;
}
const shouldIgnoreEmptyPayload =
!hasExplicitProfilePayload &&
(!incomingEventTimestamp ||
incomingEventTimestamp <= existingEventTimestamp);
if (shouldIgnoreEmptyPayload) {
return;
if (!hasExplicitProfilePayload) {
if (currentChannelProfileHasExplicitPayload) {
return;
}
const shouldIgnoreEmptyPayload =
!incomingEventTimestamp ||
incomingEventTimestamp <= existingEventTimestamp;
if (shouldIgnoreEmptyPayload) {
return;
}
}
}
@@ -3308,6 +3400,7 @@ function applyChannelProfileMetadata({
}
currentChannelProfileSnapshot = { ...normalized };
currentChannelProfileHasExplicitPayload = hasExplicitProfilePayload;
if (lightningAddress) {
if (lightningAddress !== previousLightning) {
@@ -3421,18 +3514,27 @@ async function loadUserProfile(pubkey) {
return;
}
rememberChannelProfile(pubkey, result);
const cachedUpdated = rememberChannelProfile(pubkey, result);
if (typeof app?.setProfileCacheEntry === "function") {
if (cachedUpdated && typeof app?.setProfileCacheEntry === "function") {
try {
app.setProfileCacheEntry(pubkey, {
...result.profile,
lightningAddress:
const profileForCache =
result && typeof result.profile === "object" && result.profile
? { ...result.profile }
: {};
if (!profileForCache.lightningAddress) {
const lightningFallback =
result.profile?.lightningAddress ||
result.profile?.lud16 ||
result.profile?.lud06 ||
""
});
"";
if (lightningFallback) {
profileForCache.lightningAddress = lightningFallback;
}
}
app.setProfileCacheEntry(pubkey, profileForCache);
} catch (error) {
userLogger.warn(
"Failed to persist channel profile metadata to cache:",
+4
View File
@@ -117,6 +117,10 @@ export default class ZapController {
open() {
this.resetFeedback();
if (!this.isUserLoggedIn()) {
this.notifyError("Log in with your Nostr profile to send zaps.");
return;
}
this.applyDefaultAmount();
this.preloadLightningMetadata().catch((error) => {
devLogger.warn("[zap] Preload metadata error:", error);