Files
instantadpay/public/assets/wall.js
T
martbost 3c0e94f20f Badge emblems, profile social links, and SEO/OG social previews
- Achievement badge share-image now draws a milestone emblem (⚡🎯⭐🏆) in the medal
- Profile: Facebook/X/YouTube/Instagram/TikTok/Telegram/LinkedIn/Website links, shown on the public bio page
- OG + Twitter Card tags on homepage/ledger/contract (hero banner as share image)
- Per-member OG tags server-injected into /wall/<username> so shared bio links preview with name/bio/avatar
- robots.txt + sitemap.xml; .txt/.xml MIME types

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-06 12:21:53 -05:00

75 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Public banner wall: a member's line banner plus their upline ladder, with
// their join link. The viral surface: members send traffic here, every visit
// puts eyes on the whole line.
(async function () {
await IAP.renderNav('');
const name = location.pathname.split('/').pop();
let w = null;
try { w = await (await fetch('/api/wall/' + encodeURIComponent(name))).json(); } catch (e) {}
const title = IAP.$('wallTitle');
if (!w || w.error) {
title.textContent = 'No wall under that name.';
IAP.$('wallJoin').href = '/my';
return;
}
const safeName = String(w.name).replace(/[&<>]/g, '');
title.innerHTML = safeName;
IAP.$('bioHead').hidden = false;
if (w.avatarUrl) { const av = IAP.$('bioAvatar'); av.src = w.avatarUrl; av.hidden = false; }
IAP.$('bioText').textContent = w.bio || 'Building a team on InstantAdPay — join through this page and you\'re in my line.';
if (w.qrUrl) IAP.$('bioQr').src = w.qrUrl;
// social links
const SOC = { facebook: 'Facebook', twitter: 'X', youtube: 'YouTube', instagram: 'Instagram', tiktok: 'TikTok', telegram: 'Telegram', linkedin: 'LinkedIn', website: 'Website' };
const sc = IAP.$('bioSocials');
if (sc && w.socials && typeof w.socials === 'object') {
const links = Object.keys(SOC).filter(k => w.socials[k]).map(k =>
'<a href="' + String(w.socials[k]).replace(/"/g, '%22') + '" target="_blank" rel="noopener nofollow me">' + SOC[k] + '</a>');
sc.innerHTML = links.join('');
}
IAP.$('wallCtaHead').textContent = 'Join ' + w.name + '’s line';
IAP.$('wallJoin').href = w.joinUrl;
// per-wall viewed-position memory (survives revisits; anonymous-friendly)
const VKEY = 'iap-wall-' + name;
let viewed = {};
try { viewed = JSON.parse(localStorage.getItem(VKEY) || '{}'); } catch (e) {}
const DWELL = 10;
const grid = IAP.$('wallGrid');
grid.innerHTML = '';
w.ladder.forEach((m, i) => {
const d = document.createElement('div');
d.className = 'wall-card';
const safe = String(m.name || 'member').replace(/[&<>]/g, '');
const creative = m.bannerUrl
? '<img src="' + m.bannerUrl + '" alt="' + safe + ' banner">'
: '<b>' + safe + '</b><br><span class="muted small">' + (m.targetUrl ? 'visit their site' : 'banner slot open') + '</span>';
d.innerHTML = '<div class="wall-pos">Position ' + (i + 1) + (i === 0 ? ' · this wall' : '') + '</div>'
+ '<div class="wc-creative">' + creative + '</div>'
+ '<div class="wc-action"></div>'
+ '<div class="small muted" style="margin-top:8px">' + safe + '</div>';
const act = d.querySelector('.wc-action');
const done = () => { act.innerHTML = '<span class="wc-check">✓ viewed</span>'; };
if (!m.targetUrl) { act.innerHTML = '<span class="muted small">no ad yet</span>'; }
else if (viewed[i]) { done(); }
else {
const btn = document.createElement('button');
btn.className = 'btn small'; btn.textContent = 'View this ad';
btn.addEventListener('click', () => {
window.open(m.targetUrl, '_blank'); // real visit to the advertiser
let left = DWELL;
btn.disabled = true;
act.innerHTML = '<span class="wc-timer">Viewing… ' + left + 's</span>';
const t = setInterval(() => {
left--;
if (left > 0) { act.querySelector('.wc-timer').textContent = 'Viewing… ' + left + 's'; return; }
clearInterval(t);
viewed[i] = 1;
try { localStorage.setItem(VKEY, JSON.stringify(viewed)); } catch (e) {}
done();
}, 1000);
});
act.appendChild(btn);
}
grid.appendChild(d);
});
})();