From 7ecefd707083f5382cafd77cb516695ab5dd72da Mon Sep 17 00:00:00 2001 From: Sam Garson Date: Sun, 14 Apr 2024 21:34:49 +0100 Subject: [PATCH] [WEB] Track route as well as path --- web/src/auth.ts | 2 +- web/src/components/utils/fathom.tsx | 7 +++-- web/src/hooks/use-route.ts | 47 +++++++++++++++++++++++++++++ web/src/lib/tracking/events.ts | 4 +-- 4 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 web/src/hooks/use-route.ts diff --git a/web/src/auth.ts b/web/src/auth.ts index bc7dd1fa..84ee0f51 100644 --- a/web/src/auth.ts +++ b/web/src/auth.ts @@ -60,7 +60,7 @@ export const { async signIn({ user, isNewUser, account }) { await Promise.all([ identify(user), - track('Signed In', { + track('Signed in', { 'First Time': !!isNewUser, userId: user.id, Provider: account?.provider diff --git a/web/src/components/utils/fathom.tsx b/web/src/components/utils/fathom.tsx index 3a519359..7f71851c 100644 --- a/web/src/components/utils/fathom.tsx +++ b/web/src/components/utils/fathom.tsx @@ -3,6 +3,7 @@ import { load, trackPageview } from 'fathom-client' import { usePathname, useSearchParams } from 'next/navigation' import { Suspense, useEffect } from 'react' +import { useRoute } from 'src/hooks/use-route' import { track } from 'src/lib/tracking/track' const siteId = @@ -38,14 +39,16 @@ function TrackPageView() { function Track() { const pathname = usePathname() const search = Object.fromEntries(useSearchParams().entries()) + const route = useRoute() useEffect(() => { if (!pathname) return track('Viewed a page', { Path: pathname, - Search: Object.keys(search).length > 0 ? search : undefined + Search: Object.keys(search).length > 0 ? search : undefined, + Route: route }) - }, [pathname, search]) + }, [pathname, search, route]) return null } diff --git a/web/src/hooks/use-route.ts b/web/src/hooks/use-route.ts new file mode 100644 index 00000000..c6fd1a7a --- /dev/null +++ b/web/src/hooks/use-route.ts @@ -0,0 +1,47 @@ +import { useParams, usePathname, useSearchParams } from 'next/navigation' + +// https://github.com/vercel/speed-insights/blob/main/packages/web/src/nextjs/utils.ts +export const useRoute = (): string | null => { + const params = useParams() + const searchParams = useSearchParams() + const path = usePathname() + + const finalParams = { + ...Object.fromEntries(searchParams.entries()), + ...(params || {}) + } + + return params ? computeRoute(path, finalParams) : path +} + +function computeRoute( + pathname: string | null, + pathParams: Record | null +): string | null { + if (!pathname || !pathParams) { + return pathname + } + + let result = pathname + + try { + for (const [key, valueOrArray] of Object.entries(pathParams)) { + const isValueArray = Array.isArray(valueOrArray) + const value = isValueArray ? valueOrArray.join('/') : valueOrArray + const expr = isValueArray ? `...${key}` : key + + const matcher = new RegExp(`/${escapeRegExp(value)}(?=[/?#]|$)`) + if (matcher.test(result)) { + result = result.replace(matcher, `/[${expr}]`) + } + } + + return result + } catch (e) { + return pathname + } +} + +function escapeRegExp(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} diff --git a/web/src/lib/tracking/events.ts b/web/src/lib/tracking/events.ts index 41cd3a71..83539e25 100644 --- a/web/src/lib/tracking/events.ts +++ b/web/src/lib/tracking/events.ts @@ -1,10 +1,10 @@ export type TrackableEvents = { 'Opened a modal': { Extra?: Record; Modal: string } - 'Signed In': { Provider?: string; 'First Time': boolean } + 'Signed in': { Provider?: string; 'First Time': boolean } 'Pressed a button': { Button: string; Extra?: Record } 'Viewed a page': { Path: string - Ref?: string | null + Route: string Search?: Record } }