Add a Buy me a coffee link with a QR code to the UI

The sidebar footer and the end of Settings open a dialog with a QR code
for PayPal, so it works from the TV, plus a direct link for phones.
This commit is contained in:
Knutwurst committed 2026-10-16 19:04:03 +02:00
1 parent eaab113d8a
commit bde585eab7
3 files changed
+62

No files matched your search

+39
View File
@@ -122,8 +122,47 @@ document.addEventListener("DOMContentLoaded", () => {
loadInitialData().then(openGuideFromHash);
window.addEventListener("hashchange", openGuideFromHash);
setInterval(watchTitles, 5000);
document.querySelectorAll("[data-donate]").forEach((b) => b.addEventListener("click", openDonate));
});
const DONATE_URL = "https://paypal.me/oliverkoester";
// Donation dialog. On the TV a link is no use, so it shows a QR code for
// the phone (hidden on phones, where the button opens PayPal directly).
function openDonate() {
const back = document.activeElement;
const matrix = qrEncode(DONATE_URL);
const wrap = document.createElement("div");
wrap.className = "guide-backdrop";
wrap.innerHTML = `
<section class="guide" role="dialog" aria-modal="true" aria-labelledby="donateTitle">
<header class="guide-head">
<div>
<p class="eyebrow">Support PatchDL</p>
<h2 id="donateTitle">Buy me a coffee</h2>
</div>
<button class="icon-button guide-close" type="button" aria-label="Close">×</button>
</header>
<p class="guide-why">If you like what I do, you can buy me a coffee.</p>
${matrix ? `<div class="guide-qr">${qrSvg(matrix)}<div>
<span>Scan with your phone to open PayPal:</span>
<code>${escapeHtml(DONATE_URL)}</code></div></div>` : ""}
<a class="row-button is-update donate-open" href="${DONATE_URL}" target="_blank" rel="noopener">Open PayPal</a>
<p class="guide-why"><small>It doesn't have to be a generous donation. A few cents are enough to show me who is interested in further development. So the motivation stays and I just know that I am not programming for the bin ;)</small></p>
</section>`;
const close = () => {
document.removeEventListener("keydown", onKey);
wrap.remove();
if (back && document.contains(back)) back.focus();
};
const onKey = (e) => { if (e.key === "Escape") close(); };
wrap.querySelector(".guide-close").addEventListener("click", close);
wrap.addEventListener("click", (e) => { if (e.target === wrap) close(); });
document.addEventListener("keydown", onKey);
document.body.appendChild(wrap);
wrap.querySelector(".guide-close").focus();
}
// The server rescans on its own when games are added or removed and bumps
// titles_gen whenever the list or an update check changes; reload quietly then.
let watchBusy = false;
+6
View File
@@ -58,6 +58,7 @@
<div class="rail-foot">
<span id="railFw">FW --</span>
<span id="railSpace">-- free</span>
<button type="button" class="link-button rail-donate" data-donate>Buy me a coffee</button>
</div>
</aside>
@@ -239,6 +240,11 @@
<span>Downloads on console <em id="storageTotal"></em></span>
<div id="storageList"></div>
</div>
<div class="donate-row">
<span>Like PatchDL? You can buy me a coffee.</span>
<button type="button" class="row-button is-ghost" data-donate>Buy me a coffee</button>
</div>
</div>
</section>
+17
View File
@@ -116,6 +116,23 @@ svg {
font-size: 12px;
}
.rail-foot span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-foot .rail-donate { justify-self: start; margin-top: 4px; }
.donate-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 16px;
padding: 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface-2);
color: var(--muted);
}
.donate-row .row-button { width: auto; padding: 0 16px; }
.donate-open { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; justify-self: start; padding: 0 18px; }
/* ---------------- views + topbar ---------------- */