Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 107x 65x 65x 65x 37x 39x 37x 39x 19x 19x 19x 19x 37x 37x 3x 3x 3x 1x 2x 2x 2x 2x 37x 37x 37x 2x 2x 37x 1x 1x 37x 37x 37x 37x 37x 37x | import { useEffect, useRef } from "react";
import { UserRole } from "shared-types/events/legacy-user";
import { sendGAEvent } from "./SendGAEvent";
type PathTrackerProps = {
userRole: UserRole;
children: React.ReactNode;
};
/**
* Wrap around your <C.Layout>.
* It will send:
* 1) a `custom_page_view` event on initial mount or after every route change
* 2) a `page_duration` event when the user leaves the previous route
*/
export const PathTracker = ({ userRole, children }: PathTrackerProps) => {
// keep track of the path of the page the user is leaving
const prevPathRef = useRef<string>(window.location.pathname);
// record when the current route was "entered"
const startTimeRef = useRef<number>(Date.now());
useEffect(() => {
//for tracking page views
const sendPageView = (path: string) => {
sendGAEvent("page_view", {
page_path: path,
referrer: prevPathRef.current || "",
...(userRole && { user_role: userRole }),
});
};
//for tracking duration spent on page
const sendPageDuration = (path: string, startTs: number) => {
if (userRole) {
const now = Date.now();
const deltaMs = now - startTs;
const timeOnPageSec = Math.round(deltaMs / 1000); // nearest second
sendGAEvent("page_duration", {
page_path: path,
...(userRole && { user_role: userRole }),
time_on_page_sec: timeOnPageSec,
});
}
};
// send page_view for the first load
sendPageView(window.location.pathname);
// when a route change is detected
const onRouteChange = () => {
const newPath = window.location.pathname;
const oldPath = prevPathRef.current;
// if the path didn’t actually change, do nothing
if (newPath === oldPath) {
return;
}
// 1) send page_duration for the old path
sendPageDuration(oldPath, startTimeRef.current);
// 2) send a new page_view for the new path, with referrer = old path
sendPageView(newPath);
// 3) update prevPath and reset startTime for the new page
prevPathRef.current = newPath;
startTimeRef.current = Date.now();
};
//pushState/replaceState so we catch in‐app ract navigation
const origPush = window.history.pushState;
const origReplace = window.history.replaceState;
window.history.pushState = function (this: History, ...args: any[]) {
origPush.apply(this, args);
onRouteChange();
};
window.history.replaceState = function (this: History, ...args: any[]) {
origReplace.apply(this, args);
onRouteChange();
};
//Also catch Back/Forward browser buttons
window.addEventListener("popstate", onRouteChange);
//cleanup- when PathTracker unmounts
return () => {
//send duration for whichever page user was on
sendPageDuration(prevPathRef.current, startTimeRef.current);
//restore history methods and remove listener
window.history.pushState = origPush;
window.history.replaceState = origReplace;
window.removeEventListener("popstate", onRouteChange);
};
}, [userRole]);
// Render children as usual
return <>{children}</>;
};
|