📈 Conditionally initialize posthog (#586)

This commit is contained in:
Luke Vella 2023-03-20 20:04:39 +00:00 committed by GitHub
parent 4062b572a0
commit d9061d45fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 79 additions and 53 deletions

View file

@ -46,7 +46,7 @@
"next-i18next": "^13.0.3", "next-i18next": "^13.0.3",
"next-seo": "^5.15.0", "next-seo": "^5.15.0",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"posthog-js": "^1.42.3", "posthog-js": "^1.51.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-big-calendar": "^1.5.0", "react-big-calendar": "^1.5.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View file

@ -1,12 +1,12 @@
import { AnimatePresence, m } from "framer-motion"; import { AnimatePresence, m } from "framer-motion";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import posthog from "posthog-js";
import React from "react"; import React from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Button } from "@/components/button"; import { Button } from "@/components/button";
import Share from "@/components/icons/share.svg"; import Share from "@/components/icons/share.svg";
import { usePostHog } from "@/utils/posthog";
import { useParticipants } from "./participants-provider"; import { useParticipants } from "./participants-provider";
import ManagePoll from "./poll/manage-poll"; import ManagePoll from "./poll/manage-poll";
@ -22,6 +22,7 @@ export const AdminControls = (props: { children?: React.ReactNode }) => {
const router = useRouter(); const router = useRouter();
const { mutate: updatePollMutation } = useUpdatePollMutation(); const { mutate: updatePollMutation } = useUpdatePollMutation();
const posthog = usePostHog();
React.useEffect(() => { React.useEffect(() => {
if (router.query.unsubscribe) { if (router.query.unsubscribe) {
@ -30,7 +31,7 @@ export const AdminControls = (props: { children?: React.ReactNode }) => {
{ {
onSuccess: () => { onSuccess: () => {
toast.success(t("notificationsDisabled")); toast.success(t("notificationsDisabled"));
posthog.capture("unsubscribed from notifications"); posthog?.capture("unsubscribed from notifications");
}, },
}, },
); );
@ -38,7 +39,7 @@ export const AdminControls = (props: { children?: React.ReactNode }) => {
shallow: true, shallow: true,
}); });
} }
}, [urlId, router, updatePollMutation, t]); }, [urlId, router, updatePollMutation, t, posthog]);
const { participants } = useParticipants(); const { participants } = useParticipants();

View file

@ -1,9 +1,10 @@
import Link from "next/link"; import Link from "next/link";
import { Trans, useTranslation } from "next-i18next"; import { Trans, useTranslation } from "next-i18next";
import posthog from "posthog-js";
import React from "react"; import React from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { usePostHog } from "@/utils/posthog";
import { requiredString, validEmail } from "../../utils/form-validation"; import { requiredString, validEmail } from "../../utils/form-validation";
import { trpc } from "../../utils/trpc"; import { trpc } from "../../utils/trpc";
import { Button } from "../button"; import { Button } from "../button";
@ -139,7 +140,7 @@ export const RegisterForm: React.FunctionComponent<{
const authenticateRegistration = const authenticateRegistration =
trpc.auth.authenticateRegistration.useMutation(); trpc.auth.authenticateRegistration.useMutation();
const [token, setToken] = React.useState<string>(); const [token, setToken] = React.useState<string>();
const posthog = usePostHog();
if (token) { if (token) {
return ( return (
<VerifyCode <VerifyCode
@ -154,11 +155,11 @@ export const RegisterForm: React.FunctionComponent<{
} }
onRegistered(); onRegistered();
posthog.identify(res.user.id, { posthog?.identify(res.user.id, {
email: res.user.email, email: res.user.email,
name: res.user.name, name: res.user.name,
}); });
posthog.capture("register"); posthog?.capture("register");
}} }}
onResend={async () => { onResend={async () => {
const values = getValues(); const values = getValues();
@ -285,7 +286,7 @@ export const LoginForm: React.FunctionComponent<{
const authenticateLogin = trpc.auth.authenticateLogin.useMutation(); const authenticateLogin = trpc.auth.authenticateLogin.useMutation();
const [token, setToken] = React.useState<string>(); const [token, setToken] = React.useState<string>();
const posthog = usePostHog();
if (token) { if (token) {
return ( return (
<VerifyCode <VerifyCode
@ -299,11 +300,11 @@ export const LoginForm: React.FunctionComponent<{
throw new Error("Failed to authenticate user"); throw new Error("Failed to authenticate user");
} else { } else {
onAuthenticated(); onAuthenticated();
posthog.identify(res.user.id, { posthog?.identify(res.user.id, {
email: res.user.email, email: res.user.email,
name: res.user.name, name: res.user.name,
}); });
posthog.capture("login"); posthog?.capture("login");
} }
}} }}
onResend={async () => { onResend={async () => {

View file

@ -1,9 +1,10 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import posthog from "posthog-js";
import React from "react"; import React from "react";
import { useSessionStorage } from "react-use"; import { useSessionStorage } from "react-use";
import { usePostHog } from "@/utils/posthog";
import { encodeDateOption } from "../utils/date-time-utils"; import { encodeDateOption } from "../utils/date-time-utils";
import { trpc } from "../utils/trpc"; import { trpc } from "../utils/trpc";
import { Button } from "./button"; import { Button } from "./button";
@ -89,10 +90,12 @@ const Page: React.FunctionComponent<CreatePollPageProps> = ({
const [isRedirecting, setIsRedirecting] = React.useState(false); const [isRedirecting, setIsRedirecting] = React.useState(false);
const posthog = usePostHog();
const createPoll = trpc.polls.create.useMutation({ const createPoll = trpc.polls.create.useMutation({
onSuccess: (res) => { onSuccess: (res) => {
setIsRedirecting(true); setIsRedirecting(true);
posthog.capture("created poll", { posthog?.capture("created poll", {
pollId: res.id, pollId: res.id,
numberOfOptions: formData.options?.options?.length, numberOfOptions: formData.options?.options?.length,
optionsView: formData?.options?.view, optionsView: formData?.options?.view,

View file

@ -1,9 +1,10 @@
import clsx from "clsx"; import clsx from "clsx";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import posthog from "posthog-js";
import * as React from "react"; import * as React from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { usePostHog } from "@/utils/posthog";
import { useDayjs } from "../../utils/dayjs"; import { useDayjs } from "../../utils/dayjs";
import { requiredString } from "../../utils/form-validation"; import { requiredString } from "../../utils/form-validation";
import { trpc } from "../../utils/trpc"; import { trpc } from "../../utils/trpc";
@ -38,10 +39,11 @@ const Discussion: React.FunctionComponent = () => {
trpc: {}, trpc: {},
}, },
); );
const posthog = usePostHog();
const addComment = trpc.polls.comments.add.useMutation({ const addComment = trpc.polls.comments.add.useMutation({
onSuccess: (newComment) => { onSuccess: (newComment) => {
posthog.capture("created comment"); posthog?.capture("created comment");
queryClient.polls.comments.list.setData( queryClient.polls.comments.list.setData(
{ pollId }, { pollId },
@ -62,7 +64,7 @@ const Discussion: React.FunctionComponent = () => {
); );
}, },
onSuccess: () => { onSuccess: () => {
posthog.capture("deleted comment"); posthog?.capture("deleted comment");
}, },
}); });

View file

@ -15,6 +15,7 @@ import { useModalState } from "@/components/modal/use-modal";
import { useDeleteParticipantModal } from "@/components/poll/use-delete-participant-modal"; import { useDeleteParticipantModal } from "@/components/poll/use-delete-participant-modal";
import { TextInput } from "@/components/text-input"; import { TextInput } from "@/components/text-input";
import { useFormValidation } from "@/utils/form-validation"; import { useFormValidation } from "@/utils/form-validation";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "@/utils/trpc"; import { trpc } from "@/utils/trpc";
import { Participant } from ".prisma/client"; import { Participant } from ".prisma/client";
@ -92,9 +93,10 @@ const ChangeNameModal = (props: {
participantId: string; participantId: string;
onDone: () => void; onDone: () => void;
}) => { }) => {
const posthog = usePostHog();
const changeName = trpc.polls.participants.rename.useMutation({ const changeName = trpc.polls.participants.rename.useMutation({
onSuccess: (_, { participantId, newName }) => { onSuccess: (_, { participantId, newName }) => {
posthog.capture("changed name", { posthog?.capture("changed name", {
participantId, participantId,
oldName: props.oldName, oldName: props.oldName,
newName, newName,

View file

@ -1,11 +1,11 @@
import clsx from "clsx"; import clsx from "clsx";
import { Trans, useTranslation } from "next-i18next"; import { Trans, useTranslation } from "next-i18next";
import posthog from "posthog-js";
import * as React from "react"; import * as React from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { Button } from "@/components/button"; import { Button } from "@/components/button";
import Exclamation from "@/components/icons/exclamation.svg"; import Exclamation from "@/components/icons/exclamation.svg";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "../../../utils/trpc"; import { trpc } from "../../../utils/trpc";
@ -22,9 +22,10 @@ export const DeletePollForm: React.FunctionComponent<{
const confirmationText = watch("confirmation"); const confirmationText = watch("confirmation");
const canDelete = confirmationText === confirmText; const canDelete = confirmationText === confirmText;
const posthog = usePostHog();
const deletePoll = trpc.polls.delete.useMutation({ const deletePoll = trpc.polls.delete.useMutation({
onSuccess: () => { onSuccess: () => {
posthog.capture("deleted poll"); posthog?.capture("deleted poll");
}, },
}); });

View file

@ -1,4 +1,4 @@
import posthog from "posthog-js"; import { usePostHog } from "@/utils/posthog";
import { trpc } from "../../utils/trpc"; import { trpc } from "../../utils/trpc";
import { ParticipantForm } from "./types"; import { ParticipantForm } from "./types";
@ -14,9 +14,10 @@ export const normalizeVotes = (
}; };
export const useAddParticipantMutation = () => { export const useAddParticipantMutation = () => {
const posthog = usePostHog();
return trpc.polls.participants.add.useMutation({ return trpc.polls.participants.add.useMutation({
onSuccess: (_, { pollId, name, email }) => { onSuccess: (_, { pollId, name, email }) => {
posthog.capture("add participant", { posthog?.capture("add participant", {
pollId, pollId,
name, name,
email, email,
@ -27,9 +28,10 @@ export const useAddParticipantMutation = () => {
export const useUpdateParticipantMutation = () => { export const useUpdateParticipantMutation = () => {
const queryClient = trpc.useContext(); const queryClient = trpc.useContext();
const posthog = usePostHog();
return trpc.polls.participants.update.useMutation({ return trpc.polls.participants.update.useMutation({
onSuccess: (participant) => { onSuccess: (participant) => {
posthog.capture("update participant", { posthog?.capture("update participant", {
name: participant.name, name: participant.name,
}); });
queryClient.polls.participants.list.setData( queryClient.polls.participants.list.setData(
@ -54,6 +56,7 @@ export const useUpdateParticipantMutation = () => {
export const useDeleteParticipantMutation = () => { export const useDeleteParticipantMutation = () => {
const queryClient = trpc.useContext(); const queryClient = trpc.useContext();
const posthog = usePostHog();
return trpc.polls.participants.delete.useMutation({ return trpc.polls.participants.delete.useMutation({
onMutate: ({ participantId, pollId }) => { onMutate: ({ participantId, pollId }) => {
queryClient.polls.participants.list.setData( queryClient.polls.participants.list.setData(
@ -64,7 +67,7 @@ export const useDeleteParticipantMutation = () => {
); );
}, },
onSuccess: (_, { pollId, participantId }) => { onSuccess: (_, { pollId, participantId }) => {
posthog.capture("remove participant", { posthog?.capture("remove participant", {
pollId, pollId,
participantId, participantId,
}); });
@ -74,10 +77,11 @@ export const useDeleteParticipantMutation = () => {
export const useUpdatePollMutation = () => { export const useUpdatePollMutation = () => {
const queryClient = trpc.useContext(); const queryClient = trpc.useContext();
const posthog = usePostHog();
return trpc.polls.update.useMutation({ return trpc.polls.update.useMutation({
onSuccess: (data) => { onSuccess: (data) => {
queryClient.polls.invalidate(); queryClient.polls.invalidate();
posthog.capture("updated poll", { posthog?.capture("updated poll", {
id: data.id, id: data.id,
}); });
}, },

View file

@ -1,9 +1,8 @@
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import posthog from "posthog-js";
import React from "react"; import React from "react";
import { useMount } from "react-use";
import { UserSession } from "@/utils/auth"; import { UserSession } from "@/utils/auth";
import { usePostHog } from "@/utils/posthog";
import { trpc } from "../utils/trpc"; import { trpc } from "../utils/trpc";
import { useRequiredContext } from "./use-required-context"; import { useRequiredContext } from "./use-required-context";
@ -66,28 +65,18 @@ export const UserProvider = (props: {
}, },
}); });
useMount(() => { const posthog = usePostHog();
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY ?? "fake token", {
api_host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST, React.useEffect(() => {
opt_out_capturing_by_default: false, if (user && posthog?.__loaded && posthog?.get_distinct_id() !== user.id) {
capture_pageview: false, posthog?.identify(
capture_pageleave: false, user.id,
autocapture: false, !user.isGuest
loaded: (posthog) => { ? { email: user.email, name: user.name }
if (!process.env.NEXT_PUBLIC_POSTHOG_API_KEY) { : { name: user.id },
posthog.opt_out_capturing(); );
} }
if (user && posthog.get_distinct_id() !== user.id) { }, [posthog, user]);
posthog.identify(
user.id,
!user.isGuest
? { email: user.email, name: user.name }
: { name: user.id },
);
}
},
});
});
const shortName = user const shortName = user
? user.isGuest === false ? user.isGuest === false

View file

@ -9,6 +9,8 @@ import { Inter } from "next/font/google";
import Head from "next/head"; import Head from "next/head";
import { appWithTranslation } from "next-i18next"; import { appWithTranslation } from "next-i18next";
import { DefaultSeo } from "next-seo"; import { DefaultSeo } from "next-seo";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import React from "react"; import React from "react";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
@ -33,6 +35,21 @@ type AppPropsWithLayout = AppProps<PageProps> & {
Component: NextPageWithLayout<PageProps>; Component: NextPageWithLayout<PageProps>;
}; };
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
opt_out_capturing_by_default: false,
capture_pageview: false,
capture_pageleave: false,
autocapture: false,
loaded: (posthog) => {
if (!process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
posthog.opt_out_capturing();
}
},
});
}
const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => { const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => {
useCrispChat(); useCrispChat();
@ -50,7 +67,7 @@ const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => {
const getLayout = Component.getLayout ?? ((page) => page); const getLayout = Component.getLayout ?? ((page) => page);
return ( return (
<> <PostHogProvider client={posthog}>
<DefaultSeo <DefaultSeo
openGraph={{ openGraph={{
siteName: "Rallly", siteName: "Rallly",
@ -83,7 +100,7 @@ const MyApp: NextPage<AppPropsWithLayout> = ({ Component, pageProps }) => {
} }
`}</style> `}</style>
{getLayout(<Component {...pageProps} />)} {getLayout(<Component {...pageProps} />)}
</> </PostHogProvider>
); );
}; };

View file

@ -1,10 +1,11 @@
import { NextPage } from "next"; import { NextPage } from "next";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import posthog from "posthog-js";
import React from "react"; import React from "react";
import { useMount } from "react-use"; import { useMount } from "react-use";
import { usePostHog } from "@/utils/posthog";
import FullPageLoader from "../components/full-page-loader"; import FullPageLoader from "../components/full-page-loader";
import { withSession } from "../components/user-provider"; import { withSession } from "../components/user-provider";
import { withAuthIfRequired, withSessionSsr } from "../utils/auth"; import { withAuthIfRequired, withSessionSsr } from "../utils/auth";
@ -16,10 +17,10 @@ const Demo: NextPage = () => {
const router = useRouter(); const router = useRouter();
const createDemo = trpc.polls.demo.create.useMutation(); const createDemo = trpc.polls.demo.create.useMutation();
const posthog = usePostHog();
useMount(async () => { useMount(async () => {
const urlId = await createDemo.mutateAsync(); const urlId = await createDemo.mutateAsync();
posthog.capture("create demo poll"); posthog?.capture("create demo poll");
router.replace(`/admin/${urlId}`); router.replace(`/admin/${urlId}`);
}); });

View file

@ -0,0 +1,5 @@
import { usePostHog as usePostHogHook } from "posthog-js/react";
// Seems silly but annoyingly typescript tries to import usePostHog from
// posthog-js/react/dist/types which doesn't even work.
export const usePostHog = usePostHogHook;