Compare commits

..

5 Commits

Author SHA1 Message Date
c851a8f344 Add more sizes 2025-01-24 23:03:50 +01:00
aff49782cf Add slider for zap message size 2025-01-24 23:03:37 +01:00
3b6933e16a Add slider for QR code size 2025-01-24 21:08:04 +01:00
24cc41c7d7 Draggable zap container 2025-01-24 20:04:15 +01:00
7268c8061f Remove redundant style property 2025-01-24 19:30:30 +01:00
2 changed files with 176 additions and 40 deletions

View File

@ -9,12 +9,12 @@ import (
templ Zap(inv *lightning.Invoice) { templ Zap(inv *lightning.Invoice) {
<div class="bg-[#212529] w-fit zap-animate-in border border-[#212529] m-3 rounded-lg" id={ inv.PaymentHash }> <div class="bg-[#212529] w-fit zap-animate-in border border-[#212529] m-3 rounded-lg" id={ inv.PaymentHash }>
<div class="flex flex-row gap-3 py-1 items-center"> <div class="flex flex-row gap-3 py-1 items-center">
<svg width="32" height="32" viewBox="0 0 200 307" fill="#fada5e" xmlns="http://www.w3.org/2000/svg" class="ps-3"> <svg width="32" height="32" viewBox="0 0 200 307" fill="#fada5e" xmlns="http://www.w3.org/2000/svg" class="zap-svg ps-3">
<path d="M56 0L107.606 131H90.2129H89L1.52588e-05 131L177 307L106.979 165H121H160H200L56 0Z"/> <path d="M56 0L107.606 131H90.2129H89L1.52588e-05 131L177 307L106.979 165H121H160H200L56 0Z"/>
</svg> </svg>
<div class="flex flex-col pe-3 py-1"> <div class="flex flex-col pe-3 py-1">
<div class="text-lg text-[#f0f0f0]">{ inv.Description }</div> <div class="zap-description text-[#f0f0f0]">{ inv.Description }</div>
<div class="text-sm text-slate-300">{ fmt.Sprintf("%s", humanize(inv.Msats)) }</div> <div class="zap-amount text-slate-300">{ fmt.Sprintf("%s", humanize(inv.Msats)) }</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -16,10 +16,55 @@ templ Overlay(lnurl string, lnaddr string) {
if GetEnv(ctx) == "development" { if GetEnv(ctx) == "development" {
<script src={ GetBaseUrl(ctx) + "/js/livereload.js" }></script> <script src={ GetBaseUrl(ctx) + "/js/livereload.js" }></script>
} }
<style id="zap-custom-css"></style>
</head> </head>
<body> <body>
<div hx-ext="sse" sse-connect="/overlay/sse" sse-swap="zap" hx-swap="beforeend" class="fixed bottom-0 right-0" /> <div id="controls" class="hidden grid grid-cols-2 fixed top-0 left-0 items-center gap-3 m-3">
<label for="qr-scale-slider" class="text-black">QR code size:</label>
<input
type="range"
id="qr-scale-slider"
min="0.5"
max="5"
step="0.1"
value="1.0"
class="w-32 accent-teal"
/>
<label for="zap-scale-slider" class="text-black">Zap message size:</label>
<input
type="range"
id="zap-scale-slider"
min="1"
max="10"
step="1"
value="1.0"
class="w-32 accent-teal"
/>
</div>
<div
id="zap-container"
hx-ext="sse"
sse-connect="/overlay/sse"
sse-swap="zap"
hx-swap="beforeend"
class="fixed bottom-0 right-0 cursor-move"
draggable="true"
>
<div id="zap-template" class="hidden items-center justify-center text-center border border-dashed border-[#212529] m-0 p-3 w-fit">
zap messages will show up here
</div>
</div>
<div id="qr" class="bg-white fixed bottom-0 left-0 text-center p-3 cursor-move" draggable="true">
<div id="qr-scaler">
<div class="p-2">
<div class="font-bold">{ lnaddr }</div>
<img src={ "data:image/jpeg;base64," + qrEncode(lnurl) }/>
</div>
<div class="italic">zap us a message!</div>
</div>
</div>
<script> <script>
// zap messages disappear after a minute
document.body.addEventListener('htmx:sseMessage', function (e) { document.body.addEventListener('htmx:sseMessage', function (e) {
setTimeout(() => { setTimeout(() => {
const div = document.getElementById(e.detail.lastEventId) const div = document.getElementById(e.detail.lastEventId)
@ -27,50 +72,141 @@ templ Overlay(lnurl string, lnaddr string) {
div.addEventListener('animationend', div.remove) div.addEventListener('animationend', div.remove)
}, 60_000) }, 60_000)
}) })
</script>
<div id="qr" class="bg-white fixed bottom-0 left-0 text-center p-3 cursor-move" style="left: 0; bottom: 0;" draggable="true">
<div class="p-2">
<div class="font-bold">{ lnaddr }</div>
<img src={ "data:image/jpeg;base64," + qrEncode(lnurl) }/>
</div>
<div class="italic">zap us a message!</div>
</div>
<script>
const qr = document.getElementById("qr");
const urlParams = new URLSearchParams(window.location.search); function enableDrag(elementId, xParam, yParam) {
let queryX = parseInt(urlParams.get('x')); const element = document.getElementById(elementId);
let queryY = parseInt(urlParams.get('y')); const localStorageKey = `${elementId}-position`
const [localX,localY] = localStorage.getItem('qrPosition')?.split(',').map(Number) ?? [0,0];
let currentX = !isNaN(queryX) ? queryX : localX; const urlParams = new URLSearchParams(window.location.search);
let currentY = !isNaN(queryY) ? queryY : localY; let queryX = parseInt(urlParams.get(xParam));
qr.style.transform = `translate(${currentX}px, ${currentY}px)`; let queryY = parseInt(urlParams.get(yParam));
const [localX,localY] = localStorage.getItem(localStorageKey)?.split(',').map(Number) ?? [0,0];
let startX = 0; let currentX = !isNaN(queryX) ? queryX : localX;
let startY = 0; let currentY = !isNaN(queryY) ? queryY : localY;
element.style.transform = `translate(${currentX}px, ${currentY}px)`;
qr.addEventListener("dragstart", (e) => { let startX = 0;
startX = e.clientX - currentX; let startY = 0;
startY = e.clientY - currentY;
e.dataTransfer.setData("text/plain", `${currentX},${currentY}`);
});
qr.addEventListener("drag", (e) => { element.addEventListener("dragstart", (e) => {
if (e.clientX && e.clientY) { console.log("dragstart", e)
startX = e.clientX - currentX;
startY = e.clientY - currentY;
e.dataTransfer.setData("text/plain", `${currentX},${currentY}`);
});
element.addEventListener("drag", (e) => {
if (e.clientX && e.clientY) {
currentX = e.clientX - startX;
currentY = e.clientY - startY;
element.style.transform = `translate(${currentX}px, ${currentY}px)`;
}
});
element.addEventListener("dragend", (e) => {
currentX = e.clientX - startX; currentX = e.clientX - startX;
currentY = e.clientY - startY; currentY = e.clientY - startY;
qr.style.transform = `translate(${currentX}px, ${currentY}px)`; element.style.transform = `translate(${currentX}px, ${currentY}px)`;
} localStorage.setItem(localStorageKey, `${currentX},${currentY}`);
}); const params = new URLSearchParams(window.location.search);
params.set(xParam, currentX);
params.set(yParam, currentY);
window.location.search = params.toString();
});
}
qr.addEventListener("dragend", (e) => { enableDrag("qr", "qrX", "qrY")
currentX = e.clientX - startX; enableDrag("zap-container", "zapX", "zapY")
currentY = e.clientY - startY;
qr.style.transform = `translate(${currentX}px, ${currentY}px)`; // hide 'zap messages will show up here' if query param given
localStorage.setItem('qrPosition', `${currentX},${currentY}`); function showHelpers() {
window.location.search = `?x=${currentX}&y=${currentY}` document.querySelector('#zap-template').style.display = "flex";
}); document.querySelector('#controls').style.display = "grid";
}
const params = new URLSearchParams(window.location.search)
if (params.has("help", 1)) {
showHelpers()
}
function enableSlider(sliderId, elementId, param, onChange) {
const slider = document.getElementById(sliderId);
const target = document.getElementById(elementId);
const initialValue = localStorage.getItem(param) ?? 1.0;
slider.value = initialValue;
onChange(target, initialValue);
let timeout
slider.oninput = function (e) {
const value = e.target.value
clearTimeout(timeout);
localStorage.setItem(param, value);
onChange(target, value);
timeout = setTimeout(() => {
const params = new URLSearchParams(window.location.search);
params.set(param, value);
window.location.search = params.toString();
}, 1_000);
}
}
enableSlider('qr-scale-slider', 'qr-scaler', 'qrScale', (element, value) => {
element.style.transform = `scaleX(${value}) scaleY(${value})`;
})
enableSlider('zap-scale-slider', 'zap-custom-css', 'zapScale', (element, value) => {
// https://tailwindcss.com/docs/font-size
const description = {
1.0: 'font-size: 18px', // text-lg
2.0: 'font-size: 20px', // text-xl
3.0: 'font-size: 24px', // text-2xl
4.0: 'font-size: 30px', // text-3xl
5.0: 'font-size: 36px', // text-4xl
6.0: 'font-size: 48px', // text-5xl
7.0: 'font-size: 60px', // text-6xl
8.0: 'font-size: 72px', // text-7xl
9.0: 'font-size: 96px', // text-8xl
10.0: 'font-size: 120px', // text-9xl
}
const amount = {
1.0: 'font-size: 14px', // text-sm
2.0: 'font-size: 16px', // text-base
3.0: 'font-size: 18px', // text-lg
4.0: 'font-size: 20px', // text-xl
5.0: 'font-size: 24px', // text-2xl
6.0: 'font-size: 30px', // text-3xl
7.0: 'font-size: 36px', // text-4xl
8.0: 'font-size: 48px', // text-5xl
9.0: 'font-size: 60px', // text-6xl
10.0: 'font-size: 72px', // text-7xl
}
const svg = {
1.0: 'width: 32px; height: 32px',
2.0: 'width: 40px; height: 40px',
3.0: 'width: 48px; height: 48px',
4.0: 'width: 56px; height: 56px',
5.0: 'width: 64px; height: 64px',
6.0: 'width: 72px; height: 72px',
7.0: 'width: 80px; height: 80px',
8.0: 'width: 88px; height: 88px',
9.0: 'width: 96px; height: 96px',
10.0: 'width: 104px; height: 104px',
}
element.textContent = `
#zap-template {
${description[value]}
}
.zap-description {
${description[value]}
}
.zap-amount {
${amount[value]}
}
.zap-svg {
${svg[value]}
}
`;
})
</script> </script>
</body> </body>
</html> </html>