From 13f7a5b12fc98a8ab9d096332509761bc10ffc63 Mon Sep 17 00:00:00 2001 From: Sarto Date: Fri, 8 May 2026 11:47:23 +0330 Subject: [PATCH] feat: add donate modal and address display in telemirror --- internal/web/static/index.html | 69 ++++++++++++++++++++++++++++++- internal/web/static/telemirror.js | 50 +++++++++++++--------- 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/internal/web/static/index.html b/internal/web/static/index.html index 272678d..03a9229 100644 --- a/internal/web/static/index.html +++ b/internal/web/static/index.html @@ -1207,6 +1207,25 @@ backdrop-filter: blur(6px); } .tm-lightbox-close:hover { background: rgba(255,255,255,.28); } + .donate-addr-row { + display: flex; align-items: center; gap: 8px; + background: var(--surface, var(--bg-elevated, var(--bg))); + border: 1px solid var(--border); + border-radius: 8px; + padding: 8px 10px; + direction: ltr; + } + .donate-addr { + flex: 1; min-width: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + overflow-wrap: anywhere; + word-break: break-all; + color: var(--text); + } + .donate-thanks { margin-top: 14px; font-size: 13px; color: var(--text-dim); text-align: center; } + .donate-body { font-size: 13px; line-height: 1.7; } + .tm-scroll-down { position: absolute; bottom: max(20px, env(safe-area-inset-bottom)); @@ -3114,9 +3133,11 @@ channels yet @@ -3179,6 +3200,29 @@ + + +
@@ -3865,6 +3909,12 @@ scanner_load_presets: 'بارگذاری لیست پیش‌فرض', scanner_preset_active: 'ریزالورهای پیش‌فرض بارگذاری شد', privacy_policy: 'حریم خصوصی', + telegram: 'تلگرام', + github: 'گیت‌هاب', + donate: 'حمایت مالی', + donate_body: 'برای حمایت از من می‌تونید مبلغ دلخواه‌تون رو به صورت USDT یا USDC روی شبکه‌های زیر بفرستید: ', + wallet_address: 'آدرس ولت', + donate_thanks: 'ممنون از حمایت‌تون ❤️', telemirror_load_older: 'بارگذاری پیام‌های قدیمی‌تر', telemirror_no_older: 'پیام قدیمی‌تری وجود ندارد', telemirror_load_older_failed: 'خطا در بارگذاری پیام‌های قدیمی', @@ -4114,6 +4164,12 @@ scanner_load_presets: 'Load Presets', scanner_preset_active: 'Default resolvers loaded', privacy_policy: 'Privacy', + telegram: 'Telegram', + github: 'GitHub', + donate: 'Donate', + donate_body: 'Send any amount in USDT or USDC on: ', + wallet_address: 'Wallet address', + donate_thanks: 'Thanks for your support ❤️', telemirror_load_older: 'Load older posts', telemirror_no_older: 'No older posts', telemirror_load_older_failed: 'Failed to load older posts', @@ -4220,6 +4276,7 @@ document.querySelectorAll('[data-i18n]').forEach(function (el) { el.textContent = t(el.dataset.i18n) }); document.querySelectorAll('[data-i18n-ph]').forEach(function (el) { el.placeholder = t(el.dataset.i18nPh) }); document.querySelectorAll('[data-i18n-title]').forEach(function (el) { el.title = t(el.dataset.i18nTitle) }); + document.querySelectorAll('[data-i18n-body]').forEach(function (el) { el.innerHTML = t(el.dataset.i18nBody) }); var lf = document.getElementById('langFa'); if (lf) lf.classList.toggle('active-lang', lang === 'fa'); var le = document.getElementById('langEn'); if (le) le.classList.toggle('active-lang', lang === 'en'); document.getElementById('sendInput').style.direction = isRtl ? 'rtl' : 'ltr'; @@ -8036,6 +8093,14 @@ } // ===== COPY MSG ===== + // ===== DONATE MODAL ===== + function openDonate() { document.getElementById('donateModal').classList.add('active'); } + function closeDonate() { document.getElementById('donateModal').classList.remove('active'); } + function copyDonateAddr() { + var v = document.getElementById('donateAddr').textContent.trim(); + navigator.clipboard.writeText(v).then(function () { showToast(t('copied')) }).catch(function () { }); + } + function copyMsg(idx) { var text = currentMsgTexts[idx]; if (text === undefined) return; navigator.clipboard.writeText(text).then(function () { showToast(t('msg_copied')) }).catch(function () { }); diff --git a/internal/web/static/telemirror.js b/internal/web/static/telemirror.js index dfa3152..82a90e0 100644 --- a/internal/web/static/telemirror.js +++ b/internal/web/static/telemirror.js @@ -127,21 +127,19 @@ // Fullscreen image overlay. Tap the backdrop or X to close. Tap on // the image itself does nothing (avoids accidents on iOS double-tap - // zoom). Pushes a history entry so back/swipe closes the lightbox - // first instead of dismissing the whole telemirror modal. + // Lightbox: open pushes a history entry, close just calls + // history.back() — the popstate handler is the only thing that + // removes the DOM. That way explicit close and browser-back take + // the exact same path and no layer gets confused. var tmLightboxPushed = false; window.tmCloseLightbox = function () { - var d = document.getElementById('tmLightbox'); - if (!d) return false; - d.remove(); - if (tmLightboxPushed) { - tmLightboxPushed = false; - try { history.back(); } catch (e) { } - } - return true; + if (!document.getElementById('tmLightbox')) return; + if (tmLightboxPushed) { try { history.back(); } catch (e) { } } + else { document.getElementById('tmLightbox').remove(); } }; window.tmOpenLightbox = function (src) { - window.tmCloseLightbox(); + var existing = document.getElementById('tmLightbox'); + if (existing) existing.remove(); var d = document.createElement('div'); d.id = 'tmLightbox'; d.innerHTML = @@ -224,25 +222,40 @@ tmLoadChannels(); }; + // Suppress N popstate events while we unwind the history layers + // ourselves. Without this, history.go(-N) would fire popstates that + // re-open the sidebar / lightbox we just closed. + var tmSuppressPopstate = 0; + window.closeTelemirror = function () { var modal = document.getElementById('telemirrorModal'); if (!modal || !modal.classList.contains('active')) return; + var lb = document.getElementById('tmLightbox'); + if (lb) lb.remove(); modal.classList.remove('active'); document.body.classList.remove('tm-no-scroll'); var sb = document.getElementById('tmSidebar'); if (sb) sb.classList.remove('open'); - var steps = (tmHistoryPushed ? 1 : 0) + (tmChannelViewPushed ? 1 : 0); + var steps = (tmHistoryPushed ? 1 : 0) + + (tmChannelViewPushed ? 1 : 0) + + (tmLightboxPushed ? 1 : 0); tmHistoryPushed = false; tmChannelViewPushed = false; - if (steps > 0) { try { history.go(-steps); } catch (e) { } } + tmLightboxPushed = false; + if (steps > 0) { + tmSuppressPopstate += steps; + try { history.go(-steps); } catch (e) { } + } }; // Hardware / browser back: if our modal is the top of the history // stack, intercept and close without re-popping (history already // popped us). window.addEventListener('popstate', function () { + // Programmatic unwind from closeTelemirror — swallow these. + if (tmSuppressPopstate > 0) { tmSuppressPopstate--; return; } // Layered back: lightbox → mobile sidebar reopen → modal close. - if (document.getElementById('tmLightbox')) { + if (tmLightboxPushed) { tmLightboxPushed = false; var lb = document.getElementById('tmLightbox'); if (lb) lb.remove(); @@ -254,14 +267,13 @@ if (sb1) sb1.classList.add('open'); return; } - var modal = document.getElementById('telemirrorModal'); - if (modal && modal.classList.contains('active')) { - modal.classList.remove('active'); + if (tmHistoryPushed) { + tmHistoryPushed = false; + var modal = document.getElementById('telemirrorModal'); + if (modal) modal.classList.remove('active'); document.body.classList.remove('tm-no-scroll'); var sb = document.getElementById('tmSidebar'); if (sb) sb.classList.remove('open'); - tmHistoryPushed = false; - tmChannelViewPushed = false; } });