diff --git a/components/video-modal.html b/components/video-modal.html index b48e26e9..3045c03f 100644 --- a/components/video-modal.html +++ b/components/video-modal.html @@ -58,7 +58,7 @@ aria-hidden="true" >

- Send zap + Send

diff --git a/css/style.css b/css/style.css index dea54de6..aad9283e 100644 --- a/css/style.css +++ b/css/style.css @@ -1027,6 +1027,12 @@ body.modal-open #app { .zap-popover__content { width: 18rem; max-width: calc(100vw - 2rem); + background: rgba(11, 17, 32, 0.96); + color: #e5e7eb; + border: 1px solid rgba(148, 163, 184, 0.35); + box-shadow: 0 22px 48px -18px rgba(8, 14, 28, 0.7); + -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(12px); } .modal-actions__overflow { diff --git a/js/app.js b/js/app.js index fd850e14..008aadf6 100644 --- a/js/app.js +++ b/js/app.js @@ -6603,8 +6603,8 @@ class Application { const tone = tracker.length > failureShares.length ? "warning" : "error"; const statusMessage = tracker.length > failureShares.length - ? `Partial zap failure. Tap zap again to retry: ${summary}.` - : `Zap failed. Tap zap again to retry: ${summary}.`; + ? `Partial zap failure. Press Send again to retry: ${summary}.` + : `Zap failed. Press Send again to retry: ${summary}.`; this.videoModal?.setZapStatus(statusMessage, tone); this.showError(error?.message || statusMessage); } else { diff --git a/js/channelProfile.js b/js/channelProfile.js index 7d634d17..20cb7549 100644 --- a/js/channelProfile.js +++ b/js/channelProfile.js @@ -105,6 +105,7 @@ let cachedZapButton = null; let cachedChannelShareButton = null; let cachedChannelMenu = null; let cachedZapControls = null; +let cachedZapForm = null; let cachedZapAmountInput = null; let cachedZapSplitSummary = null; let cachedZapStatus = null; @@ -112,6 +113,7 @@ let cachedZapReceipts = null; let cachedZapWalletPrompt = null; let cachedZapWalletLink = null; let cachedZapCloseBtn = null; +let cachedZapSendBtn = null; let pendingZapRetry = null; let zapInFlight = false; @@ -131,6 +133,7 @@ function setChannelZapVisibility(visible) { const zapButton = getChannelZapButton(); const controls = getZapControlsContainer(); const amountInput = getZapAmountInput(); + const sendButton = getZapSendButton(); if (!zapButton) { return; } @@ -161,6 +164,17 @@ function setChannelZapVisibility(visible) { if (amountInput) { amountInput.disabled = !shouldShow; } + if (sendButton) { + sendButton.disabled = !shouldShow; + sendButton.setAttribute("aria-hidden", (!shouldShow).toString()); + if (shouldShow) { + sendButton.removeAttribute("tabindex"); + sendButton.removeAttribute("aria-busy"); + sendButton.classList.remove("opacity-50", "pointer-events-none"); + } else { + sendButton.setAttribute("tabindex", "-1"); + } + } const hasWallet = typeof app?.hasActiveWalletConnection === "function" @@ -210,6 +224,16 @@ function getZapControlsContainer() { return cachedZapControls; } +function getZapFormElement() { + if (cachedZapForm && !document.body.contains(cachedZapForm)) { + cachedZapForm = null; + } + if (!cachedZapForm) { + cachedZapForm = document.getElementById("zapForm"); + } + return cachedZapForm; +} + function getZapAmountInput() { if (cachedZapAmountInput && !document.body.contains(cachedZapAmountInput)) { cachedZapAmountInput = null; @@ -250,6 +274,16 @@ function getZapReceiptsList() { return cachedZapReceipts; } +function getZapSendButton() { + if (cachedZapSendBtn && !document.body.contains(cachedZapSendBtn)) { + cachedZapSendBtn = null; + } + if (!cachedZapSendBtn) { + cachedZapSendBtn = document.getElementById("zapSendBtn"); + } + return cachedZapSendBtn; +} + function getZapWalletPrompt() { if (cachedZapWalletPrompt && !document.body.contains(cachedZapWalletPrompt)) { cachedZapWalletPrompt = null; @@ -528,6 +562,13 @@ function resetZapRetryState() { zapButton.setAttribute("aria-label", "Open zap dialog"); zapButton.title = "Open zap dialog"; } + const sendButton = getZapSendButton(); + if (sendButton) { + delete sendButton.dataset.retryPending; + sendButton.textContent = "Send"; + sendButton.setAttribute("aria-label", "Send a zap"); + sendButton.removeAttribute("title"); + } } function markZapRetryPending(shares) { @@ -546,6 +587,13 @@ function markZapRetryPending(shares) { zapButton.setAttribute("aria-label", "Retry failed zap shares"); zapButton.title = "Retry failed zap shares"; } + const sendButton = getZapSendButton(); + if (sendButton) { + sendButton.dataset.retryPending = "true"; + sendButton.textContent = "Retry"; + sendButton.setAttribute("aria-label", "Retry failed zap shares"); + sendButton.title = "Retry failed zap shares"; + } } function getZapVideoEvent() { @@ -947,16 +995,39 @@ async function executePendingRetry({ walletSettings }) { resetZapRetryState(); } -async function handleZapButtonClick(event) { +function handleZapButtonClick(event) { if (event) { event.preventDefault(); } + const zapButton = getChannelZapButton(); + if (!zapButton) { + return; + } + + if (!isZapControlsOpen()) { + openZapControls({ focus: true }); + return; + } + + if (zapInFlight) { + return; + } + + closeZapControls({ focusButton: true }); +} + +async function handleZapSend(event) { + if (event?.preventDefault) { + event.preventDefault(); + } + const zapButton = getChannelZapButton(); const amountInput = getZapAmountInput(); + const sendButton = getZapSendButton(); const app = getApp(); - if (!zapButton || !amountInput) { + if (!amountInput || !sendButton) { return; } @@ -966,8 +1037,9 @@ async function handleZapButtonClick(event) { } if (!currentChannelLightningAddress) { - setZapStatus("This creator has not configured a Lightning address yet.", "error"); - app?.showError?.("This creator has not configured a Lightning address yet."); + const message = "This creator has not configured a Lightning address yet."; + setZapStatus(message, "error"); + app?.showError?.(message); return; } @@ -981,9 +1053,13 @@ async function handleZapButtonClick(event) { } zapInFlight = true; - zapButton.disabled = true; - zapButton.setAttribute("aria-busy", "true"); - zapButton.classList.add("opacity-50", "pointer-events-none"); + if (zapButton) { + zapButton.disabled = true; + zapButton.setAttribute("aria-disabled", "true"); + } + sendButton.disabled = true; + sendButton.setAttribute("aria-busy", "true"); + sendButton.classList.add("opacity-50", "pointer-events-none"); amountInput.disabled = true; let attemptedAmount = null; @@ -1028,7 +1104,7 @@ async function handleZapButtonClick(event) { ? error.__zapShareTracker : []; logZapError( - "handleZapButtonClick", + "handleZapSend", { amount: attemptedAmount, walletSettings, @@ -1055,8 +1131,8 @@ async function handleZapButtonClick(event) { const tone = tracker.length > failureShares.length ? "warning" : "error"; const statusMessage = tracker.length > failureShares.length - ? `Partial zap failure. Tap zap again to retry: ${summary}.` - : `Zap failed. Tap zap again to retry: ${summary}.`; + ? `Partial zap failure. Press Send again to retry: ${summary}.` + : `Zap failed. Press Send again to retry: ${summary}.`; setZapStatus(statusMessage, tone); app?.showError?.(error?.message || statusMessage); } else { @@ -1067,9 +1143,13 @@ async function handleZapButtonClick(event) { } } finally { zapInFlight = false; - zapButton.disabled = false; - zapButton.removeAttribute("aria-busy"); - zapButton.classList.remove("opacity-50", "pointer-events-none"); + if (zapButton) { + zapButton.disabled = false; + zapButton.removeAttribute("aria-disabled"); + } + sendButton.disabled = false; + sendButton.removeAttribute("aria-busy"); + sendButton.classList.remove("opacity-50", "pointer-events-none"); amountInput.disabled = false; } } @@ -1297,7 +1377,8 @@ function setupZapButton() { const zapButton = getChannelZapButton(); const amountInput = getZapAmountInput(); const controls = getZapControlsContainer(); - if (!zapButton || !amountInput || !controls) { + const zapForm = getZapFormElement(); + if (!zapButton || !amountInput || !controls || !zapForm || !getZapSendButton()) { return; } @@ -1332,6 +1413,10 @@ function setupZapButton() { updateZapSplitSummary(); amountInput.addEventListener("input", handleZapAmountChange); amountInput.addEventListener("change", handleZapAmountChange); + if (zapForm.dataset.initialized !== "true") { + zapForm.addEventListener("submit", handleZapSend); + zapForm.dataset.initialized = "true"; + } zapButton.addEventListener("click", handleZapButtonClick); zapButton.dataset.initialized = "true"; } diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index 701db469..4993d2fd 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -857,7 +857,7 @@ export class VideoModal { if (pending) { this.modalZapSendBtn.dataset.retryPending = "true"; - this.modalZapSendBtn.textContent = "Retry zap"; + this.modalZapSendBtn.textContent = "Retry"; this.modalZapSendBtn.setAttribute( "aria-label", "Retry failed zap shares" @@ -867,7 +867,7 @@ export class VideoModal { } } else { delete this.modalZapSendBtn.dataset.retryPending; - this.modalZapSendBtn.textContent = "Send zap"; + this.modalZapSendBtn.textContent = "Send"; this.modalZapSendBtn.setAttribute("aria-label", "Send a zap"); this.modalZapSendBtn.removeAttribute("title"); } diff --git a/views/channel-profile.html b/views/channel-profile.html index 9ba77323..90747dd6 100644 --- a/views/channel-profile.html +++ b/views/channel-profile.html @@ -53,7 +53,7 @@ aria-label="Channel zap dialog" >

to send zaps.

- - -

- Enter an amount to view the split. -

-

-
    +
    +
    + + +
    +

    + Enter an amount to view the split. +

    +

    +
      +
      + +
      +