Badge cards: seven AI artworks for the hunting achievements, the hunter's name stamped on the ribbon server-side (ffmpeg), public share pages /b/<who>/<id> with OG cards, badge strip + share modal + unlock celebration on the board, Telegram photo post gated by OUTBOUND
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Admin · PolHunter</title>
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<link rel="stylesheet" href="/style.css?v=9">
|
||||
<link rel="stylesheet" href="/style.css?v=10">
|
||||
<style>
|
||||
label{display:block;font-size:12px;color:var(--dim);letter-spacing:.08em;text-transform:uppercase;margin:10px 0 4px}
|
||||
input,textarea,select{width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--edge);background:rgba(0,0,0,.35);color:var(--ink);font:14px var(--font)}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Your board · PolHunter</title>
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="stylesheet" href="/style.css?v=9">
|
||||
<link rel="stylesheet" href="/style.css?v=10">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
@@ -21,6 +21,12 @@
|
||||
<div class="grid" id="missions"><div class="card"><p>Loading your board…</p></div></div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="badgesSec">
|
||||
<h2>Your badges</h2>
|
||||
<div class="sub">Earned from your finds. Share one and the card carries your name and your link.</div>
|
||||
<div class="badge-strip" id="badgeStrip"></div>
|
||||
</section>
|
||||
|
||||
<div class="adslot" data-ad="board"><!-- nas: 468x60 on phones, 728x90 on wider screens --><script>(function(){var m=window.innerWidth<600;document.write('<scr'+'ipt src="https://www.networkadspace.com/showadss.php?'+(m?'w=468&h=60&n=1&bw=468&bh=60':'w=728&h=90&n=1&bw=728&bh=90')+'&c=999"></scr'+'ipt>');})();</script></div>
|
||||
|
||||
<section class="section">
|
||||
@@ -39,6 +45,7 @@
|
||||
|
||||
<footer class="foot">Rewards are for completed missions, not income. The daily pool is limited; when it is spent, claims close until midnight Central. Cryptocurrency involves risk of loss.</footer>
|
||||
</div>
|
||||
<script src="/app.js?v=6"></script>
|
||||
<div class="modal" id="shareModal" hidden><div class="modal-card"><button class="modal-x" id="shareClose" type="button" aria-label="Close">×</button><div id="shareBody"></div></div></div>
|
||||
<script src="/app.js?v=7"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -32,7 +32,26 @@
|
||||
+ '<div class="step"><b>Come back through the door.</b> Return to your dashboard and press <a href="' + IAP + '/api/my/polhunter">Open my board</a> again. Your missions unlock the moment a wallet is on the account.</div>'
|
||||
+ '</div>';
|
||||
}
|
||||
// badge cards: art, lock state, share
|
||||
function badgeStrip() {
|
||||
const el = $('badgeStrip'); if (!el || !board.badgeCards) return;
|
||||
el.innerHTML = board.badgeCards.map(b => '<div class="badge-a' + (b.earned ? '' : ' locked') + '"><img src="' + b.art + '" alt="' + esc(b.name) + '" loading="lazy"><div class="bl">' + b.icon + ' ' + esc(b.name) + '</div><div class="bs">' + esc(b.why) + '</div>' + (b.earned ? '<button class="btn pol sm" data-share="' + b.id + '">Share</button>' : '<div class="bs">\u{1F512} locked</div>') + '</div>').join('');
|
||||
el.querySelectorAll('[data-share]').forEach(x => x.addEventListener('click', () => shareBadge(x.dataset.share, false)));
|
||||
}
|
||||
function shareBadge(id, unlocked) {
|
||||
const b = (board.badgeCards || []).find(x => x.id === id); if (!b || !b.earned) return;
|
||||
const text = (unlocked ? 'Achievement unlocked on PolHunter: ' : 'I earned ') + b.name + ' (' + b.why + ') on PolHunter. Visit a site, find your code, get a drip of POL to your wallet. ' + b.page;
|
||||
const T = encodeURIComponent(text), U = encodeURIComponent(b.page);
|
||||
$('shareBody').innerHTML = '<div class="share-hd">' + (unlocked ? '<span class="tag gold">Achievement unlocked</span>' : '') + '<h3>' + b.icon + ' ' + esc(b.name) + '</h3><p class="site">' + esc(b.why) + '</p></div>'
|
||||
+ '<img class="share-img" src="' + b.image + '?t=' + Date.now() + '" alt="' + esc(b.name) + '">'
|
||||
+ '<div class="sharebtns" style="justify-content:center"><a class="btn ghost sm" target="_blank" rel="noopener" href="https://twitter.com/intent/tweet?text=' + T + '">X</a><a class="btn ghost sm" target="_blank" rel="noopener" href="https://t.me/share/url?url=' + U + '&text=' + encodeURIComponent(text.replace(b.page, '').trim()) + '">Telegram</a><a class="btn ghost sm" target="_blank" rel="noopener" href="https://www.facebook.com/sharer/sharer.php?u=' + U + '">Facebook</a><a class="btn ghost sm" target="_blank" rel="noopener" href="https://wa.me/?text=' + T + '">WhatsApp</a><a class="btn ghost sm" href="' + b.image + '" download="polhunter-' + b.id + '.jpg">Save image</a><button class="btn pol sm" id="shareCopy">Copy link</button></div>'
|
||||
+ '<p class="small" style="margin-top:10px;text-align:center">The card carries your name; the link carries your referral.</p>';
|
||||
$('shareModal').hidden = false;
|
||||
$('shareCopy').onclick = async () => { try { await navigator.clipboard.writeText(b.page); } catch (e) {} $('shareCopy').textContent = 'Copied'; setTimeout(() => { $('shareCopy').textContent = 'Copy link'; }, 1200); };
|
||||
}
|
||||
document.addEventListener('click', e => { if (e.target.id === 'shareClose' || e.target.id === 'shareModal') $('shareModal').hidden = true; });
|
||||
function render() {
|
||||
badgeStrip();
|
||||
$('who').innerHTML = '<i></i> ' + esc(board.me.username ? '@' + board.me.username : '#' + board.me.memberId) + (board.me.wallet ? '' : ' · no wallet linked');
|
||||
const r = board.missions[0] && board.missions[0].reward;
|
||||
if (r) $('rangeLine').textContent = 'Each find pays a random drip between ' + r.minPol + ' and ' + r.maxPol + ' POL. Your codes are yours alone.';
|
||||
@@ -86,6 +105,8 @@
|
||||
const r = await api('/api/my/submit', { token: open.token, code });
|
||||
const m = $('msg'); m.className = 'msg ' + (r.error ? 'bad' : 'ok'); m.textContent = r.error || r.message;
|
||||
if (!r.error) { const a = document.createElement('a'); a.href = '#share'; a.textContent = ' Share this find \u2197'; a.style.marginLeft = '6px'; m.appendChild(a); delete $('sharetext').dataset.touched; }
|
||||
// an achievement unlocked by this find: celebrate it once the board has reloaded
|
||||
if (!r.error && r.unlocked && r.unlocked.length) { const id = r.unlocked[0]; setTimeout(async () => { await load(); shareBadge(id, true); }, 1200); }
|
||||
$('submit').disabled = false;
|
||||
if (!r.error || r.spent) { open = null; setTimeout(load, 900); }
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 158 KiB |
@@ -12,7 +12,7 @@
|
||||
<meta property="og:url" content="https://polhunter.com/">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://polhunter.com/promo/polhunter-1200x630.jpg">
|
||||
<link rel="stylesheet" href="/style.css?v=9">
|
||||
<link rel="stylesheet" href="/style.css?v=10">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Leaderboard · PolHunter</title>
|
||||
<meta name="description" content="The hunters with the most finds this week, this month and all time. Every drip is on Polygon.">
|
||||
<link rel="stylesheet" href="/style.css?v=9">
|
||||
<link rel="stylesheet" href="/style.css?v=10">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
@@ -37,7 +37,7 @@
|
||||
<section class="section">
|
||||
<h2>Badges</h2>
|
||||
<div class="sub">Earned automatically from your finds. They show next to your name up there and on your board.</div>
|
||||
<div class="grid" id="badges"></div>
|
||||
<div class="badge-strip" id="badges"></div>
|
||||
</section>
|
||||
|
||||
<div class="adslot" data-ad="leaders"><!-- nas: 468x60 on phones, 728x90 on wider screens --><script>(function(){var m=window.innerWidth<600;document.write('<scr'+'ipt src="https://www.networkadspace.com/showadss.php?'+(m?'w=468&h=60&n=1&bw=468&bh=60':'w=728&h=90&n=1&bw=728&bh=90')+'&c=999"></scr'+'ipt>');})();</script></div>
|
||||
@@ -63,7 +63,7 @@
|
||||
if (pz.last && pz.last.winners.length) { document.getElementById('winners').style.display = 'block'; document.getElementById('winsub').textContent = 'Week of ' + pz.last.week + '.'; document.getElementById('winrows').innerHTML = pz.last.winners.map(w => '<div class="row lb"><span><b class="num">' + medal(w.rank) + '</b> ' + esc(w.who) + '</span><span class="site">' + w.finds + ' finds</span><span class="pol">+' + w.prizePol + ' POL prize</span></div>').join(''); }
|
||||
} catch (e) {}
|
||||
const b = await (await fetch('/api/badges')).json();
|
||||
document.getElementById('badges').innerHTML = b.badges.map(x => '<div class="card"><div style="font-size:34px">' + x.icon + '</div><h3>' + esc(x.name) + '</h3><p>' + esc(x.why) + '</p>' + (me && me.badges && me.badges.some(y => y.id === x.id) ? '<p style="margin-top:8px"><span class="tag done">Earned</span></p>' : '') + '</div>').join('');
|
||||
document.getElementById('badges').innerHTML = b.badges.map(x => { const got = me && me.badges && me.badges.some(y => y.id === x.id); return '<div class="badge-a' + (got ? '' : ' locked') + '"><img src="' + x.art + '" alt="' + esc(x.name) + '" loading="lazy"><div class="bl">' + x.icon + ' ' + esc(x.name) + '</div><div class="bs">' + esc(x.why) + '</div>' + (got ? '<p style="margin-top:8px"><span class="tag done">Earned</span></p>' : '') + '</div>'; }).join('');
|
||||
show('week');
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Promo tools · PolHunter</title>
|
||||
<meta name="description" content="Banners, posts, swipes and the teaser video, all carrying your InstantAdPay referral.">
|
||||
<link rel="stylesheet" href="/style.css?v=9">
|
||||
<link rel="stylesheet" href="/style.css?v=10">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
@@ -55,6 +55,17 @@ textarea{width:100%;padding:12px 14px;border-radius:12px;border:1px solid var(--
|
||||
.sharebtns{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} .share .card textarea{margin-top:10px}
|
||||
.banners{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px} .banner{background:var(--glass);border:1px solid var(--edge);border-radius:18px;padding:16px} .banner .bhead{margin-bottom:10px} .banner .bimg{padding:6px 0} .banner .bimg img{max-width:100%;height:auto;display:block;border-radius:6px} .banner textarea{margin-top:10px;font:500 12px var(--mono)}
|
||||
.small{font-size:13px;color:var(--dim)}
|
||||
/* badge cards */
|
||||
.badge-strip{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px}
|
||||
.badge-a{background:var(--glass);border:1px solid var(--edge);border-radius:18px;padding:12px;text-align:center;transition:border-color .2s,transform .2s}
|
||||
.badge-a:hover{border-color:var(--edge-hi);transform:translateY(-2px)} .badge-a img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:12px;display:block}
|
||||
.badge-a .bl{font-weight:800;margin-top:10px;font-size:14px} .badge-a .bs{font-size:12px;color:var(--muted);margin:4px 0 8px}
|
||||
.badge-a.locked img{filter:grayscale(1) brightness(.4)} .badge-a.locked .bl{color:var(--dim)}
|
||||
.modal{position:fixed;inset:0;z-index:1000;background:rgba(8,6,20,.82);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:20px;overflow:auto}
|
||||
.modal-card{position:relative;width:100%;max-width:520px;background:linear-gradient(180deg,#171036,#0e0a22);border:1px solid var(--edge-hi);border-radius:22px;padding:22px;box-shadow:0 30px 90px rgba(0,0,0,.6)}
|
||||
.modal-x{position:absolute;right:12px;top:10px;width:36px;height:36px;border-radius:50%;border:1px solid var(--edge);background:var(--glass);color:var(--ink);font-size:22px;cursor:pointer}
|
||||
.share-hd{text-align:center;margin-bottom:12px} .share-hd h3{font-size:22px;font-weight:800;margin-top:8px} .share-img{width:100%;border-radius:14px;display:block;margin:0 auto 14px;box-shadow:0 16px 50px rgba(130,71,229,.35)}
|
||||
.bp{max-width:560px;margin:0 auto;text-align:center;padding:30px 0 50px} .bp img{width:100%;max-width:520px;border-radius:18px;border:1px solid var(--edge);box-shadow:0 20px 60px rgba(0,0,0,.5)} .bp h1{font-size:clamp(24px,5vw,34px);margin:22px 0 8px;font-weight:800;letter-spacing:-.6px} .bp h1 em{font-style:normal;background:linear-gradient(90deg,var(--gold-hi),var(--gold));-webkit-background-clip:text;background-clip:text;color:transparent} .bp p{color:var(--muted);margin:0 auto 20px;max-width:46ch}
|
||||
.pill{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;background:var(--glass);border:1px solid var(--edge);color:var(--muted);font-size:12px;letter-spacing:.1em;text-transform:uppercase}
|
||||
.pill i{width:8px;height:8px;border-radius:50%;background:var(--ok);box-shadow:0 0 10px var(--ok)}
|
||||
/* cards */
|
||||
|
||||