diff --git a/manager/templates/list.html b/manager/templates/list.html index 1fab16a..cf364b8 100644 --- a/manager/templates/list.html +++ b/manager/templates/list.html @@ -299,9 +299,23 @@ function onAvatarChange(input) { reader.readAsDataURL(file); } +// Clipboard that also works over plain-HTTP LAN (navigator.clipboard needs a secure context). +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); +} function copyAddr(ev, btn, addr) { ev.stopPropagation(); - navigator.clipboard.writeText(addr).then(() => { + robustCopy(addr).then(() => { btn.innerHTML = ''; setTimeout(() => btn.innerHTML = '', 1500); }); diff --git a/manager/templates/profile.html b/manager/templates/profile.html index b9ac6f6..4099c0f 100644 --- a/manager/templates/profile.html +++ b/manager/templates/profile.html @@ -198,6 +198,15 @@ +
{% endmacro %} @@ -369,8 +378,23 @@ 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); +} function copyAddr(btn, addr) { - navigator.clipboard.writeText(addr).then(() => { + robustCopy(addr).then(() => { btn.innerHTML = ''; setTimeout(() => btn.innerHTML = '', 1500); }); @@ -504,7 +528,7 @@ async function createGroup() { function copyChLink() { const val = document.getElementById('ch-link-out').value; - navigator.clipboard.writeText(val).then(() => { + robustCopy(val).then(() => { document.getElementById('ch-result').textContent = '✓ Copied'; }); } @@ -572,20 +596,37 @@ 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 = '…'; - const resp = await fetch(`/api/profiles/{{ profile.id }}/groups/${groupId}/link`, { - headers: {'X-Token': _token()}, - }); - const data = await resp.json(); - if (data.link) { - await navigator.clipboard.writeText(data.link); - btn.textContent = '✓ Copied'; - } else { - btn.textContent = 'No link'; + 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); } - setTimeout(() => btn.textContent = orig, 2000); } // ─────────────────────────────────────────────────────────────────────────────