Analytika: landing event zachytava prvu navstevu aj s referrerom
Navsteva zacinajuca na / (preklik z FB/Messengeru, natukanie domeny) doteraz nezanechala v page_views ziadny zaznam: / je v skip liste beaconu a gate redirect na /auth je REPLACE navigacia, takze sa skipli oba hopy a referrer sa stratil. trackLanding() v main.tsx posle raz za kazdy plny load stranky pomenovany event landing s document.referrer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,8 @@ function AppInner() {
|
|||||||
// too -- that's the dashboard viewing its own traffic, not player usage.
|
// too -- that's the dashboard viewing its own traffic, not player usage.
|
||||||
// Skip /, /lobby, /game -- high-frequency in-game navigation with no
|
// Skip /, /lobby, /game -- high-frequency in-game navigation with no
|
||||||
// analytical value; the backend drops these anyway (api/stats.py _SKIPPED_PATHS).
|
// analytical value; the backend drops these anyway (api/stats.py _SKIPPED_PATHS).
|
||||||
|
// The visitor's first touch (incl. landings on "/", which both skips would
|
||||||
|
// otherwise swallow) is captured by trackLanding() in main.tsx instead.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (navigationType === 'REPLACE') return;
|
if (navigationType === 'REPLACE') return;
|
||||||
if (location.pathname.startsWith('/admin')) return;
|
if (location.pathname.startsWith('/admin')) return;
|
||||||
|
|||||||
@@ -10,3 +10,14 @@ export function trackEvent(path: string, referrer = '') {
|
|||||||
keepalive: true,
|
keepalive: true,
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** One "landing" event per full page load — the visitor's first touch,
|
||||||
|
* carrying document.referrer (e.g. facebook.com). Without it a visit that
|
||||||
|
* starts on "/" leaves no trace at all: "/" is in the beacon's skip list and
|
||||||
|
* the auth-gate redirect to /auth is a REPLACE navigation (see App.tsx), so
|
||||||
|
* both hops are skipped. Not sent on /admin — the dashboard doesn't track
|
||||||
|
* itself. */
|
||||||
|
export function trackLanding() {
|
||||||
|
if (window.location.pathname.startsWith('/admin')) return;
|
||||||
|
trackEvent('landing', document.referrer);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ import ReactDOM from 'react-dom/client';
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import { setupSocketListeners, socket, setAuthToken } from './lib/socket';
|
import { setupSocketListeners, socket, setAuthToken } from './lib/socket';
|
||||||
|
import { trackLanding } from './lib/track';
|
||||||
|
|
||||||
setupSocketListeners();
|
setupSocketListeners();
|
||||||
// Carry the stored session token into the handshake so the server auto-logs us in.
|
// Carry the stored session token into the handshake so the server auto-logs us in.
|
||||||
setAuthToken(localStorage.getItem('bridzik_token'));
|
setAuthToken(localStorage.getItem('bridzik_token'));
|
||||||
socket.connect();
|
socket.connect();
|
||||||
|
// Here (not in a component effect): runs exactly once per full page load,
|
||||||
|
// unaffected by StrictMode double-mounting.
|
||||||
|
trackLanding();
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@@ -159,6 +159,20 @@ class StatsCase(unittest.TestCase):
|
|||||||
data = run(stats.get_daily_stats())
|
data = run(stats.get_daily_stats())
|
||||||
self.assertGreaterEqual(data["top_paths"].get("login", 0), 1)
|
self.assertGreaterEqual(data["top_paths"].get("login", 0), 1)
|
||||||
|
|
||||||
|
def test_landing_event_preserves_referrer(self):
|
||||||
|
# "landing" event (main.tsx, jeden na kazdy plny load stranky) nesie
|
||||||
|
# referrer prveho dotyku -- jediny zaznam z navstevy, ktora zacina na
|
||||||
|
# "/" (preklik z FB a pod.), kedze "/" aj REPLACE redirecty sa skipuju.
|
||||||
|
run(stats.record_pageview(
|
||||||
|
path="landing",
|
||||||
|
referrer="https://facebook.com/",
|
||||||
|
user_agent=CHROME_UA,
|
||||||
|
ip="203.0.113.99",
|
||||||
|
))
|
||||||
|
data = run(stats.get_daily_stats())
|
||||||
|
self.assertGreaterEqual(data["top_paths"].get("landing", 0), 1)
|
||||||
|
self.assertGreaterEqual(data["top_referrers"].get("https://facebook.com/", 0), 1)
|
||||||
|
|
||||||
def test_rules_view_event_recorded_without_player_id(self):
|
def test_rules_view_event_recorded_without_player_id(self):
|
||||||
run(stats.record_pageview(path="rules_view", referrer="", user_agent=CHROME_UA))
|
run(stats.record_pageview(path="rules_view", referrer="", user_agent=CHROME_UA))
|
||||||
data = run(stats.get_daily_stats())
|
data = run(stats.get_daily_stats())
|
||||||
|
|||||||
Reference in New Issue
Block a user