From 655dc57099094798ad8d064e90186d8e514cd5bc Mon Sep 17 00:00:00 2001
From: Nikita <93587872+ncarazon@users.noreply.github.com>
Date: Thu, 24 Oct 2024 20:18:29 +0300
Subject: [PATCH] fix(analytics): adjust cookies modal and posthog setup,
implement GA custom events for feed search and sidebar
---
front_end/package-lock.json | 21 ++++++--
front_end/package.json | 2 +-
.../tournament/components/tournament_feed.tsx | 2 +
.../components/cookies_banner/index.tsx | 47 +++++++++--------
.../questions/components/question_topics.tsx | 51 +++++++++++++++++--
front_end/src/app/layout.tsx | 9 ++++
front_end/src/app/providers.tsx | 30 ++++++-----
.../src/components/posthog_page_view.tsx | 24 +++++++++
.../components/posts_feed/paginated_feed.tsx | 9 +++-
9 files changed, 149 insertions(+), 46 deletions(-)
create mode 100644 front_end/src/components/posthog_page_view.tsx
diff --git a/front_end/package-lock.json b/front_end/package-lock.json
index cc4fa2fe6..f30f5c681 100644
--- a/front_end/package-lock.json
+++ b/front_end/package-lock.json
@@ -49,7 +49,7 @@
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.12",
"percent-round": "^2.3.1",
- "posthog-js": "^1.160.0",
+ "posthog-js": "^1.175.0",
"puppeteer": "^22.12.1",
"rc-slider": "^10.6.2",
"react": "^18",
@@ -6046,6 +6046,16 @@
"cookie": "^0.6.0"
}
},
+ "node_modules/core-js": {
+ "version": "3.38.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz",
+ "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==",
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
"node_modules/cosmiconfig": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
@@ -11998,13 +12008,14 @@
}
},
"node_modules/posthog-js": {
- "version": "1.160.0",
- "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.160.0.tgz",
- "integrity": "sha512-K/RRgmPYIpP69nnveCJfkclb8VU+R+jsgqlrKaLGsM5CtQM9g01WOzAiT3u36WLswi58JiFMXgJtECKQuoqTgQ==",
+ "version": "1.175.0",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.175.0.tgz",
+ "integrity": "sha512-ZPdM7/azh90gqqlYSJ3RIBJVViER8JDR7LtHWatVt2UCsIRvV+FSRQd85n770TFLDQSKAekFVcwCC9t+syTJJA==",
"dependencies": {
+ "core-js": "^3.38.1",
"fflate": "^0.4.8",
"preact": "^10.19.3",
- "web-vitals": "^4.0.1"
+ "web-vitals": "^4.2.0"
}
},
"node_modules/preact": {
diff --git a/front_end/package.json b/front_end/package.json
index 042418163..be48c8d1e 100644
--- a/front_end/package.json
+++ b/front_end/package.json
@@ -53,7 +53,7 @@
"next-themes": "^0.3.0",
"nextjs-toploader": "^1.6.12",
"percent-round": "^2.3.1",
- "posthog-js": "^1.160.0",
+ "posthog-js": "^1.175.0",
"puppeteer": "^22.12.1",
"rc-slider": "^10.6.2",
"react": "^18",
diff --git a/front_end/src/app/(main)/(tournaments)/tournament/components/tournament_feed.tsx b/front_end/src/app/(main)/(tournaments)/tournament/components/tournament_feed.tsx
index 8be21c506..5191764b9 100644
--- a/front_end/src/app/(main)/(tournaments)/tournament/components/tournament_feed.tsx
+++ b/front_end/src/app/(main)/(tournaments)/tournament/components/tournament_feed.tsx
@@ -1,5 +1,6 @@
"use client";
+import { sendGAEvent } from "@next/third-parties/google";
import { useSearchParams } from "next/navigation";
import { FC, useEffect, useState } from "react";
@@ -39,6 +40,7 @@ const TournamentFeed: FC = ({ slug }) => {
setIsLoading(true);
setError(undefined);
try {
+ sendGAEvent("event", "feedSearch", { value: pageFilters });
const { questions } = (await fetchPosts(
pageFilters,
0,
diff --git a/front_end/src/app/(main)/components/cookies_banner/index.tsx b/front_end/src/app/(main)/components/cookies_banner/index.tsx
index 6e3d2df65..8247ccb8c 100644
--- a/front_end/src/app/(main)/components/cookies_banner/index.tsx
+++ b/front_end/src/app/(main)/components/cookies_banner/index.tsx
@@ -1,7 +1,8 @@
"use client";
+
import Link from "next/link";
import { useTranslations } from "next-intl";
-// import posthog from "posthog-js";
+import posthog from "posthog-js";
import { FC, useEffect, useState } from "react";
import Button from "@/components/ui/button";
@@ -15,26 +16,31 @@ const STORAGE_KEY = "analytic_cookie_consent";
const CookiesBanner: FC = () => {
const t = useTranslations();
- // const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isModalOpen, setIsModalOpen] = useState(false);
const [consentGiven, setConsentGiven] = useState(null);
const [analyticsCheckboxValue, setAnalyticsCheckboxValue] =
useState(true);
- // const openModal = () => setIsModalOpen(true);
- // const closeModal = () => setIsModalOpen(false);
+ const openModal = () => setIsModalOpen(true);
+ const closeModal = () => setIsModalOpen(false);
useEffect(() => {
setConsentGiven(getAnalyticsCookieConsentGiven());
}, []);
- // TODO: uncomment once we want to support cookies configuration
- // useEffect(() => {
- // if (consentGiven !== null) {
- // posthog.set_config({
- // persistence: consentGiven === "yes" ? "localStorage+cookie" : "memory",
- // });
- // }
- // }, [consentGiven]);
+ useEffect(() => {
+ if (consentGiven !== null) {
+ posthog.set_config({
+ persistence: consentGiven === "yes" ? "localStorage+cookie" : "memory",
+ });
+
+ if (typeof window !== "undefined" && window.gtag) {
+ window.gtag("consent", "update", {
+ analytics_storage: consentGiven === "yes" ? "granted" : "denied",
+ });
+ }
+ }
+ }, [consentGiven]);
const submitBanner = () => {
const consentValue = analyticsCheckboxValue ? "yes" : "no";
@@ -67,21 +73,20 @@ const CookiesBanner: FC = () => {
- {/*TODO: uncomment once we want to support cookies configuration*/}
- {/**/}
+
- {/**/}
+
);
};
diff --git a/front_end/src/app/(main)/questions/components/question_topics.tsx b/front_end/src/app/(main)/questions/components/question_topics.tsx
index d7220ed81..35461da6b 100644
--- a/front_end/src/app/(main)/questions/components/question_topics.tsx
+++ b/front_end/src/app/(main)/questions/components/question_topics.tsx
@@ -6,6 +6,7 @@ import {
faHome,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { sendGAEvent } from "@next/third-parties/google";
import classNames from "classnames";
import { useTranslations } from "next-intl";
import { FC, useMemo, useState } from "react";
@@ -112,13 +113,23 @@ const QuestionTopics: FC = ({ topics }) => {
switchFeed(FeedType.MY_PREDICTIONS)}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: t("myPredictions"),
+ });
+ switchFeed(FeedType.MY_PREDICTIONS);
+ }}
isActive={currentFeed === FeedType.MY_PREDICTIONS}
/>
switchFeed(FeedType.MY_QUESTIONS_AND_POSTS)}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: t("myQuestionsAndPosts"),
+ });
+ switchFeed(FeedType.MY_QUESTIONS_AND_POSTS);
+ }}
isActive={currentFeed === FeedType.MY_QUESTIONS_AND_POSTS}
/>
>
@@ -128,12 +139,22 @@ const QuestionTopics: FC = ({ topics }) => {
emoji="π€π"
text="AI Benchmarking"
href="/aib"
+ onClick={() =>
+ sendGAEvent("event", "sidebarClick", {
+ value: "AI Benchmarking",
+ })
+ }
/>
+ sendGAEvent("event", "sidebarClick", {
+ value: "2024 US Election Hub",
+ })
+ }
/>
{!!hotTopics.length && (
<>
@@ -144,7 +165,12 @@ const QuestionTopics: FC = ({ topics }) => {
isActive={selectedTopic === topic.slug}
emoji={topic.emoji}
text={topic.name}
- onClick={() => selectTopic(topic)}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: topic.name,
+ });
+ selectTopic(topic);
+ }}
/>
))}
>
@@ -159,7 +185,12 @@ const QuestionTopics: FC = ({ topics }) => {
isActive={selectedTopic === category.slug}
emoji={category.emoji}
text={category.name}
- onClick={() => selectTopic(category)}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: category.name,
+ });
+ selectTopic(category);
+ }}
/>
))}
>
@@ -170,13 +201,23 @@ const QuestionTopics: FC = ({ topics }) => {
text={t("seeAllCategories")}
emoji={}
isActive={false}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: t("seeAllCategories"),
+ });
+ }}
/>
{user && (
}
- onClick={() => switchFeed(FeedType.IN_REVIEW)}
+ onClick={() => {
+ sendGAEvent("event", "sidebarClick", {
+ value: t("inReview"),
+ });
+ switchFeed(FeedType.IN_REVIEW);
+ }}
isActive={currentFeed === FeedType.IN_REVIEW}
/>
)}
diff --git a/front_end/src/app/layout.tsx b/front_end/src/app/layout.tsx
index 02bdbf332..ab9c6f9cc 100644
--- a/front_end/src/app/layout.tsx
+++ b/front_end/src/app/layout.tsx
@@ -3,6 +3,7 @@ import "@fortawesome/fontawesome-svg-core/styles.css";
import { GoogleAnalytics } from "@next/third-parties/google";
import type { Metadata } from "next";
import "./globals.css";
+import dynamic from "next/dynamic";
import localFont from "next/font/local";
import { NextIntlClientProvider } from "next-intl";
import { getLocale, getMessages } from "next-intl/server";
@@ -19,6 +20,13 @@ import ProfileApi from "@/services/profile";
import { CSPostHogProvider } from "./providers";
+const PostHogPageView = dynamic(
+ () => import("@/components/posthog_page_view"),
+ {
+ ssr: false,
+ }
+);
+
config.autoAddCss = false;
const sourceSerifPro = localFont({
@@ -128,6 +136,7 @@ export default async function RootLayout({
>
+
diff --git a/front_end/src/app/providers.tsx b/front_end/src/app/providers.tsx
index 6e01263dc..6d973b11f 100644
--- a/front_end/src/app/providers.tsx
+++ b/front_end/src/app/providers.tsx
@@ -1,21 +1,27 @@
"use client";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
+import { useEffect } from "react";
import { getAnalyticsCookieConsentGiven } from "@/app/(main)/components/cookies_banner";
export function CSPostHogProvider({ children }: { children: any }) {
- if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
- posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
- api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
- // set to 'always' to create profiles for anonymous users as well
- person_profiles: "identified_only",
- // TODO: uncomment once we want to support cookies configuration
- // persistence:
- // getAnalyticsCookieConsentGiven() === "yes"
- // ? "localStorage+cookie"
- // : "memory",
- });
- }
+ useEffect(() => {
+ if (process.env.NEXT_PUBLIC_POSTHOG_KEY) {
+ posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
+ api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
+ // set to 'always' to create profiles for anonymous users as well
+ person_profiles: "identified_only",
+ // Disable automatic pageview capture, as we capture manually
+ capture_pageview: false,
+ disable_session_recording: true,
+ persistence:
+ getAnalyticsCookieConsentGiven() === "yes"
+ ? "localStorage+cookie"
+ : "memory",
+ });
+ }
+ }, []);
+
return {children};
}
diff --git a/front_end/src/components/posthog_page_view.tsx b/front_end/src/components/posthog_page_view.tsx
new file mode 100644
index 000000000..6f39f134a
--- /dev/null
+++ b/front_end/src/components/posthog_page_view.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import { usePathname, useSearchParams } from "next/navigation";
+import { usePostHog } from "posthog-js/react";
+import { useEffect } from "react";
+
+export default function PostHogPageView() {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+ const posthog = usePostHog();
+ useEffect(() => {
+ if (pathname && posthog) {
+ let url = window.origin + pathname;
+ if (searchParams.toString()) {
+ url = url + `?${searchParams.toString()}`;
+ }
+ posthog.capture("$pageview", {
+ $current_url: url,
+ });
+ }
+ }, [pathname, searchParams, posthog]);
+
+ return null;
+}
diff --git a/front_end/src/components/posts_feed/paginated_feed.tsx b/front_end/src/components/posts_feed/paginated_feed.tsx
index d351303d2..5c6c26190 100644
--- a/front_end/src/components/posts_feed/paginated_feed.tsx
+++ b/front_end/src/components/posts_feed/paginated_feed.tsx
@@ -1,7 +1,7 @@
"use client";
-import * as Sentry from "@sentry/nextjs";
+import { sendGAEvent } from "@next/third-parties/google";
import { useTranslations } from "next-intl";
-import { FC, Fragment, useState } from "react";
+import { FC, Fragment, useEffect, useState } from "react";
import { fetchMorePosts } from "@/app/(main)/questions/actions";
import NewsCard from "@/components/news_card";
@@ -42,11 +42,16 @@ const PaginatedPostsFeed: FC = ({
(Error & { digest?: string }) | undefined
>();
+ useEffect(() => {
+ // capture search event from AwaitedPostsFeed
+ sendGAEvent("event", "feedSearch", { value: filters });
+ }, [filters]);
const loadMorePosts = async () => {
if (hasMoreData) {
setIsLoading(true);
setError(undefined);
try {
+ sendGAEvent("event", "feedSearch", { value: filters });
const { newPosts, hasNextPage } = await fetchMorePosts(
filters,
offset,