Viral links: any public page + ?ref=<username|code|id> sets the same last-touch sponsor cookie as /join, counts under a 'page' hook in link stats, then redirects to the clean URL; Promo tools > Viral links builder (pages list + paste any address, copy + share); blog posts show a signed-in member's own link; chatbot answer; QA walk proves redirect + cookie
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,19 @@
|
||||
// public blog pages: the shared nav (with wallet status) and footer, nothing else
|
||||
(function () { try { IAP.renderNav(location.pathname.indexOf('/leaderboard') === 0 ? 'leaderboard' : 'blog'); } catch (e) {} })();
|
||||
// Viral links (2026-09-21): a signed-in member sees this article's address with their own ?ref= on the
|
||||
// end, and the share buttons carry it too. Whoever opens it is tagged to them like an invite link.
|
||||
(async function () {
|
||||
try {
|
||||
const share = document.querySelector('p.share'); if (!share) return;
|
||||
const me = await (await fetch('/api/me')).json();
|
||||
const tok = me && me.signedIn && (me.username || me.refCode); if (!tok) return;
|
||||
const link = 'https://instantadpay.com' + location.pathname + '?ref=' + encodeURIComponent(tok);
|
||||
share.querySelectorAll('a').forEach(a => { a.href = a.href.replace(encodeURIComponent('https://instantadpay.com' + location.pathname), encodeURIComponent(link)); });
|
||||
const bar = document.createElement('div');
|
||||
bar.id = 'viralBar';
|
||||
bar.style.cssText = 'margin-top:18px;padding:12px 14px;border:1px solid var(--line);border-radius:12px;background:var(--card, rgba(255,255,255,.03));display:flex;gap:10px;align-items:center;flex-wrap:wrap';
|
||||
bar.innerHTML = '<div style="flex:1;min-width:220px"><div class="eyebrow" style="margin:0 0 4px">Your link for this article</div><div class="mono small" style="overflow-wrap:anywhere">' + link.replace(/[&<>]/g, '') + '</div><div class="muted small" style="margin-top:4px">Share this and anyone who joins from it is yours, same as your invite link.</div></div><button class="btn small" type="button">Copy link</button>';
|
||||
bar.querySelector('button').onclick = async () => { try { await navigator.clipboard.writeText(link); IAP.status('Link copied.', 'ok'); } catch (e) { IAP.status('Copy failed.', 'bad'); } };
|
||||
share.parentNode.insertBefore(bar, share);
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
||||
+2
-2
@@ -1523,7 +1523,7 @@
|
||||
const rows = (r.angles || []).filter(a => a.views || a.joins || a.buyers);
|
||||
if (!rows.length) { t.innerHTML = '<tr><td class="muted small">No views yet. Share your link and the numbers start here.</td></tr>'; return; }
|
||||
t.innerHTML = '<tr><th>Hook</th><th>Views (30d)</th><th>Views (all)</th><th>Joined</th><th>Qualifying buyers</th></tr>'
|
||||
+ rows.map(a => '<tr><td>' + esc(a.angle === 'plain' ? 'plain link' : '?v=' + a.angle) + '</td><td class="mono">' + a.views30 + '</td><td class="mono">' + a.views + '</td><td class="mono">' + a.joins + '</td><td class="mono">' + a.buyers + '</td></tr>').join('');
|
||||
+ rows.map(a => '<tr><td>' + esc(a.angle === 'plain' ? 'plain link' : a.angle === 'page' ? 'any page (viral link)' : '?v=' + a.angle) + '</td><td class="mono">' + a.views30 + '</td><td class="mono">' + a.views + '</td><td class="mono">' + a.joins + '</td><td class="mono">' + a.buyers + '</td></tr>').join('');
|
||||
const st = $('linkSrcTable');
|
||||
if (st) {
|
||||
const src = (r.sources || []).filter(x => x.views || x.joins);
|
||||
@@ -1887,7 +1887,7 @@
|
||||
if ($('tkGo')) { $('tkGo').addEventListener('click', tkGenerate); $('tkAgain').addEventListener('click', tkGenerate); $('tkCopy').addEventListener('click', async () => { try { await navigator.clipboard.writeText($('tkText').value); IAP.status('Copied.', 'ok'); } catch (e) { $('tkText').select(); } }); $('tkSendBc').addEventListener('click', tkSendBroadcast); }
|
||||
// promo tools: pill menu switches between posts / swipes / banners / wall / videos
|
||||
function setPromoSub(name) {
|
||||
const ids = ['toolkit', 'posts', 'text', 'swipe', 'banners', 'wall', 'objections', 'videos'];
|
||||
const ids = ['toolkit', 'posts', 'text', 'swipe', 'banners', 'wall', 'objections', 'videos', 'viral'];
|
||||
if (name === 'toolkit') loadToolkit();
|
||||
if (!ids.includes(name)) name = 'posts';
|
||||
ids.forEach(id => { const el = $('promo-' + id); if (el) el.hidden = id !== name; });
|
||||
|
||||
@@ -198,6 +198,39 @@ window.IAPPromo = (function () {
|
||||
vw.appendChild(card);
|
||||
}
|
||||
}
|
||||
// viral link builder: any page on the site + ?ref=<you> (same cookie as the invite link)
|
||||
const vp = $('viralPage');
|
||||
if (vp && token && vp.dataset.filled !== token) {
|
||||
vp.dataset.filled = token;
|
||||
const origin = 'https://instantadpay.com', enc = encodeURIComponent;
|
||||
const build = raw => {
|
||||
let s = String(raw || '').trim(); if (!s) return '';
|
||||
if (!/^https?:\/\//i.test(s)) s = origin + (s.charAt(0) === '/' ? '' : '/') + s;
|
||||
let x; try { x = new URL(s); } catch (e) { return ''; }
|
||||
if (!/(^|\.)instantadpay\.com$/i.test(x.hostname) || /^\/(join|from|api|admin)(\/|$)/.test(x.pathname)) return '';
|
||||
x.searchParams.delete('ref'); x.searchParams.set('ref', token);
|
||||
return origin + x.pathname + x.search;
|
||||
};
|
||||
const render = () => {
|
||||
const typed = $('viralUrl').value.trim();
|
||||
const l = build(typed || vp.value);
|
||||
$('viralLink').textContent = l || (typed ? 'That is not a page on instantadpay.com.' : '…');
|
||||
const sh = $('viralShare'); sh.innerHTML = '';
|
||||
if (!l) return;
|
||||
const title = (!typed && vp.selectedOptions[0]) ? vp.selectedOptions[0].textContent.replace(/^Article: /, '') : 'InstantAdPay';
|
||||
[linkBtn('https://x.com/intent/tweet?url=' + enc(l) + '&text=' + enc(title), 'X'),
|
||||
linkBtn('https://www.facebook.com/sharer/sharer.php?u=' + enc(l), 'Facebook'),
|
||||
linkBtn('https://t.me/share/url?url=' + enc(l) + '&text=' + enc(title), 'Telegram'),
|
||||
linkBtn('https://wa.me/?text=' + enc(title + ' ' + l), 'WhatsApp'),
|
||||
linkBtn('https://www.linkedin.com/sharing/share-offsite/?url=' + enc(l), 'LinkedIn'),
|
||||
linkBtn('mailto:?subject=' + enc(title) + '&body=' + enc(l), 'Email')].forEach(b => sh.appendChild(b));
|
||||
if (navigator.share) { const b = document.createElement('button'); b.className = 'btn small sec'; b.type = 'button'; b.textContent = 'Share'; b.onclick = async () => { try { await navigator.share({ title, url: l }); } catch (e) {} }; sh.appendChild(b); }
|
||||
};
|
||||
fetch('/api/pages').then(r => r.json()).then(r => { vp.innerHTML = (r.pages || []).map(pg => '<option value="' + esc(pg.path) + '">' + esc(pg.title) + '</option>').join(''); render(); }).catch(() => { vp.innerHTML = '<option value="/">Home page</option>'; render(); });
|
||||
vp.onchange = () => { $('viralUrl').value = ''; render(); };
|
||||
$('viralUrl').oninput = render;
|
||||
$('viralCopy').onclick = async () => { const l = $('viralLink').textContent; if (!/^https:/.test(l)) return; try { await navigator.clipboard.writeText(l); status('Link copied.', 'ok'); } catch (e) { status('Copy failed.', 'bad'); } };
|
||||
}
|
||||
// objections
|
||||
const ob = $('promoObjections');
|
||||
if (ob && ob.dataset.filled !== link) {
|
||||
|
||||
+13
-1
@@ -744,6 +744,18 @@
|
||||
<button class="chip-t" data-promo="wall" type="button" role="tab">Banner wall</button>
|
||||
<button class="chip-t" data-promo="objections" type="button" role="tab">Objections</button>
|
||||
<button class="chip-t" data-promo="videos" type="button" role="tab">Videos</button>
|
||||
<button class="chip-t" data-promo="viral" type="button" role="tab">Viral links</button>
|
||||
</div>
|
||||
<div class="card promo-sec" id="promo-viral" hidden>
|
||||
<div class="card-head"><h3>Viral link builder</h3><span class="sub">any page on the site becomes your link</span></div>
|
||||
<p class="muted small" style="margin:0 0 10px">Pick a page or paste any instantadpay.com address. The link you get carries your name on the end. Whoever opens it is tagged to you for 30 days, exactly like your invite link, so a blog article you share can bring you a member.</p>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:8px">
|
||||
<label class="small">Page<select id="viralPage" style="width:100%"></select></label>
|
||||
<label class="small">Or paste any page address<input id="viralUrl" type="text" placeholder="https://instantadpay.com/blog/..." autocomplete="off" style="width:100%"></label>
|
||||
</div>
|
||||
<div class="promo-strip" style="margin-top:10px;padding:0"><div><span class="eyebrow" style="margin:0 0 4px">Your viral link</span><div class="mono small" id="viralLink" style="overflow-wrap:anywhere">…</div></div><button class="btn small" id="viralCopy" type="button">Copy link</button></div>
|
||||
<div id="viralShare" class="angle-btns" style="margin-top:10px"></div>
|
||||
<p class="muted small" style="margin:10px 0 0">Views and joins from these links show under Link stats as the "any page" hook. Last touch still wins: the most recent link a person opened is the one that gets the credit.</p>
|
||||
</div>
|
||||
<div class="card promo-sec" id="promo-toolkit" hidden>
|
||||
<div class="card-head"><h3>Your toolkit</h3><span class="sub" id="tkTierLine"></span></div>
|
||||
@@ -1040,7 +1052,7 @@
|
||||
</div>
|
||||
<script src="/assets/common.js?v=20260916a"></script>
|
||||
<script src="/assets/wallet.js?v=20260911a"></script>
|
||||
<script src="/assets/promo.js?v=20260911a"></script>
|
||||
<script src="/assets/promo.js?v=20260921a"></script>
|
||||
<script src="/assets/my.js?v=20260924b"></script>
|
||||
<script src="/assets/chat.js?v=20260907l"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user