diff --git a/js/app.js b/js/app.js index 38061915..7c0c1466 100644 --- a/js/app.js +++ b/js/app.js @@ -57,6 +57,7 @@ import { ingestLocalViewEvent, } from "./viewCounter.js"; import { splitAndZap as splitAndZapDefault } from "./payments/zapSplit.js"; +import { LOGIN_TO_ZAP_MESSAGE } from "./payments/zapMessages.js"; import { formatAbsoluteTimestamp as formatAbsoluteTimestampUtil, formatTimeAgo as formatTimeAgoUtil, @@ -650,7 +651,7 @@ class Application { this.boundVideoModalZapOpenHandler = (event) => { const requiresLogin = Boolean(event?.detail?.requiresLogin); if (requiresLogin) { - this.showError("Log in with your Nostr profile to send zaps."); + this.showStatus(LOGIN_TO_ZAP_MESSAGE, { autoHideMs: 5000 }); } this.zapController?.open(); }; @@ -751,6 +752,7 @@ class Application { this.statusContainer = document.getElementById("statusContainer") || null; this.statusMessage = this.statusContainer?.querySelector("[data-status-message]") || null; + this.statusAutoHideHandle = null; // Auth state this.pubkey = null; @@ -7282,9 +7284,24 @@ class Application { }, 5000); } - showStatus(msg) { + showStatus(msg, options = {}) { const container = this.statusContainer; const messageTarget = this.statusMessage; + const ownerDocument = + container?.ownerDocument || (typeof document !== "undefined" ? document : null); + const defaultView = + ownerDocument?.defaultView || (typeof window !== "undefined" ? window : null); + const clearScheduler = + defaultView?.clearTimeout || + (typeof clearTimeout === "function" ? clearTimeout : null); + const schedule = + defaultView?.setTimeout || (typeof setTimeout === "function" ? setTimeout : null); + + if (this.statusAutoHideHandle && typeof clearScheduler === "function") { + clearScheduler(this.statusAutoHideHandle); + this.statusAutoHideHandle = null; + } + const HTMLElementCtor = container?.ownerDocument?.defaultView?.HTMLElement || (typeof HTMLElement !== "undefined" ? HTMLElement : null); @@ -7302,6 +7319,9 @@ class Application { return; } + const { autoHideMs } = + options && typeof options === "object" ? options : Object.create(null); + if (messageTarget && messageTarget instanceof HTMLElementCtor) { messageTarget.textContent = msg; } else { @@ -7309,6 +7329,38 @@ class Application { } container.classList.remove("hidden"); this.updateNotificationPortalVisibility(); + + if ( + Number.isFinite(autoHideMs) && + autoHideMs > 0 && + typeof schedule === "function" + ) { + const expectedText = + messageTarget && messageTarget instanceof HTMLElementCtor + ? messageTarget.textContent + : container.textContent; + this.statusAutoHideHandle = schedule(() => { + this.statusAutoHideHandle = null; + const activeContainer = this.statusContainer; + if (activeContainer !== container) { + return; + } + const activeMessageTarget = + this.statusMessage && typeof this.statusMessage.textContent === "string" + ? this.statusMessage + : activeContainer; + if (activeMessageTarget.textContent !== expectedText) { + return; + } + if (activeMessageTarget === this.statusMessage) { + this.statusMessage.textContent = ""; + } else { + activeContainer.textContent = ""; + } + activeContainer.classList.add("hidden"); + this.updateNotificationPortalVisibility(); + }, autoHideMs); + } } showSuccess(msg) { @@ -7361,6 +7413,22 @@ class Application { this.teardownModalViewCountSubscription(); this.videoModalReadyPromise = null; + if (this.statusAutoHideHandle) { + const ownerDocument = + this.statusContainer?.ownerDocument || + (typeof document !== "undefined" ? document : null); + const defaultView = + ownerDocument?.defaultView || + (typeof window !== "undefined" ? window : null); + const clearScheduler = + defaultView?.clearTimeout || + (typeof clearTimeout === "function" ? clearTimeout : null); + if (typeof clearScheduler === "function") { + clearScheduler(this.statusAutoHideHandle); + } + this.statusAutoHideHandle = null; + } + if (this.watchHistoryTelemetry) { try { this.watchHistoryTelemetry.destroy(); diff --git a/js/channelProfile.js b/js/channelProfile.js index ce23ca8f..46a90202 100644 --- a/js/channelProfile.js +++ b/js/channelProfile.js @@ -33,6 +33,7 @@ import { validateInvoiceAmount } from "./payments/zapSharedState.js"; import { splitAndZap } from "./payments/zapSplit.js"; +import { LOGIN_TO_ZAP_MESSAGE } from "./payments/zapMessages.js"; import { resolveLightningAddress, fetchPayServiceData, @@ -47,7 +48,7 @@ import { const getApp = () => getApplication(); -const LOGIN_TO_ZAP_MESSAGE = "Log in with your Nostr profile to send zaps."; +const LOGIN_NOTIFICATION_AUTO_HIDE_MS = 5000; function syncNotificationPortalVisibility(doc) { if (!doc) { @@ -79,45 +80,131 @@ function syncNotificationPortalVisibility(doc) { function showLoginRequiredToZapNotification() { const app = getApp(); - if (app && typeof app.showError === "function") { - app.showError(LOGIN_TO_ZAP_MESSAGE); - return; + if (app) { + if (typeof app.showStatus === "function") { + app.showStatus(LOGIN_TO_ZAP_MESSAGE, { + autoHideMs: LOGIN_NOTIFICATION_AUTO_HIDE_MS, + }); + return; + } + + if (typeof app.showError === "function") { + app.showError(LOGIN_TO_ZAP_MESSAGE); + return; + } } const doc = typeof document !== "undefined" ? document : null; - const container = doc?.getElementById("errorContainer") || null; - const HTMLElementCtor = - doc?.defaultView?.HTMLElement || - (typeof HTMLElement !== "undefined" ? HTMLElement : null); - if (!HTMLElementCtor || !(container instanceof HTMLElementCtor)) { + if (!doc) { return; } - container.textContent = LOGIN_TO_ZAP_MESSAGE; - container.classList.remove("hidden"); + const HTMLElementCtor = + doc.defaultView?.HTMLElement || + (typeof HTMLElement !== "undefined" ? HTMLElement : null); + + if (!HTMLElementCtor) { + return; + } + + const statusContainer = doc.getElementById("statusContainer"); + + if (statusContainer && statusContainer instanceof HTMLElementCtor) { + const messageTarget = + statusContainer.querySelector("[data-status-message]") || null; + + if (messageTarget && messageTarget instanceof HTMLElementCtor) { + messageTarget.textContent = LOGIN_TO_ZAP_MESSAGE; + } else { + statusContainer.textContent = LOGIN_TO_ZAP_MESSAGE; + } + + statusContainer.classList.remove("hidden"); + syncNotificationPortalVisibility(doc); + + const schedule = + doc.defaultView?.setTimeout || + (typeof setTimeout === "function" ? setTimeout : null); + + if (typeof schedule === "function") { + const initialTarget = + messageTarget && messageTarget instanceof HTMLElementCtor + ? messageTarget + : statusContainer; + const expectedText = initialTarget.textContent; + schedule(() => { + if (!doc || typeof doc.contains !== "function") { + return; + } + + if (!doc.contains(statusContainer)) { + return; + } + + const activeTarget = + statusContainer.querySelector("[data-status-message]") || null; + const resolvedTarget = + activeTarget && activeTarget instanceof HTMLElementCtor + ? activeTarget + : messageTarget && messageTarget instanceof HTMLElementCtor + ? messageTarget + : statusContainer; + + if (resolvedTarget.textContent !== expectedText) { + return; + } + + if (activeTarget && activeTarget instanceof HTMLElementCtor) { + activeTarget.textContent = ""; + } else if (messageTarget && messageTarget instanceof HTMLElementCtor) { + messageTarget.textContent = ""; + } else { + statusContainer.textContent = ""; + } + + statusContainer.classList.add("hidden"); + syncNotificationPortalVisibility(doc); + }, LOGIN_NOTIFICATION_AUTO_HIDE_MS); + } + + return; + } + + const errorContainer = doc.getElementById("errorContainer"); + + if (!errorContainer || !(errorContainer instanceof HTMLElementCtor)) { + return; + } + + errorContainer.textContent = LOGIN_TO_ZAP_MESSAGE; + errorContainer.classList.remove("hidden"); syncNotificationPortalVisibility(doc); - const scheduler = - doc?.defaultView?.setTimeout || + const schedule = + doc.defaultView?.setTimeout || (typeof setTimeout === "function" ? setTimeout : null); - if (typeof scheduler === "function") { - scheduler(() => { - if (!doc || typeof doc.contains !== "function") { - return; - } - - if (!doc.contains(container)) { - return; - } - - if (container.textContent === LOGIN_TO_ZAP_MESSAGE) { - container.textContent = ""; - container.classList.add("hidden"); - syncNotificationPortalVisibility(doc); - } - }, 5000); + if (typeof schedule !== "function") { + return; } + + schedule(() => { + if (!doc || typeof doc.contains !== "function") { + return; + } + + if (!doc.contains(errorContainer)) { + return; + } + + if (errorContainer.textContent !== LOGIN_TO_ZAP_MESSAGE) { + return; + } + + errorContainer.textContent = ""; + errorContainer.classList.add("hidden"); + syncNotificationPortalVisibility(doc); + }, LOGIN_NOTIFICATION_AUTO_HIDE_MS); } let currentChannelHex = null; diff --git a/js/payments/zapMessages.js b/js/payments/zapMessages.js new file mode 100644 index 00000000..94349493 --- /dev/null +++ b/js/payments/zapMessages.js @@ -0,0 +1,5 @@ +// js/payments/zapMessages.js + +export const LOGIN_TO_ZAP_MESSAGE = + "Log in and connect a wallet to send zaps."; +