diff --git a/public/join-now.js b/public/join-now.js index eb7a454..20d74df 100644 --- a/public/join-now.js +++ b/public/join-now.js @@ -59,9 +59,12 @@ fetch('/api/public/config').then(function(r){return r.json()}).then(function(c){ // someone's /join/ page. Without this a prospect who wandered through the nav // enrolled under the company rotation and the inviter lost the referral. if(!ref){ try{ var ck=document.cookie.match(/(?:^|;\s*)rmc_ref=(\d{1,15})/); if(ck)ref=ck[1]; }catch(e){} } + // ...and the placement choice the invite link carried, for the same reason. + var directFlag=qp.get('direct'); + if(directFlag!=='1'&&directFlag!=='0'){ try{ var cd=document.cookie.match(/(?:^|;\s*)rmc_direct=([01])/); if(cd)directFlag=cd[1]; }catch(e){} } // Direct link (?direct=1): opt out of rotation — register straight under // the inviter (spillover in their team), never the next-to-qualify member. - if(ref&&/^\d{1,15}$/.test(ref)&&qp.get('direct')==='1'){ + if(ref&&/^\d{1,15}$/.test(ref)&&directFlag==='1'){ sponsorId=String(ref); $('sponsorLine').innerHTML='You’ll join directly under #'+ref+' — a spillover placement in their team.'; return; } if(ref&&/^\d{1,15}$/.test(ref)){ diff --git a/public/start.js b/public/start.js index d4b53ab..62b9a4e 100644 --- a/public/start.js +++ b/public/start.js @@ -26,7 +26,12 @@ async function load(){ // Route exactly the way that member's own invite page would: their moving link sends // the join to the next position in their leg, unless their position is set to take // direct placements. Same rules, so the two pages can never disagree. - const t=(s.directDefault||!s.joinTarget)?{id:s.id,referralUrl:s.referralUrl,directCount:s.directCount}:s.joinTarget; + // an explicit ?direct=1/0 from the invite link wins over the position's default + const dq=(function(){try{const q=new URLSearchParams(location.search).get('direct'); + if(q==='1'||q==='0')return q; + const m=document.cookie.match(/(?:^|;\s*)rmc_direct=([01])/);return m?m[1]:null;}catch(e){return null}})(); + const goDirect=dq==='1'||(dq!=='0'&&(s.directDefault||!s.joinTarget)); + const t=goDirect?{id:s.id,referralUrl:s.referralUrl,directCount:s.directCount}:s.joinTarget; currentSponsor={id:t.id,name:null,directs:(t.directCount!==undefined?t.directCount:0),goal:2,referralUrl:t.referralUrl}; // say whose team this is, so it never reads as an anonymous "current placement" const badge=document.querySelector('.live-badge'); @@ -59,7 +64,7 @@ async function load(){ // carry the inviter through to enrolment — /join-now already knows how to place a // ?ref properly (direct placement or next open spot in their leg). Without this the // button dropped the referral on the floor and enrolled under the company rotation. - document.getElementById('joinButton').href='/join-now'+(ref?('?ref='+encodeURIComponent(ref)):''); + document.getElementById('joinButton').href='/join-now'+(ref?('?ref='+encodeURIComponent(ref)+(dq?('&direct='+dq):'')):''); {const dl=document.getElementById('dappJoinLink');if(dl&¤tSponsor.referralUrl){dl.href=currentSponsor.referralUrl;dl.classList.remove('hidden');}} // the company-queue line only means anything on the company-rotation path // only on the company-rotation path — the invited path already wrote its own line here diff --git a/server.js b/server.js index c0d3659..678f356 100644 --- a/server.js +++ b/server.js @@ -1793,7 +1793,14 @@ function serveMemberPage(req,res,file,kind,id){ // to a stranger. The invite is the referral, so it has to outlive the one page it landed // on. 30 days, Lax so it survives a normal click-through from anywhere. if(kind==='join'&&/^\d{1,15}$/.test(String(id))){ - head['Set-Cookie']='rmc_ref='+id+'; Path=/; Max-Age=2592000; SameSite=Lax'+(IS_PROD?'; Secure':''); + const attrs='; Path=/; Max-Age=2592000; SameSite=Lax'+(IS_PROD?'; Secure':''); + const ck=['rmc_ref='+id+attrs]; + // ?direct=1/0 is the inviter's explicit choice about placement, so it has to travel with + // the referral. Without it a prospect who wandered off the invite page would come back + // through the moving link instead — the opposite of what the shared link asked for. + const dq=new URL(req.url,'http://x').searchParams.get('direct'); + if(dq==='1'||dq==='0')ck.push('rmc_direct='+dq+attrs); + head['Set-Cookie']=ck; } res.writeHead(200,securityHeaders(head)); res.end(html);