From 332b4a1801904717cdae02624d6b060b6e12b2e6 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 5 Jun 2026 22:46:26 +0100 Subject: [PATCH] Add show/hide Link + QR toggles for every SimpleX link (default hidden) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reusable link box (Jinja macro in _macros.html + shared JS/CSS/QR lib in base.html): a 'Link' button toggles the URL (with copy) and a 'QR' button toggles a lazily-rendered QR of the same link — both hidden by default. Applied to the profile address, profile groups & channels, and the profile cards on the list pages. Centralize robustCopy in base.html; drop the per-page duplicates and the old async group-link fetch (groups now use their known link). Co-Authored-By: Claude Opus 4.8 --- manager/templates/_macros.html | 23 +++++++++ manager/templates/base.html | 55 ++++++++++++++++++++++ manager/templates/list.html | 29 ++---------- manager/templates/profile.html | 86 +++++----------------------------- 4 files changed, 93 insertions(+), 100 deletions(-) create mode 100644 manager/templates/_macros.html diff --git a/manager/templates/_macros.html b/manager/templates/_macros.html new file mode 100644 index 0000000..665ec70 --- /dev/null +++ b/manager/templates/_macros.html @@ -0,0 +1,23 @@ +{# Reusable SimpleX link box: a "Link" and a "QR" toggle button, both hidden by default. + linkbtns(id) — just the two toggle buttons (for table action cells) + linkpanels(link,id) — the hidden link + QR containers (place where they can span) + linkbox(link,id) — buttons + panels together (for block contexts) + `id` must be unique on the page. JS lives in base.html (sxToggleLink/sxToggleQr/sxCopy). #} + +{% macro linkbtns(id) %} + + +{% endmacro %} + +{% macro linkpanels(link, id) %} + + +{% endmacro %} + +{% macro linkbox(link, id) %} +
{{ linkbtns(id) }}
+ {{ linkpanels(link, id) }} +{% endmacro %} diff --git a/manager/templates/base.html b/manager/templates/base.html index afabf5a..607b2ae 100644 --- a/manager/templates/base.html +++ b/manager/templates/base.html @@ -16,6 +16,7 @@ + - + {{ ui.linkbox(profile.address, 'addr') }} {% else %}

Start the profile to generate an address.

{% endif %} @@ -176,20 +165,20 @@ {{ name }} {% if invited %} - ⏳ invited + invited {% else %} {% endif %} -
+
{% if invited %} {% else %} {{ 'Broadcast' if g.is_channel else 'Chat' }} - + {% if g.link %}{{ ui.linkbtns('g' ~ gid) }}{% endif %} {% if is_owner %} @@ -198,15 +187,9 @@
- - -
- - -
- - + {% if g.link %} + {{ ui.linkpanels(g.link, 'g' ~ gid) }} + {% endif %} {% endmacro %} @@ -378,21 +361,7 @@ async function sendMsg() { } } -// Clipboard that also works over plain-HTTP LAN (navigator.clipboard needs a -// secure context). Falls back to a hidden textarea + execCommand. -function robustCopy(text) { - if (navigator.clipboard && window.isSecureContext) { - return navigator.clipboard.writeText(text).catch(() => fallbackCopy(text)); - } - return Promise.resolve(fallbackCopy(text)); -} -function fallbackCopy(text) { - const ta = document.createElement('textarea'); - ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'; - document.body.appendChild(ta); ta.focus(); ta.select(); - try { document.execCommand('copy'); } catch (e) {} - document.body.removeChild(ta); -} +// robustCopy/fallbackCopy live in base.html (shared). Directory-website URL copy: function copyAddr(btn, addr) { robustCopy(addr).then(() => { btn.innerHTML = ''; @@ -596,38 +565,7 @@ async function leaveGroup(groupId, name, btn) { else { btn.disabled = false; btn.textContent = 'Leave'; alert('Failed to leave: ' + (data.detail || 'unknown')); } } -function copyGroupLinkBtn(gid, btn) { - const url = document.getElementById('link-url-' + gid).textContent; - robustCopy(url).then(() => { - btn.innerHTML = ''; - setTimeout(() => btn.innerHTML = '', 1500); - }); -} -async function getGroupLink(groupId, btn) { - const row = document.getElementById('link-row-' + groupId); - if (row && row.style.display !== 'none') { row.style.display = 'none'; return; } // toggle off - const orig = btn.textContent; - btn.textContent = '…'; - try { - const resp = await fetch(`/api/profiles/{{ profile.id }}/groups/${groupId}/link`, { - headers: {'X-Token': _token()}, - }); - const data = await resp.json(); - btn.textContent = orig; - if (data.link) { - const a = document.getElementById('link-url-' + groupId); - a.textContent = data.link; - a.href = data.link; - row.style.display = ''; - } else { - btn.textContent = 'No link'; - setTimeout(() => btn.textContent = orig, 2000); - } - } catch (e) { - btn.textContent = 'Error'; - setTimeout(() => btn.textContent = orig, 2000); - } -} +// Group/channel links use the shared link box (sxToggleLink/sxToggleQr in base.html). // ───────────────────────────────────────────────────────────────────────────── function refreshLog(event) {