From f13c7299238e8a3102ebf49d45a41e7f93e1bf72 Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Wed, 3 May 2023 15:42:56 +0200 Subject: [PATCH 01/13] chore: add analytics event to hp herop --- apps/www/components/Hero.tsx | 23 +++++++++++-- apps/www/hooks/useGoogleAnalyticsProps.tsx | 13 ++++++++ apps/www/lib/constants.ts | 2 ++ apps/www/lib/fetchWrapper.ts | 4 +-- apps/www/lib/gaEvents.ts | 20 ++++++++++++ apps/www/lib/telemetry.ts | 38 ++++++++++++++++++++++ apps/www/pages/_app.tsx | 2 +- 7 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 apps/www/hooks/useGoogleAnalyticsProps.tsx create mode 100644 apps/www/lib/gaEvents.ts create mode 100644 apps/www/lib/telemetry.ts diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index e97cd31fa74..5557da55251 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -1,10 +1,18 @@ -import { Button, IconBookOpen, Space } from 'ui' +import { Button, IconBookOpen } from 'ui' import Link from 'next/link' import { useRouter } from 'next/router' import SectionContainer from './Layouts/SectionContainer' +import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry' +import gaEvents from '~/lib/gaEvents' +import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' const Hero = () => { const { basePath } = useRouter() + const googleAnalyticsProps = useGoogleAnalyticsProps() + + const handleClick = (event: GoogleAnalyticsEvent) => { + Telemetry.sendEvent(event, googleAnalyticsProps) + } return (
@@ -41,14 +49,23 @@ const Hero = () => {
- - diff --git a/apps/www/hooks/useGoogleAnalyticsProps.tsx b/apps/www/hooks/useGoogleAnalyticsProps.tsx new file mode 100644 index 00000000000..a3254a8b307 --- /dev/null +++ b/apps/www/hooks/useGoogleAnalyticsProps.tsx @@ -0,0 +1,13 @@ +import { useRouter } from 'next/router' + +const useGoogleAnalyticsProps = () => { + const { locale } = useRouter() + + return { + screenResolution: + typeof window !== 'undefined' ? `${window.innerWidth}x${window.innerHeight}` : undefined, + language: locale ?? 'en-US', + } +} + +export default useGoogleAnalyticsProps diff --git a/apps/www/lib/constants.ts b/apps/www/lib/constants.ts index e3c815b5dca..1b7537a438d 100644 --- a/apps/www/lib/constants.ts +++ b/apps/www/lib/constants.ts @@ -1,5 +1,7 @@ export const APP_NAME = 'Supabase' export const DESCRIPTION = 'The Open Source Alternative to Firebase.' +export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' +export const API_URL = !IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api' // Launch Weeek export const SAMPLE_TICKET_NUMBER = 1234 diff --git a/apps/www/lib/fetchWrapper.ts b/apps/www/lib/fetchWrapper.ts index c573a0d37dc..0313e49565f 100644 --- a/apps/www/lib/fetchWrapper.ts +++ b/apps/www/lib/fetchWrapper.ts @@ -1,7 +1,5 @@ interface DataProps { - referrer?: string - title: string - route?: string + [prop: string]: any } export const post = (url: string, data: DataProps, options = {}) => { diff --git a/apps/www/lib/gaEvents.ts b/apps/www/lib/gaEvents.ts new file mode 100644 index 00000000000..53908751474 --- /dev/null +++ b/apps/www/lib/gaEvents.ts @@ -0,0 +1,20 @@ +import { GoogleAnalyticsEvent } from './telemetry' + +interface GoogleAnalyticsEventType { + [key: string]: GoogleAnalyticsEvent +} + +const GoogleAnalyticsEvents: GoogleAnalyticsEventType = { + www_hp_hero_startProject: { + action: 'www_hp_hero_startProject', + category: 'link', + label: 'Clicks on “Start your project” button in the home page hero', + }, + www_hp_hero_documentation: { + action: 'www_hp_hero_documentation', + category: 'link', + label: 'Clicks on “Documentation” button in the home page hero', + }, +} + +export default GoogleAnalyticsEvents diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts new file mode 100644 index 00000000000..cdaa08cf1e8 --- /dev/null +++ b/apps/www/lib/telemetry.ts @@ -0,0 +1,38 @@ +import { post } from '~/lib/fetchWrapper' +import { API_URL, IS_PROD } from 'lib/constants' + +export interface GoogleAnalyticsEvent { + category: string + action: string + label: string + value?: string +} + +export interface GoogleAnalyticsProps { + screenResolution?: string + language: string +} + +// This event is the same as in studio/lib/telemetry.tx +// but uses different ENV variables for www + +const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) => { + if (!IS_PROD) return + + const { category, action, label, value } = event + + return post(`${API_URL}/telemetry/event`, { + action: action, + category: category, + label: label, + value: value, + ga: { + screen_resolution: gaProps?.screenResolution, + language: gaProps?.language, + }, + }) +} + +export default { + sendEvent, +} diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index 2ccb053b3ae..60a1a4684fe 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -5,7 +5,7 @@ import { useRouter } from 'next/router' import { useEffect } from 'react' import Meta from '~/components/Favicons' import '../styles/index.css' -import { post } from './../lib/fetchWrapper' +import { post } from '~/lib/fetchWrapper' import { AuthProvider, ThemeProvider } from 'common' import Head from 'next/head' From da33e3bea6a2507da6f995d5ecdfd47ba678f72f Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Wed, 3 May 2023 16:09:32 +0200 Subject: [PATCH 02/13] chore: navigate with router --- apps/www/components/Hero.tsx | 58 ++++++++++++++++++------------------ apps/www/lib/constants.ts | 2 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index 5557da55251..53df050350e 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -7,11 +7,12 @@ import gaEvents from '~/lib/gaEvents' import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' const Hero = () => { - const { basePath } = useRouter() + const router = useRouter() const googleAnalyticsProps = useGoogleAnalyticsProps() - const handleClick = (event: GoogleAnalyticsEvent) => { + const trackAndNavigate = (url: string, event: GoogleAnalyticsEvent) => { Telemetry.sendEvent(event, googleAnalyticsProps) + router.push(url) } return ( @@ -47,29 +48,28 @@ const Hero = () => {
- - - - - - - - - - + +
backed by @@ -77,22 +77,22 @@ const Hero = () => {
Y Combinator Mozilla Coatue Felicis
diff --git a/apps/www/lib/constants.ts b/apps/www/lib/constants.ts index 1b7537a438d..5586bb59f81 100644 --- a/apps/www/lib/constants.ts +++ b/apps/www/lib/constants.ts @@ -1,7 +1,7 @@ export const APP_NAME = 'Supabase' export const DESCRIPTION = 'The Open Source Alternative to Firebase.' export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' -export const API_URL = !IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api' +export const API_URL = IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api' // Launch Weeek export const SAMPLE_TICKET_NUMBER = 1234 From 5e2dca6cc07904482590eefb7e07e2ddcd3c8a9a Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Wed, 3 May 2023 16:19:51 +0200 Subject: [PATCH 03/13] chore: update API_URL preview check --- apps/www/components/Hero.tsx | 5 ++--- apps/www/lib/constants.ts | 3 ++- apps/www/lib/gaEvents.ts | 4 ++-- apps/www/lib/telemetry.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index 53df050350e..32f699d2939 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -1,5 +1,4 @@ import { Button, IconBookOpen } from 'ui' -import Link from 'next/link' import { useRouter } from 'next/router' import SectionContainer from './Layouts/SectionContainer' import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry' @@ -10,8 +9,8 @@ const Hero = () => { const router = useRouter() const googleAnalyticsProps = useGoogleAnalyticsProps() - const trackAndNavigate = (url: string, event: GoogleAnalyticsEvent) => { - Telemetry.sendEvent(event, googleAnalyticsProps) + const trackAndNavigate = async (url: string, event: GoogleAnalyticsEvent) => { + await Telemetry.sendEvent(event, googleAnalyticsProps) router.push(url) } diff --git a/apps/www/lib/constants.ts b/apps/www/lib/constants.ts index 5586bb59f81..445c0a6810a 100644 --- a/apps/www/lib/constants.ts +++ b/apps/www/lib/constants.ts @@ -1,7 +1,8 @@ export const APP_NAME = 'Supabase' export const DESCRIPTION = 'The Open Source Alternative to Firebase.' export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' -export const API_URL = IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api' +export const IS_PREVIEW = process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview' +export const API_URL = process.env.NEXT_PUBLIC_API_URL // Launch Weeek export const SAMPLE_TICKET_NUMBER = 1234 diff --git a/apps/www/lib/gaEvents.ts b/apps/www/lib/gaEvents.ts index 53908751474..167aa356a0c 100644 --- a/apps/www/lib/gaEvents.ts +++ b/apps/www/lib/gaEvents.ts @@ -8,12 +8,12 @@ const GoogleAnalyticsEvents: GoogleAnalyticsEventType = { www_hp_hero_startProject: { action: 'www_hp_hero_startProject', category: 'link', - label: 'Clicks on “Start your project” button in the home page hero', + label: '', }, www_hp_hero_documentation: { action: 'www_hp_hero_documentation', category: 'link', - label: 'Clicks on “Documentation” button in the home page hero', + label: '', }, } diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts index cdaa08cf1e8..0c6fb2ed9fe 100644 --- a/apps/www/lib/telemetry.ts +++ b/apps/www/lib/telemetry.ts @@ -1,5 +1,5 @@ import { post } from '~/lib/fetchWrapper' -import { API_URL, IS_PROD } from 'lib/constants' +import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants' export interface GoogleAnalyticsEvent { category: string @@ -17,7 +17,7 @@ export interface GoogleAnalyticsProps { // but uses different ENV variables for www const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) => { - if (!IS_PROD) return + if (!IS_PROD && !IS_PREVIEW) return const { category, action, label, value } = event From 6c33e30f4c91fb8a41ea123d25905314aacddacd Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 4 May 2023 12:42:11 +0200 Subject: [PATCH 04/13] chore: update telemetry API_URL on page load From 77ecbde3c7873f9f3b4732a54c564bfdda222298 Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 4 May 2023 12:59:16 +0200 Subject: [PATCH 05/13] chore: update telemetry API_URL on page load --- apps/www/pages/_app.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index 60a1a4684fe..dce4295802c 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -1,4 +1,4 @@ -import { APP_NAME, DESCRIPTION } from 'lib/constants' +import { API_URL, APP_NAME, DESCRIPTION } from 'lib/constants' import { DefaultSeo } from 'next-seo' import { AppProps } from 'next/app' import { useRouter } from 'next/router' @@ -13,7 +13,7 @@ export default function MyApp({ Component, pageProps }: AppProps) { const router = useRouter() function handlePageTelemetry(route: string) { - return post(`https://api.supabase.io/platform/telemetry/page`, { + return post(`${API_URL}/telemetry/page`, { referrer: document.referrer, title: document.title, route, From eaddf931eb820248938555f4bbd5ec43aea2e642 Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 4 May 2023 13:49:16 +0200 Subject: [PATCH 06/13] chore: update hardcoded API_URL in docs page telemetry --- apps/docs/lib/constants.ts | 1 + apps/docs/pages/_app.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/docs/lib/constants.ts b/apps/docs/lib/constants.ts index d1bbedbf47c..42c12c0d3f4 100644 --- a/apps/docs/lib/constants.ts +++ b/apps/docs/lib/constants.ts @@ -1,2 +1,3 @@ export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true' +export const API_URL = process.env.NEXT_PUBLIC_API_URL diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx index 0ac7df56c63..ed75637efaa 100644 --- a/apps/docs/pages/_app.tsx +++ b/apps/docs/pages/_app.tsx @@ -10,7 +10,7 @@ import { CommandMenuProvider } from 'ui' import components from '~/components' import Favicons from '~/components/Favicons' import SiteLayout from '~/layouts/SiteLayout' -import { IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants' +import { API_URL, IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants' import { post } from '~/lib/fetchWrappers' import '../styles/ch.scss' import '../styles/main.scss?v=1.0.0' @@ -25,7 +25,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ) function handlePageTelemetry(route: string) { - return post(`https://api.supabase.io/platform/telemetry/page`, { + return post(`${API_URL}/telemetry/page`, { referrer: document.referrer, title: document.title, route, From 03f7a509285e05365bbafead2eb47df9c70627ee Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 4 May 2023 17:12:32 +0200 Subject: [PATCH 07/13] chore: add product links ga events --- apps/www/components/Features/index.tsx | 48 ++++++++++++++++++++++-- apps/www/components/Hero.tsx | 52 ++++++++++++-------------- apps/www/components/TextLink.tsx | 28 +++++++------- apps/www/lib/gaEvents.ts | 25 +++++++++++++ 4 files changed, 107 insertions(+), 46 deletions(-) diff --git a/apps/www/components/Features/index.tsx b/apps/www/components/Features/index.tsx index b5c8dc3f68f..132690287bf 100644 --- a/apps/www/components/Features/index.tsx +++ b/apps/www/components/Features/index.tsx @@ -1,12 +1,46 @@ -import { Button, Badge, IconArrowRight } from 'ui' -import SectionHeader from 'components/UI/SectionHeader' +import { Badge } from 'ui' import Solutions from 'data/Solutions.json' -import Link from 'next/link' +import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' +import Telemetry from '~/lib/telemetry' +import gaEvents from '~/lib/gaEvents' import SectionContainer from '../Layouts/SectionContainer' import ProductIcon from '../ProductIcon' import TextLink from '../TextLink' const Features = () => { + const googleAnalyticsProps = useGoogleAnalyticsProps() + + const sendTelemetryEvent = async (product: any) => { + switch (product) { + case 'Database': + return await Telemetry.sendEvent( + gaEvents['www_hp_subhero_products_database'], + googleAnalyticsProps + ) + case 'Authentication': + return await Telemetry.sendEvent( + gaEvents['www_hp_subhero_products_auth'], + googleAnalyticsProps + ) + case 'Storage': + return await Telemetry.sendEvent( + gaEvents['www_hp_subhero_products_storage'], + googleAnalyticsProps + ) + case 'Edge Functions': + console.log('edge!!') + return await Telemetry.sendEvent( + gaEvents['www_hp_subhero_products_edgeFunctions'], + googleAnalyticsProps + ) + case 'Realtime': + return await Telemetry.sendEvent( + gaEvents['www_hp_subhero_products_realtime'], + googleAnalyticsProps + ) + } + } + const IconSections = Object.values(Solutions).map((solution: any) => { const { name, description, icon, label, url } = solution if (solution.name === 'Realtime') return null @@ -24,7 +58,13 @@ const Features = () => { {label}
)} - {url && } + {url && ( + sendTelemetryEvent(name)} + /> + )} ) }) diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index 32f699d2939..b532225359b 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -1,17 +1,17 @@ import { Button, IconBookOpen } from 'ui' import { useRouter } from 'next/router' +import Link from 'next/link' import SectionContainer from './Layouts/SectionContainer' import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry' import gaEvents from '~/lib/gaEvents' import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' const Hero = () => { - const router = useRouter() + const { basePath } = useRouter() const googleAnalyticsProps = useGoogleAnalyticsProps() - const trackAndNavigate = async (url: string, event: GoogleAnalyticsEvent) => { + const sendTelemetryEvent = async (event: GoogleAnalyticsEvent) => { await Telemetry.sendEvent(event, googleAnalyticsProps) - router.push(url) } return ( @@ -47,28 +47,22 @@ const Hero = () => {
- - + + sendTelemetryEvent(gaEvents['www_hp_hero_startProject'])}> + + + + + sendTelemetryEvent(gaEvents['www_hp_hero_documentation'])} + > + + +
backed by @@ -76,22 +70,22 @@ const Hero = () => {
Y Combinator Mozilla Coatue Felicis
diff --git a/apps/www/components/TextLink.tsx b/apps/www/components/TextLink.tsx index 3bd3edfc10e..5da959a41fe 100644 --- a/apps/www/components/TextLink.tsx +++ b/apps/www/components/TextLink.tsx @@ -1,20 +1,22 @@ -import { IconArrowRight, IconChevronRight } from 'ui' +import { IconChevronRight } from 'ui' import Link from 'next/link' -function TextLink({ url, label }: any) { +function TextLink({ url, label, ...props }: any) { return ( - -
- {`${label} about ${url}`} - {label} - - + + ) } diff --git a/apps/www/lib/gaEvents.ts b/apps/www/lib/gaEvents.ts index 167aa356a0c..73599720f86 100644 --- a/apps/www/lib/gaEvents.ts +++ b/apps/www/lib/gaEvents.ts @@ -15,6 +15,31 @@ const GoogleAnalyticsEvents: GoogleAnalyticsEventType = { category: 'link', label: '', }, + www_hp_subhero_products_database: { + action: 'www_hp_subhero_products_database', + category: 'link', + label: '', + }, + www_hp_subhero_products_auth: { + action: 'www_hp_subhero_products_auth', + category: 'link', + label: '', + }, + www_hp_subhero_products_storage: { + action: 'www_hp_subhero_products_storage', + category: 'link', + label: '', + }, + www_hp_subhero_products_edgeFunctions: { + action: 'www_hp_subhero_products_edgeFunctions', + category: 'link', + label: '', + }, + www_hp_subhero_products_realtime: { + action: 'www_hp_subhero_products_realtime', + category: 'link', + label: '', + }, } export default GoogleAnalyticsEvents From 4715044f5672be33ccbf53df4bd3e3d576ed1ab5 Mon Sep 17 00:00:00 2001 From: Francesco Sansalvadore Date: Thu, 4 May 2023 17:22:46 +0200 Subject: [PATCH 08/13] chore: fix TextLink href --- apps/www/components/Features/index.tsx | 1 - apps/www/components/TextLink.tsx | 4 ++-- apps/www/data/Solutions.json | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/www/components/Features/index.tsx b/apps/www/components/Features/index.tsx index 132690287bf..bb84b4dbf4c 100644 --- a/apps/www/components/Features/index.tsx +++ b/apps/www/components/Features/index.tsx @@ -28,7 +28,6 @@ const Features = () => { googleAnalyticsProps ) case 'Edge Functions': - console.log('edge!!') return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_edgeFunctions'], googleAnalyticsProps diff --git a/apps/www/components/TextLink.tsx b/apps/www/components/TextLink.tsx index 5da959a41fe..73d54f6136e 100644 --- a/apps/www/components/TextLink.tsx +++ b/apps/www/components/TextLink.tsx @@ -1,9 +1,9 @@ import { IconChevronRight } from 'ui' import Link from 'next/link' -function TextLink({ url, label, ...props }: any) { +function TextLink({ url = '', label, ...props }: any) { return ( - + Date: Thu, 4 May 2023 17:24:50 +0200 Subject: [PATCH 09/13] chore: fix docs link --- apps/www/components/Hero.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index b532225359b..28cbc079bca 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -54,7 +54,7 @@ const Hero = () => { - + sendTelemetryEvent(gaEvents['www_hp_hero_documentation'])} > From 10d52861dc2ad5f541a05dd64c85eb5c156066de Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 8 May 2023 09:27:46 +0700 Subject: [PATCH 10/13] feat: shared useGoogleAnalyticsProps --- apps/docs/lib/fetchWrappers.tsx | 4 +-- apps/docs/pages/_app.tsx | 7 +++- apps/www/components/Features/index.tsx | 2 +- apps/www/components/Hero.tsx | 2 +- apps/www/hooks/useGoogleAnalyticsProps.tsx | 13 ------- apps/www/package.json | 1 + apps/www/pages/_app.tsx | 17 ++++++++- package-lock.json | 35 ++++++++++++++++++- packages/common/hooks/index.ts | 1 + .../common/hooks/useGoogleAnalyticsProps.ts | 12 +++++++ 10 files changed, 73 insertions(+), 21 deletions(-) delete mode 100644 apps/www/hooks/useGoogleAnalyticsProps.tsx create mode 100644 packages/common/hooks/useGoogleAnalyticsProps.ts diff --git a/apps/docs/lib/fetchWrappers.tsx b/apps/docs/lib/fetchWrappers.tsx index c573a0d37dc..0313e49565f 100644 --- a/apps/docs/lib/fetchWrappers.tsx +++ b/apps/docs/lib/fetchWrappers.tsx @@ -1,7 +1,5 @@ interface DataProps { - referrer?: string - title: string - route?: string + [prop: string]: any } export const post = (url: string, data: DataProps, options = {}) => { diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx index f66ae722cd3..e80d0dd598d 100644 --- a/apps/docs/pages/_app.tsx +++ b/apps/docs/pages/_app.tsx @@ -1,6 +1,6 @@ import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs' import { SessionContextProvider } from '@supabase/auth-helpers-react' -import { AuthProvider, ThemeProvider } from 'common' +import { AuthProvider, ThemeProvider, useGoogleAnalyticsProps } from 'common' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import ReactMarkdown from 'react-markdown' @@ -19,6 +19,7 @@ import '../styles/prism-okaidia.scss' function MyApp({ Component, pageProps }: AppPropsWithLayout) { const router = useRouter() + const googleAnalyticsProps = useGoogleAnalyticsProps() const [supabase] = useState(() => IS_PLATFORM || LOCAL_SUPABASE ? createBrowserSupabaseClient() : undefined @@ -29,6 +30,10 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { referrer: document.referrer, title: document.title, route, + ga: { + screen_resolution: googleAnalyticsProps?.screenResolution, + language: googleAnalyticsProps?.language, + }, }) } diff --git a/apps/www/components/Features/index.tsx b/apps/www/components/Features/index.tsx index bb84b4dbf4c..2b14cc6fbfe 100644 --- a/apps/www/components/Features/index.tsx +++ b/apps/www/components/Features/index.tsx @@ -1,11 +1,11 @@ import { Badge } from 'ui' import Solutions from 'data/Solutions.json' -import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' import Telemetry from '~/lib/telemetry' import gaEvents from '~/lib/gaEvents' import SectionContainer from '../Layouts/SectionContainer' import ProductIcon from '../ProductIcon' import TextLink from '../TextLink' +import { useGoogleAnalyticsProps } from 'common/hooks/useGoogleAnalyticsProps' const Features = () => { const googleAnalyticsProps = useGoogleAnalyticsProps() diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index 28cbc079bca..e8d336787af 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -4,7 +4,7 @@ import Link from 'next/link' import SectionContainer from './Layouts/SectionContainer' import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry' import gaEvents from '~/lib/gaEvents' -import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps' +import { useGoogleAnalyticsProps } from 'common/hooks/useGoogleAnalyticsProps' const Hero = () => { const { basePath } = useRouter() diff --git a/apps/www/hooks/useGoogleAnalyticsProps.tsx b/apps/www/hooks/useGoogleAnalyticsProps.tsx deleted file mode 100644 index a3254a8b307..00000000000 --- a/apps/www/hooks/useGoogleAnalyticsProps.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useRouter } from 'next/router' - -const useGoogleAnalyticsProps = () => { - const { locale } = useRouter() - - return { - screenResolution: - typeof window !== 'undefined' ? `${window.innerWidth}x${window.innerHeight}` : undefined, - language: locale ?? 'en-US', - } -} - -export default useGoogleAnalyticsProps diff --git a/apps/www/package.json b/apps/www/package.json index cadcf99b872..8242300daea 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -20,6 +20,7 @@ "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", "@supabase/supabase-js": "^2.13.0", + "browser-session-tabs": "^3.0.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index dce4295802c..f2e7ad106a0 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -6,20 +6,35 @@ import { useEffect } from 'react' import Meta from '~/components/Favicons' import '../styles/index.css' import { post } from '~/lib/fetchWrapper' -import { AuthProvider, ThemeProvider } from 'common' +import { AuthProvider, ThemeProvider, useGoogleAnalyticsProps } from 'common' +import { BrowserTabTracker } from 'browser-session-tabs' +import { v4 as uuidv4 } from 'uuid' import Head from 'next/head' export default function MyApp({ Component, pageProps }: AppProps) { const router = useRouter() + const googleAnalyticsProps = useGoogleAnalyticsProps() function handlePageTelemetry(route: string) { return post(`${API_URL}/telemetry/page`, { referrer: document.referrer, title: document.title, route, + ga: { + screen_resolution: googleAnalyticsProps?.screenResolution, + language: googleAnalyticsProps?.language, + }, }) } + useEffect(() => { + // Generate browser session id for anon tracking + BrowserTabTracker.initialize({ + storageKey: 'supabase.browser.session', + sessionIdGenerator: () => uuidv4(), + }) + }, []) + useEffect(() => { function handleRouteChange(url: string) { handlePageTelemetry(url) diff --git a/package-lock.json b/package-lock.json index 0881d78353b..efb8b271642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -673,6 +673,7 @@ "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", "@supabase/supabase-js": "^2.13.0", + "browser-session-tabs": "^3.0.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", @@ -12807,6 +12808,22 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, + "node_modules/browser-session-tabs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/browser-session-tabs/-/browser-session-tabs-3.0.0.tgz", + "integrity": "sha512-HBJWkCbcDYFL+hlSGld54SF1SgaiSXnw9vaTPdDeOxT9HCTFt3hVdArIroQ3HDhh9vhqkbl6TkMjTFok0ZfwIA==", + "dependencies": { + "js-cookie": "^3.0.1" + } + }, + "node_modules/browser-session-tabs/node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -48910,6 +48927,21 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, + "browser-session-tabs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/browser-session-tabs/-/browser-session-tabs-3.0.0.tgz", + "integrity": "sha512-HBJWkCbcDYFL+hlSGld54SF1SgaiSXnw9vaTPdDeOxT9HCTFt3hVdArIroQ3HDhh9vhqkbl6TkMjTFok0ZfwIA==", + "requires": { + "js-cookie": "^3.0.1" + }, + "dependencies": { + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + } + } + }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -68107,7 +68139,7 @@ "@types/react": "^17.0.37", "@types/react-copy-to-clipboard": "^5.0.4", "@types/react-dom": "^17.0.11", - "@types/react-syntax-highlighter": "*", + "@types/react-syntax-highlighter": "^15.5.6", "autoprefixer": "^10.4.2", "clsx": "^1.2.1", "cmdk-supabase": "^0.2.2", @@ -69873,6 +69905,7 @@ "@types/react-dom": "^17.0.11", "@types/react-syntax-highlighter": "^13.5.2", "autoprefixer": "^10.4.12", + "browser-session-tabs": "^3.0.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", diff --git a/packages/common/hooks/index.ts b/packages/common/hooks/index.ts index 7560f7ebcf5..1d07190bbf5 100644 --- a/packages/common/hooks/index.ts +++ b/packages/common/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useBreakpoint' export * from './useParams' +export * from './useGoogleAnalyticsProps' diff --git a/packages/common/hooks/useGoogleAnalyticsProps.ts b/packages/common/hooks/useGoogleAnalyticsProps.ts new file mode 100644 index 00000000000..3b247008f9f --- /dev/null +++ b/packages/common/hooks/useGoogleAnalyticsProps.ts @@ -0,0 +1,12 @@ +import { useRouter } from 'next/router' + +const isBrowser = typeof window !== 'undefined' + +export function useGoogleAnalyticsProps() { + const { locale } = useRouter() + + return { + screenResolution: isBrowser ? `${window.innerWidth}x${window.innerHeight}` : undefined, + language: locale ?? 'en-US', + } +} From f26d4a823282122b7bf20935a1604b6fc91d54e6 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 8 May 2023 09:33:57 +0700 Subject: [PATCH 11/13] fix: use shared useGoogleAnalyticsProps --- .../SQLEditor/SQLTemplates/SQLTemplates.tsx | 7 ++++--- .../SQLEditor/UtilityPanel/ResultsDropdown.tsx | 6 ++++-- studio/components/ui/PageTelemetry.tsx | 8 ++++---- studio/lib/auth.tsx | 6 ++++-- studio/stores/UiStore.ts | 17 +---------------- 5 files changed, 17 insertions(+), 27 deletions(-) diff --git a/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx b/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx index 43548b7ec7d..d1ea1631330 100644 --- a/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx +++ b/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx @@ -8,7 +8,7 @@ import { useProfileQuery } from 'data/profile/profile-query' import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.constants' import SQLCard from './SQLCard' import { createSqlSnippetSkeleton } from '../SQLEditor.utils' -import { useParams } from 'common' +import { useGoogleAnalyticsProps, useParams } from 'common' import { useRouter } from 'next/router' import { SqlSnippet } from 'data/content/sql-snippets-query' import { useSqlEditorStateSnapshot } from 'state/sql-editor' @@ -21,6 +21,7 @@ const SQLTemplates = observer(() => { const { data: profile } = useProfileQuery() const [sql, quickStart] = partition(SQL_TEMPLATES, { type: 'template' }) + const googleAnalyticsProps = useGoogleAnalyticsProps() const snap = useSqlEditorStateSnapshot() const canCreateSQLSnippet = checkPermissions(PermissionAction.CREATE, 'user_content', { resource: { type: 'sql', owner_id: profile?.id }, @@ -77,7 +78,7 @@ const SQLTemplates = observer(() => { action: 'script_clicked', label: x.title, }, - ui.googleAnalyticsProps + googleAnalyticsProps ) }} /> @@ -111,7 +112,7 @@ const SQLTemplates = observer(() => { action: 'quickstart_clicked', label: x.title, }, - ui.googleAnalyticsProps + googleAnalyticsProps ) }} /> diff --git a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx index 2ba8e6a3324..0f9ac2fe627 100644 --- a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx +++ b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx @@ -9,6 +9,7 @@ import { CSVLink } from 'react-csv' import { useSqlEditorStateSnapshot } from 'state/sql-editor' // @ts-ignore import MarkdownTable from 'markdown-table' +import { useGoogleAnalyticsProps } from 'common' export type ResultsDropdownProps = { id: string @@ -17,6 +18,7 @@ export type ResultsDropdownProps = { const ResultsDropdown = ({ id }: ResultsDropdownProps) => { const { project } = useProjectContext() const snap = useSqlEditorStateSnapshot() + const googleAnalyticsProps = useGoogleAnalyticsProps() const result = snap.results?.[id]?.[0] ?? undefined const { ui } = useStore() const csvRef = useRef(null) @@ -30,7 +32,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { csvRef.current?.link.click() Telemetry.sendEvent( { category: 'sql_editor', action: 'sql_download_csv', label: '' }, - ui.googleAnalyticsProps + googleAnalyticsProps ) } @@ -53,7 +55,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { ui.setNotification({ category: 'success', message: 'Copied results to clipboard' }) Telemetry.sendEvent( { category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' }, - ui.googleAnalyticsProps + googleAnalyticsProps ) }) } diff --git a/studio/components/ui/PageTelemetry.tsx b/studio/components/ui/PageTelemetry.tsx index 412336e28d0..b6f2591b0b4 100644 --- a/studio/components/ui/PageTelemetry.tsx +++ b/studio/components/ui/PageTelemetry.tsx @@ -1,4 +1,4 @@ -import { useStore } from 'hooks' +import { useGoogleAnalyticsProps } from 'common' import { post } from 'lib/common/fetch' import { API_URL, IS_PLATFORM } from 'lib/constants' import { observer } from 'mobx-react-lite' @@ -25,7 +25,7 @@ function sanitizePageViewRoute(_route?: string) { const PageTelemetry: FC = ({ children }) => { const router = useRouter() - const { ui } = useStore() + const googleAnalyticsProps = useGoogleAnalyticsProps() useEffect(() => { function handleRouteChange(url: string) { @@ -71,8 +71,8 @@ const PageTelemetry: FC = ({ children }) => { title: document.title, route, ga: { - screen_resolution: ui.googleAnalyticsProps?.screenResolution, - language: ui.googleAnalyticsProps?.language, + screen_resolution: googleAnalyticsProps?.screenResolution, + language: googleAnalyticsProps?.language, }, }) } diff --git a/studio/lib/auth.tsx b/studio/lib/auth.tsx index c7b962a2010..f02f3688130 100644 --- a/studio/lib/auth.tsx +++ b/studio/lib/auth.tsx @@ -1,18 +1,20 @@ import { AuthContext as AuthContextInternal, AuthProvider as AuthProviderInternal, - AuthProviderProps, gotrueClient, + useGoogleAnalyticsProps, } from 'common' import { useProfileQuery } from 'data/profile/profile-query' import { useStore } from 'hooks' import { PropsWithChildren, useEffect } from 'react' import { GOTRUE_ERRORS, IS_PLATFORM } from './constants' +import Telemetry from 'lib/telemetry' export const AuthContext = AuthContextInternal export const AuthProvider = ({ children }: PropsWithChildren<{}>) => { const { ui, app } = useStore() + const googleAnalyticsProps = useGoogleAnalyticsProps() // Check for unverified GitHub users after a GitHub sign in useEffect(() => { @@ -34,7 +36,7 @@ export const AuthProvider = ({ children }: PropsWithChildren<{}>) => { // Track telemetry for the current user useProfileQuery({ onSuccess(profile) { - ui.setProfile(profile) + Telemetry.sendIdentify(profile, googleAnalyticsProps) // [Joshen] Temp fix: For new users, the GET profile call also creates a default org // But because the dashboard's logged in state is using gotrue as the source of truth diff --git a/studio/stores/UiStore.ts b/studio/stores/UiStore.ts index 1b91898ae31..0e7b35339f9 100644 --- a/studio/stores/UiStore.ts +++ b/studio/stores/UiStore.ts @@ -1,8 +1,7 @@ import { uuidv4 } from 'lib/helpers' import { action, makeAutoObservable } from 'mobx' -import { Project, Notification, User, Organization, ProjectBase, Permission } from 'types' +import { Project, Notification, Organization, ProjectBase, Permission } from 'types' import { IRootStore } from './RootStore' -import Telemetry, { GoogleAnalyticsProps } from 'lib/telemetry' export interface IUiStore { language: 'en-US' @@ -12,12 +11,10 @@ export interface IUiStore { selectedOrganization?: Organization notification?: Notification permissions?: Permission[] - googleAnalyticsProps?: GoogleAnalyticsProps load: () => void setProjectRef: (ref?: string) => void setOrganizationSlug: (slug?: string) => void setNotification: (notification: Notification) => string - setProfile: (value: User) => void setPermissions: (permissions?: Permission[]) => void } export default class UiStore implements IUiStore { @@ -83,14 +80,6 @@ export default class UiStore implements IUiStore { return undefined } - get googleAnalyticsProps() { - return { - screenResolution: - typeof window !== 'undefined' ? `${window.innerWidth}x${window.innerHeight}` : undefined, - language: this.language, - } - } - load() { if (typeof window === 'undefined') return } @@ -109,10 +98,6 @@ export default class UiStore implements IUiStore { return id } - setProfile(value: User) { - Telemetry.sendIdentify(value, this.googleAnalyticsProps) - } - setPermissions(permissions?: any) { this.permissions = permissions } From 5ab4f79a3167ddf08820ef59f3c346009be1cd4d Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 8 May 2023 09:47:00 +0700 Subject: [PATCH 12/13] feat: send browser session on telemetry call --- apps/docs/package.json | 1 + apps/docs/pages/_app.tsx | 11 +++++++++++ apps/www/lib/telemetry.ts | 2 ++ apps/www/pages/_app.tsx | 1 + package-lock.json | 4 ++++ studio/components/ui/PageTelemetry.tsx | 2 ++ studio/lib/telemetry.ts | 3 +++ studio/package.json | 1 + studio/pages/_app.tsx | 10 ++++++++++ 9 files changed, 35 insertions(+) diff --git a/apps/docs/package.json b/apps/docs/package.json index 0eb29301d62..7f8438339e1 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -56,6 +56,7 @@ "@supabase/supabase-js": "^2.13.0", "algoliasearch": "^4.14.2", "babel": "^6.23.0", + "browser-session-tabs": "^3.0.0", "clsx": "^1.2.1", "common": "*", "config": "*", diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx index e80d0dd598d..e0ad82b5602 100644 --- a/apps/docs/pages/_app.tsx +++ b/apps/docs/pages/_app.tsx @@ -12,6 +12,8 @@ import Favicons from '~/components/Favicons' import SiteLayout from '~/layouts/SiteLayout' import { API_URL, IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants' import { post } from '~/lib/fetchWrappers' +import { BrowserTabTracker } from 'browser-session-tabs' +import { v4 as uuidv4 } from 'uuid' import '../styles/ch.scss' import '../styles/main.scss?v=1.0.0' import '../styles/new-docs.scss' @@ -33,10 +35,19 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ga: { screen_resolution: googleAnalyticsProps?.screenResolution, language: googleAnalyticsProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } + useEffect(() => { + // Generate browser session id for anon tracking + BrowserTabTracker.initialize({ + storageKey: 'supabase.browser.session', + sessionIdGenerator: () => uuidv4(), + }) + }, []) + useEffect(() => { function handleRouteChange(url: string) { /* diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts index 0c6fb2ed9fe..aa0097d1f17 100644 --- a/apps/www/lib/telemetry.ts +++ b/apps/www/lib/telemetry.ts @@ -1,5 +1,6 @@ import { post } from '~/lib/fetchWrapper' import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants' +import { BrowserTabTracker } from 'browser-session-tabs' export interface GoogleAnalyticsEvent { category: string @@ -29,6 +30,7 @@ const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) = ga: { screen_resolution: gaProps?.screenResolution, language: gaProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index f2e7ad106a0..ab0dbf807d2 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -23,6 +23,7 @@ export default function MyApp({ Component, pageProps }: AppProps) { ga: { screen_resolution: googleAnalyticsProps?.screenResolution, language: googleAnalyticsProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } diff --git a/package-lock.json b/package-lock.json index efb8b271642..4b3477ab38d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "@supabase/supabase-js": "^2.13.0", "algoliasearch": "^4.14.2", "babel": "^6.23.0", + "browser-session-tabs": "^3.0.0", "clsx": "^1.2.1", "common": "*", "config": "*", @@ -39206,6 +39207,7 @@ "analytics-node": "^3.5.0", "awesome-debounce-promise": "^2.1.0", "blueimp-md5": "^2.19.0", + "browser-session-tabs": "^3.0.0", "clipboard": "^2.0.8", "clsx": "^1.2.1", "common": "*", @@ -51570,6 +51572,7 @@ "@types/react": "17.0.39", "algoliasearch": "^4.14.2", "babel": "^6.23.0", + "browser-session-tabs": "^3.0.0", "clsx": "^1.2.1", "common": "*", "config": "*", @@ -66453,6 +66456,7 @@ "aws-sdk": "^2.1354.0", "babel-loader": "^8.2.3", "blueimp-md5": "^2.19.0", + "browser-session-tabs": "^3.0.0", "clipboard": "^2.0.8", "clsx": "^1.2.1", "common": "*", diff --git a/studio/components/ui/PageTelemetry.tsx b/studio/components/ui/PageTelemetry.tsx index b6f2591b0b4..54b9155cec1 100644 --- a/studio/components/ui/PageTelemetry.tsx +++ b/studio/components/ui/PageTelemetry.tsx @@ -1,3 +1,4 @@ +import { BrowserTabTracker } from 'browser-session-tabs' import { useGoogleAnalyticsProps } from 'common' import { post } from 'lib/common/fetch' import { API_URL, IS_PLATFORM } from 'lib/constants' @@ -73,6 +74,7 @@ const PageTelemetry: FC = ({ children }) => { ga: { screen_resolution: googleAnalyticsProps?.screenResolution, language: googleAnalyticsProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } diff --git a/studio/lib/telemetry.ts b/studio/lib/telemetry.ts index ea5dcb38bcb..1fc185b1699 100644 --- a/studio/lib/telemetry.ts +++ b/studio/lib/telemetry.ts @@ -1,6 +1,7 @@ import { post } from 'lib/common/fetch' import { API_URL, IS_PLATFORM } from 'lib/constants' import { User } from 'types' +import { BrowserTabTracker } from 'browser-session-tabs' export interface GoogleAnalyticsProps { screenResolution?: string @@ -28,6 +29,7 @@ const sendEvent = ( ga: { screen_resolution: gaProps?.screenResolution, language: gaProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } @@ -44,6 +46,7 @@ const sendIdentify = (user: User, gaProps?: GoogleAnalyticsProps) => { ga: { screen_resolution: gaProps?.screenResolution, language: gaProps?.language, + session_id: BrowserTabTracker.sessionId, }, }) } diff --git a/studio/package.json b/studio/package.json index 2f845d6011e..e85e5682e10 100644 --- a/studio/package.json +++ b/studio/package.json @@ -41,6 +41,7 @@ "analytics-node": "^3.5.0", "awesome-debounce-promise": "^2.1.0", "blueimp-md5": "^2.19.0", + "browser-session-tabs": "^3.0.0", "clipboard": "^2.0.8", "clsx": "^1.2.1", "common": "*", diff --git a/studio/pages/_app.tsx b/studio/pages/_app.tsx index 8a9144cf26f..c35c5c382ae 100644 --- a/studio/pages/_app.tsx +++ b/studio/pages/_app.tsx @@ -50,6 +50,8 @@ import Favicons from 'components/head/Favicons' import { IS_PLATFORM } from 'lib/constants' import { SessionContextProvider } from '@supabase/auth-helpers-react' import { createClient } from '@supabase/supabase-js' +import { BrowserTabTracker } from 'browser-session-tabs' +import { v4 as uuidv4 } from 'uuid' dayjs.extend(customParseFormat) dayjs.extend(utc) @@ -74,6 +76,14 @@ function CustomApp({ Component, pageProps }: AppPropsWithLayout) { const getSavingState = () => rootStore.content.savingState + useEffect(() => { + // Generate browser session id for anon tracking + BrowserTabTracker.initialize({ + storageKey: 'supabase.browser.session', + sessionIdGenerator: () => uuidv4(), + }) + }, []) + // prompt the user if they try and leave with unsaved content store changes useEffect(() => { const warningText = 'You have unsaved changes - are you sure you wish to leave this page?' From 00e76cd00da28b441d8ee60e79c7d7e3d05e1790 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 8 May 2023 15:29:47 +0700 Subject: [PATCH 13/13] feat: send page params on telemetry event --- apps/www/components/Features/index.tsx | 17 ++++++++++++----- apps/www/components/Hero.tsx | 5 +++-- apps/www/lib/telemetry.ts | 10 +++++++++- .../SQLEditor/SQLTemplates/SQLTemplates.tsx | 6 ++++-- .../SQLEditor/UtilityPanel/ResultsDropdown.tsx | 8 ++++++-- studio/lib/telemetry.ts | 7 ++++++- 6 files changed, 40 insertions(+), 13 deletions(-) diff --git a/apps/www/components/Features/index.tsx b/apps/www/components/Features/index.tsx index 2b14cc6fbfe..4b291e78767 100644 --- a/apps/www/components/Features/index.tsx +++ b/apps/www/components/Features/index.tsx @@ -6,8 +6,10 @@ import SectionContainer from '../Layouts/SectionContainer' import ProductIcon from '../ProductIcon' import TextLink from '../TextLink' import { useGoogleAnalyticsProps } from 'common/hooks/useGoogleAnalyticsProps' +import { useRouter } from 'next/router' const Features = () => { + const router = useRouter() const googleAnalyticsProps = useGoogleAnalyticsProps() const sendTelemetryEvent = async (product: any) => { @@ -15,27 +17,32 @@ const Features = () => { case 'Database': return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_database'], - googleAnalyticsProps + googleAnalyticsProps, + router ) case 'Authentication': return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_auth'], - googleAnalyticsProps + googleAnalyticsProps, + router ) case 'Storage': return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_storage'], - googleAnalyticsProps + googleAnalyticsProps, + router ) case 'Edge Functions': return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_edgeFunctions'], - googleAnalyticsProps + googleAnalyticsProps, + router ) case 'Realtime': return await Telemetry.sendEvent( gaEvents['www_hp_subhero_products_realtime'], - googleAnalyticsProps + googleAnalyticsProps, + router ) } } diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index e8d336787af..33a1e3deec6 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -7,11 +7,12 @@ import gaEvents from '~/lib/gaEvents' import { useGoogleAnalyticsProps } from 'common/hooks/useGoogleAnalyticsProps' const Hero = () => { - const { basePath } = useRouter() + const router = useRouter() + const { basePath } = router const googleAnalyticsProps = useGoogleAnalyticsProps() const sendTelemetryEvent = async (event: GoogleAnalyticsEvent) => { - await Telemetry.sendEvent(event, googleAnalyticsProps) + await Telemetry.sendEvent(event, googleAnalyticsProps, router) } return ( diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts index aa0097d1f17..e64f0992b68 100644 --- a/apps/www/lib/telemetry.ts +++ b/apps/www/lib/telemetry.ts @@ -1,6 +1,7 @@ import { post } from '~/lib/fetchWrapper' import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants' import { BrowserTabTracker } from 'browser-session-tabs' +import { NextRouter } from 'next/router' export interface GoogleAnalyticsEvent { category: string @@ -17,7 +18,11 @@ export interface GoogleAnalyticsProps { // This event is the same as in studio/lib/telemetry.tx // but uses different ENV variables for www -const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) => { +const sendEvent = ( + event: GoogleAnalyticsEvent, + gaProps: GoogleAnalyticsProps, + router: NextRouter +) => { if (!IS_PROD && !IS_PREVIEW) return const { category, action, label, value } = event @@ -27,6 +32,9 @@ const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) = category: category, label: label, value: value, + page_referrer: document?.referrer, + page_title: document?.title, + page_location: router.asPath, ga: { screen_resolution: gaProps?.screenResolution, language: gaProps?.language, diff --git a/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx b/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx index d1ea1631330..f400dbd6a13 100644 --- a/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx +++ b/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx @@ -78,7 +78,8 @@ const SQLTemplates = observer(() => { action: 'script_clicked', label: x.title, }, - googleAnalyticsProps + googleAnalyticsProps, + router ) }} /> @@ -112,7 +113,8 @@ const SQLTemplates = observer(() => { action: 'quickstart_clicked', label: x.title, }, - googleAnalyticsProps + googleAnalyticsProps, + router ) }} /> diff --git a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx index 0f9ac2fe627..4d47dcedb13 100644 --- a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx +++ b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx @@ -10,6 +10,7 @@ import { useSqlEditorStateSnapshot } from 'state/sql-editor' // @ts-ignore import MarkdownTable from 'markdown-table' import { useGoogleAnalyticsProps } from 'common' +import { useRouter } from 'next/router' export type ResultsDropdownProps = { id: string @@ -22,6 +23,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { const result = snap.results?.[id]?.[0] ?? undefined const { ui } = useStore() const csvRef = useRef(null) + const router = useRouter() const csvData = useMemo( () => (result?.rows ? compact(Array.from(result.rows || [])) : ''), @@ -32,7 +34,8 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { csvRef.current?.link.click() Telemetry.sendEvent( { category: 'sql_editor', action: 'sql_download_csv', label: '' }, - googleAnalyticsProps + googleAnalyticsProps, + router ) } @@ -55,7 +58,8 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { ui.setNotification({ category: 'success', message: 'Copied results to clipboard' }) Telemetry.sendEvent( { category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' }, - googleAnalyticsProps + googleAnalyticsProps, + router ) }) } diff --git a/studio/lib/telemetry.ts b/studio/lib/telemetry.ts index 1fc185b1699..f3a75970c6a 100644 --- a/studio/lib/telemetry.ts +++ b/studio/lib/telemetry.ts @@ -2,6 +2,7 @@ import { post } from 'lib/common/fetch' import { API_URL, IS_PLATFORM } from 'lib/constants' import { User } from 'types' import { BrowserTabTracker } from 'browser-session-tabs' +import { NextRouter } from 'next/router' export interface GoogleAnalyticsProps { screenResolution?: string @@ -15,7 +16,8 @@ const sendEvent = ( label: string value?: string }, - gaProps?: GoogleAnalyticsProps + gaProps: GoogleAnalyticsProps, + router: NextRouter ) => { if (!IS_PLATFORM) return @@ -26,6 +28,9 @@ const sendEvent = ( category: category, label: label, value: value, + page_referrer: document?.referrer, + page_title: document?.title, + page_location: router.asPath, ga: { screen_resolution: gaProps?.screenResolution, language: gaProps?.language,