Add Plausible event tracking and Last-Modified cache busting

Track feature clicks, subscriptions, micro-transactions, top-ups,
share receipt, and close app as Plausible custom events.

Cache-bust style.css and app.js using Last-Modified headers from
HEAD requests — no build step needed, caches break on file change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-18 09:16:20 +01:00
commit 3a457b362b
2 changed files with 28 additions and 5 deletions

View file

@ -75,6 +75,7 @@ const AD_COPY = [
];
const fmt = s => `${Math.floor(s/60)}:${String(Math.floor(s%60)).padStart(2,"0")}`;
const track = (event, props) => { try { window.plausible(event, { props }); } catch(e) {} };
// Cha-ching sound effect via Tone.js
const playChaChing = async () => {
@ -279,6 +280,7 @@ function PayPlay() {
}, [flash]);
const tryAct = (k, fn) => {
track("feature_click", { feature: k, subscribed: !!has(k) });
if (has(k)) { fn(); return; }
setModal({ key: k, action: fn });
setModalPhase("choose");
@ -291,6 +293,7 @@ function PayPlay() {
if (bal < p) { setModalPhase("broke"); return; }
charge(p, s.name);
setSubs_(prev => ({ ...prev, [modal.key]: true }));
track("subscribe", { feature: modal.key, price: p });
setSuccessText(`${s.name} is yours. Was it worth it? (It wasn't.)`);
setModalPhase("success");
setTimeout(() => { modal.action(); setModal(null); setModalPhase("choose"); }, 1800);
@ -304,6 +307,7 @@ function PayPlay() {
const s = SUBS[modal.key];
if (bal < s.micro) { setModalPhase("broke"); return; }
charge(s.micro, `${s.name} (1x)`);
track("microtransaction", { feature: modal.key, price: s.micro });
setSuccessText(`One-time access. That's $${s.micro.toFixed(2)} you'll never see again.`);
setModalPhase("success");
setTimeout(() => { modal.action(); setModal(null); setModalPhase("choose"); }, 1500);
@ -317,6 +321,7 @@ function PayPlay() {
setBalKey(k => k + 1);
flash("+$9.50 ($0.50 processing fee)", "#f39c12");
playChaChing();
track("topup", { source: "modal" });
setModalPhase("choose");
};
@ -436,7 +441,7 @@ function PayPlay() {
"", `Try it: ${window.location.href}`
];
if (navigator.clipboard) {
navigator.clipboard.writeText(lines.join("\n")).then(() => flash("Receipt copied! Share your shame.", accent));
navigator.clipboard.writeText(lines.join("\n")).then(() => { flash("Receipt copied! Share your shame.", accent); track("share_receipt", { spent: spent.toFixed(2) }); });
}
};
@ -487,7 +492,7 @@ function PayPlay() {
</div>
<div style={{display:"flex",gap:10,alignItems:"center"}}>
<div key={balKey} style={{background:card,padding:"6px 14px",borderRadius:20,fontSize:12,fontWeight:700,color:accent,animation:balKey>0?"balFlash 0.6s ease":"none",transition:"background 0.3s ease"}}>💰 ${bal.toFixed(2)}</div>
<button onClick={() => { setBal(b => +(b + 9.50).toFixed(2)); setSpent(s => +(s + 0.50).toFixed(2)); setActs(x => x + 1); setBalKey(k => k + 1); flash("+$9.50 ($0.50 processing fee)", "#f39c12"); playChaChing(); }} style={{background:accent,color:"#000",border:"none",borderRadius:20,padding:"6px 14px",fontSize:11,fontWeight:700,cursor:"pointer",boxShadow:`0 2px 10px ${accent}33`}}>+$10</button>
<button onClick={() => { setBal(b => +(b + 9.50).toFixed(2)); setSpent(s => +(s + 0.50).toFixed(2)); setActs(x => x + 1); setBalKey(k => k + 1); flash("+$9.50 ($0.50 processing fee)", "#f39c12"); playChaChing(); track("topup", { source: "header" }); }} style={{background:accent,color:"#000",border:"none",borderRadius:20,padding:"6px 14px",fontSize:11,fontWeight:700,cursor:"pointer",boxShadow:`0 2px 10px ${accent}33`}}>+$10</button>
</div>
</header>
@ -609,7 +614,7 @@ function PayPlay() {
))}
</section>
<button onClick={() => has("exit") ? setShowExit(true) : setShowAd(true)} style={{background:"transparent",border:"1px solid #e74c3c33",color:"#e74c3c",padding:"10px 28px",borderRadius:8,fontSize:11,cursor:"pointer",marginBottom:16}}> Close App</button>
<button onClick={() => { track("close_app", { has_exit: !!has("exit") }); has("exit") ? setShowExit(true) : setShowAd(true); }} style={{background:"transparent",border:"1px solid #e74c3c33",color:"#e74c3c",padding:"10px 28px",borderRadius:8,fontSize:11,cursor:"pointer",marginBottom:16}}> Close App</button>
<footer style={{fontSize:10,color:muted,textAlign:"center",maxWidth:380,padding:"0 20px",lineHeight:1.8,opacity:0.7}}>
Pay2Play! v6.6.6 Music procedurally generated even the songs are cheaply made