analytics: honest engagement metric — Engaged readers vs Recorded visits (Codex)
Admin now shows two numbers: - Recorded visits: the existing raw count (one daily 'visit' beacon; still includes UA-spoofing bots that slip past the UA filter). - Engaged readers: distinct visitor-day with DELIBERATE activity — either the new gesture-gated 'engaged' beacon (fires once/day only after ~8s visible AND a real scroll/pointer/key/touch) or a deliberate action (source_click, full_story, share, replace_used, paywall_replace, not_today/less_like_this/hide_topic, game start/ complete/share). Explicitly EXCLUDES auto-fired visit/summary_viewed/open, replace_none, and game *_arrival (a share-loop landing, not engagement). armEngaged() in analytics.js (wired in the global layout) + a mirrored vanilla-JS beacon on the server-rendered /a/<id> share pages. 'engaged' added to the event allowlist and fired with article_id=0 so the uniqueness constraint dedups it per day. queries.admin_stats gains engaged_today/d7/d30. Bots are doubly excluded (UA filter at the beacon + the gesture gate). Tests cover the metric (engaged + deliberate counted; visit/summary/arrival not). 447 backend + 36 frontend tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -344,6 +344,14 @@ def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
// visit isn't recorded for a /a/ landing — count it here, once per day per device.
|
||||
var t=new Date().toISOString().slice(0,10);
|
||||
if(localStorage.getItem('goodnews:visitday')!==t){{localStorage.setItem('goodnews:visitday',t);beacon({{kind:'visit',article_id:0,visitor:v}});}}
|
||||
// Engaged-reader signal (mirrors the SPA's armEngaged): ~8s visible + a real gesture, once/day.
|
||||
var eng=false,gest=false,secs=0;
|
||||
function fireEng(){{
|
||||
if(eng||!gest||secs<8) return; eng=true;
|
||||
try{{ if(localStorage.getItem('goodnews:engagedday')!==t){{localStorage.setItem('goodnews:engagedday',t);beacon({{kind:'engaged',article_id:0,visitor:v}});}} }}catch(e){{}}
|
||||
}}
|
||||
var iv=setInterval(function(){{ if(document.visibilityState==='visible'){{secs++;fireEng();}} if(eng) clearInterval(iv); }},1000);
|
||||
['scroll','pointerdown','keydown','touchstart'].forEach(function(e){{window.addEventListener(e,function(){{gest=true;fireEng();}},{{passive:true}});}});
|
||||
}}catch(e){{}}
|
||||
}})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user