Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial GTM Migrations #456

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 57 additions & 5 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import Script from "next/script";
import type { AppProps } from "next/app";
import { DocsContextProvider } from "layouts/DocsPage/context";
import { posthog, sendPageview } from "utils/posthog";
import { posthog, sendEngagedView, sendPageview } from "utils/posthog";
import { TabContextProvider } from "components/Tabs";

// https://larsmagnus.co/blog/how-to-optimize-custom-fonts-with-next-font
Expand Down Expand Up @@ -68,7 +68,32 @@ interface dataLayerItem {
declare global {
var dataLayer: dataLayerItem[]; // eslint-disable-line no-var
}
const useIsEngaged = () => {
const router = useRouter();
const [timerReached, setTimerReached] = useState(false);
const [secondPageReached, setSecondPageReached] = useState(false);
const [isEngaged, setIsEngaged] = useState(false);

useEffect(() => {
setTimeout(() => {
setTimerReached(true);
}, 30000);
const routeChanged = () => {
setSecondPageReached(true);
};

router.events.on("routeChangeComplete", routeChanged);
return () => {
router.events.off("routeChangeComplete", routeChanged);
};
}, [router.events]);

useEffect(() => {
setIsEngaged(secondPageReached && timerReached);
}, [secondPageReached, timerReached]);

Comment on lines +77 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this have to be two effects? From what I can tell from the logic, we are checking

  1. if the second page was reached
  2. if they've been here for 30 seconds

Since the timer isn't being reset anywhere, we can just put setIsEngaged into the timeout instead right? this removes extra state and an extra useEffect. something like this pseudo code below. what do you think?

  useEffect(() => {
    const timeout = setTimeout(() => {
      setIsEngaged(secondPageReached);
    }, 30000);

    const routeChanged = () => {
      setSecondPageReached(true);
    };

    router.events.on('routeChangeComplete', routeChanged);
    return () => {
      clearTimeout(timeout);
      router.events.off('routeChangeComplete', routeChanged);
    };
  }, [router.events, secondPageReached]);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't quite match the criteria for tracking the engagement metrics.

With the current logic the timeout continues through page navigation and will trigger the tracking events when

  1. The User has spent 30 seconds on the site
  2. The user has visited at least two pages

With this proposed implementation the timeout resets on page navigation and the user would have to spend the full 30 seconds on the second (or any subsequent) page visited to be marked as an engaged viewer. This would cause a rapidly navigating user to not be marked as an engaged viewer.

return isEngaged;
};
const Analytics = () => {
return (
<>
Expand Down Expand Up @@ -163,6 +188,18 @@ const Analytics = () => {
{/* End Google Tag Manager (noscript) */}
</>
)}
{/* Quailified Script */}
<Script id="script_qualified">
{`(function (w, q) {
w["QualifiedObject"] = q;
w[q] =
w[q] ||
function () {
(w[q].q = w[q].q || []).push(arguments);
};
})(window, "qualified")`}
</Script>
<Script src="https://js.qualified.com/qualified.js?token=GWPbwWJLtjykim4W" />

{NEXT_PUBLIC_REDDIT_ID && (
<>
Expand All @@ -180,14 +217,29 @@ const Analytics = () => {

const MyApp = ({ Component, pageProps }: AppProps) => {
const router = useRouter();
const isEngaged = useIsEngaged();

useEffect(() => {
posthog(); // init posthog
if (!isEngaged) return;
// Trigger engagement view events here
sendEngagedView();
}, [isEngaged]);

Comment on lines +223 to +226
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to return an isEngaged? Would it be better if we just sendEngagedView in the hook where we are setting engaged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this mainly comes down to which solution you perceive as being more readable. I think the current solution keeps the hook readable by isolating the tracking logic from the tracking events.
But like I said, it's mostly personal preference at this point.

router.events.on("routeChangeComplete", sendPageview);
const Pageviews = () => {
// Trigger page views here

// Qualified page view
if (!!window["qualified"]) window["qualified"]("page");
// Posthog page view
sendPageview();
};
useEffect(() => {
posthog(); // init posthog
// Trigger initial load page views
Pageviews();
router.events.on("routeChangeComplete", Pageviews);
return () => {
router.events.off("routeChangeComplete", sendPageview);
router.events.off("routeChangeComplete", Pageviews);
};
}, [router.events]);

Expand Down
8 changes: 8 additions & 0 deletions utils/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ export const sendDocsFeedback = async (rating: string, comment: string) => {
"web.docs.comment": comment,
});
};

export const sendEngagedView = async () => {
const ph = await posthog();
ph?.capture("web.engagement.timer", {
"web.engagement.sessionTimerThreshold": 30,
"web.engagement.sessionPageThreshold": 2,
});
};
Loading