bank landing: community-shared explorer withdraw UI and how-it-works

This commit is contained in:
Hernâni Marques 2026-07-10 20:12:00 +02:00
parent 57973db4f0
commit 31c8dcdb9c
No known key found for this signature in database

View file

@ -82,6 +82,32 @@
border: 1px solid rgba(232, 168, 56, 0.4); border: 1px solid rgba(232, 168, 56, 0.4);
letter-spacing: 0.04em; letter-spacing: 0.04em;
} }
.community-note,
.how-note {
margin: 0 0 1.15rem;
padding: 0.8rem 0.95rem;
border-radius: 12px;
font-size: 0.88rem;
line-height: 1.45;
text-align: left;
}
.community-note {
background: rgba(94, 234, 212, 0.1);
border: 1px solid rgba(94, 234, 212, 0.4);
color: #ccfbf1;
}
.community-note strong { color: #5eead4; font-weight: 750; }
.how-note {
background: rgba(232, 168, 56, 0.08);
border: 1px solid rgba(232, 168, 56, 0.35);
color: #f5e6c8;
}
.how-note strong { color: #f0c86a; font-weight: 750; }
.how-note ol {
margin: 0.4rem 0 0;
padding-left: 1.2rem;
}
.how-note li { margin: 0.25rem 0; }
.step-card { .step-card {
background: var(--card); background: var(--card);
@ -535,10 +561,17 @@
<span class="badge">Exploration · GOA</span> <span class="badge">Exploration · GOA</span>
<h1>GOA Exploration Bank</h1> <h1>GOA Exploration Bank</h1>
<p class="sub"> <p class="sub">
Install the wallet, then <strong>one QR</strong> adds the exchange and starts a withdraw. Install the wallet, then <strong>one QR</strong> withdraws GOA from a
<strong>community-shared bank account</strong> — no personal bank registration.
</p> </p>
</header> </header>
<p class="community-note" role="note">
<strong>Community pool.</strong>
Demo GOA comes from one shared exploration account used by everyone on this site
(not your private bank login). Balance is shared; amounts are small; for play only.
</p>
<!-- ========== LIVE STATS ========== --> <!-- ========== LIVE STATS ========== -->
<section class="stats" id="stats" aria-labelledby="stats-title" hidden> <section class="stats" id="stats" aria-labelledby="stats-title" hidden>
<h2 id="stats-title">GOA flow</h2> <h2 id="stats-title">GOA flow</h2>
@ -683,23 +716,36 @@
</div> </div>
</section> </section>
<!-- ========== STEP 2: one-step withdraw-exchange ========== --> <!-- ========== STEP 2: one-click community pool withdraw ========== -->
<section class="step-card step-money" aria-labelledby="step2-title"> <section class="step-card step-money" id="withdraw" aria-labelledby="step2-title">
<h2 id="step2-title"><span class="step-num">2</span> Withdraw GOA</h2> <h2 id="step2-title"><span class="step-num">2</span> Withdraw GOA</h2>
<p class="step-tagline">One-step: add exchange + withdraw</p> <p class="step-tagline">One QR · community-shared account · no registration</p>
<div class="step-body"> <div class="step-body">
<div class="qr-box"> <div class="qr-box">
<div id="qr-withdraw-exchange"></div> <div id="qr-withdraw-demo"></div>
<div class="qr-label">Scan or open in wallet</div> <div class="qr-label">Scan or open in wallet</div>
</div> </div>
<a class="cta teal" id="open-withdraw-exchange" <a class="cta teal" id="open-withdraw-demo" href="#">
href="taler://withdraw-exchange/exchange.hacktivism.ch/">
Open in wallet → Open in wallet →
</a> </a>
<p class="meta">taler://withdraw-exchange/exchange.hacktivism.ch/</p> <p class="meta" id="withdraw-demo-meta">Preparing community withdraw…</p>
<p class="meta" id="withdraw-demo-amount" hidden></p>
<button type="button" class="cta-sec" id="withdraw-demo-refresh" style="margin-top:0.5rem">
Get a fresh QR
</button>
</div> </div>
</section> </section>
<aside class="how-note" aria-labelledby="how-title">
<strong id="how-title">How this works</strong>
<ol>
<li>A <strong>community-shared bank account</strong> (demo pool) already holds GOA.</li>
<li>Each “Open in wallet” / QR creates a <strong>one-shot withdraw</strong> from that pool — you do <em>not</em> open your own bank account.</li>
<li>Your GNU Taler app receives coins after the bank confirms (automatic on this demo).</li>
<li>Optional below: register your <em>own</em> bank UI account if you want a private balance.</li>
</ol>
</aside>
<!-- ========== Optional bank UI + mini GOA shop teaser ========== --> <!-- ========== Optional bank UI + mini GOA shop teaser ========== -->
<div class="more-section"> <div class="more-section">
<span class="more-label">Optional · bank UI &amp; GOA shop</span> <span class="more-label">Optional · bank UI &amp; GOA shop</span>
@ -757,25 +803,124 @@
<script> <script>
(function () { (function () {
var WEBUI = "https://bank.hacktivism.ch/webui/"; var WEBUI = "https://bank.hacktivism.ch/webui/";
/* taler-ops one-step: add exchange + start withdraw */ /* Secondary: own-account path (register first) */
var WITHDRAW_EXCHANGE = "taler://withdraw-exchange/exchange.hacktivism.ch/"; var WITHDRAW_EXCHANGE = "taler://withdraw-exchange/exchange.hacktivism.ch/";
function makeQr(el, text) { function makeQr(el, text) {
if (!el) return; if (!el) return;
el.innerHTML = ""; el.innerHTML = "";
if (!text) {
el.textContent = "…";
return;
}
if (typeof QRCode === "undefined") { if (typeof QRCode === "undefined") {
el.textContent = "QR lib missing"; el.textContent = "QR lib missing";
return; return;
} }
new QRCode(el, { // Encode off-DOM so QR is visible (canvas/img not collapsed)
text: text, var size = 220;
width: 220, var scratch = document.createElement("div");
height: 220, scratch.style.cssText =
"position:fixed;left:-9999px;top:0;width:" + size + "px;height:" + size + "px;opacity:0";
document.body.appendChild(scratch);
try {
new QRCode(scratch, {
text: String(text),
width: size,
height: size,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.M correctLevel: QRCode.CorrectLevel.M
}); });
var canvas = scratch.querySelector("canvas");
var dataUrl = canvas ? canvas.toDataURL("image/png") : "";
if (dataUrl) {
var img = document.createElement("img");
img.alt = "QR code";
img.width = size;
img.height = size;
img.src = dataUrl;
img.style.display = "block";
img.style.margin = "0 auto";
el.appendChild(img);
} else {
el.appendChild(scratch.firstChild || document.createTextNode("QR failed"));
}
} catch (e) {
el.textContent = "QR encode failed";
}
if (scratch.parentNode) scratch.parentNode.removeChild(scratch);
}
function setDemoWithdraw(uri, amount) {
uri = String(uri || "").replace(/:443\//g, "/").replace(/:443\?/g, "?");
var open = document.getElementById("open-withdraw-demo");
var meta = document.getElementById("withdraw-demo-meta");
var amtEl = document.getElementById("withdraw-demo-amount");
if (open) {
open.href = uri;
open.classList.remove("disabled");
}
if (meta) meta.textContent = uri;
if (amtEl) {
if (amount) {
amtEl.hidden = false;
amtEl.textContent = "Amount: " + amount + " · from community pool";
} else {
amtEl.hidden = true;
}
}
makeQr(document.getElementById("qr-withdraw-demo"), uri);
}
function loadDemoWithdraw() {
var meta = document.getElementById("withdraw-demo-meta");
var open = document.getElementById("open-withdraw-demo");
if (meta) meta.textContent = "Preparing community withdraw…";
if (open) {
open.removeAttribute("href");
open.classList.add("disabled");
}
// Prefer live mint (fresh op each time); fall back to static withdraw.uri
fetch("/intro/demo-withdraw.json", { cache: "no-store" })
.then(function (r) {
if (!r.ok) throw new Error("HTTP " + r.status);
return r.json();
})
.then(function (d) {
if (!d || !d.taler_withdraw_uri) throw new Error(d && d.error ? d.error : "no uri");
setDemoWithdraw(d.taler_withdraw_uri, d.amount || "");
})
.catch(function () {
return fetch("/intro/withdraw.uri", { cache: "no-store" })
.then(function (r) {
if (!r.ok) throw new Error("no fallback");
return r.text();
})
.then(function (t) {
var uri = String(t || "").trim();
if (!uri) throw new Error("empty");
return fetch("/intro/withdraw.amount", { cache: "no-store" })
.then(function (r) {
return r.ok ? r.text() : Promise.resolve("");
})
.then(function (a) {
setDemoWithdraw(uri, String(a || "").trim());
});
});
})
.catch(function (err) {
if (meta) {
meta.textContent =
"Could not prepare withdraw — try again or use bank UI. " +
(err && err.message ? err.message : "");
}
});
} }
makeQr(document.getElementById("qr-withdraw-exchange"), WITHDRAW_EXCHANGE); loadDemoWithdraw();
var refreshBtn = document.getElementById("withdraw-demo-refresh");
if (refreshBtn) refreshBtn.onclick = function () { loadDemoWithdraw(); };
makeQr(document.getElementById("qr-webui"), WEBUI); makeQr(document.getElementById("qr-webui"), WEBUI);
function fmtCest(unix, fallback) { function fmtCest(unix, fallback) {