Update telemetry calls for docs to support PH (#29749)

* Update telemetry calls for docs to support PH

* Update telemetry calls for www to support PH

* Add ts ignore

* Remove use of useRouter for docs

* Add credentials include for www and docs for telemetry calls

* Update TELEMETRY_CONSENT in common for www and docs to reset telemetry opt in preference

* Update common telemetry to use new endpoint and payload, and trigger reset request when opting out of telemetry from ui patterns PrigacySettings

* Fix

* Fix

* Fix

* Fix build issue in docs

* Fix build issue in docs

* I hope this fixes the build issues

* once more...

* Fix

* Add credentials include

* Fix
This commit is contained in:
Joshen Lim
2024-10-16 15:16:33 +08:00
committed by GitHub
parent c506934a1b
commit ed207751ad
11 changed files with 172 additions and 86 deletions

View File

@@ -1,38 +1,42 @@
'use client'
import { useTelemetryProps } from 'common'
import { isBrowser } from 'common'
import { usePathname } from 'next/navigation'
import { useCallback, useEffect } from 'react'
import { useConsent } from 'ui-patterns/ConsentToast'
import { BASE_PATH, IS_PLATFORM } from '~/lib/constants'
import { IS_PLATFORM } from '~/lib/constants'
import { unauthedAllowedPost } from '~/lib/fetch/fetchWrappers'
const useSendPageTelemetryWithConsent = () => {
const pathname = usePathname()
const { hasAcceptedConsent } = useConsent()
const telemetryProps = useTelemetryProps()
const sendPageTelemetry = useCallback(
(route: string) => {
if (!(IS_PLATFORM && hasAcceptedConsent)) return
const sendPageTelemetry = useCallback(() => {
if (!(IS_PLATFORM && hasAcceptedConsent)) return
unauthedAllowedPost('/platform/telemetry/page', {
body: {
// @ts-ignore [JOSHEN] To be fixed for PH
referrer: document.referrer,
title: document.title,
route: `${BASE_PATH}${route}`,
ga: {
screen_resolution: telemetryProps?.screenResolution,
language: telemetryProps?.language,
session_id: '',
},
const title = typeof document !== 'undefined' ? document?.title : ''
const referrer = typeof document !== 'undefined' ? document?.referrer : ''
unauthedAllowedPost('/platform/telemetry/page', {
body: {
pathname,
page_url: isBrowser ? window.location.href : '',
page_title: title,
ph: {
referrer,
language: navigator.language ?? 'en-US',
user_agent: navigator.userAgent,
search: isBrowser ? window.location.search : '',
viewport_height: isBrowser ? window.innerHeight : 0,
viewport_width: isBrowser ? window.innerWidth : 0,
},
}).catch((e) => {
console.error('Problem sending telemetry:', e)
})
},
[telemetryProps, hasAcceptedConsent]
)
},
headers: { Version: '2' },
credentials: 'include',
}).catch((e) => {
console.error('Problem sending telemetry:', e)
})
}, [pathname, hasAcceptedConsent])
return sendPageTelemetry
}
@@ -42,9 +46,7 @@ const PageTelemetry = () => {
const sendPageTelemetry = useSendPageTelemetryWithConsent()
useEffect(() => {
if (pathname) {
sendPageTelemetry(pathname)
}
if (pathname) sendPageTelemetry()
}, [pathname, sendPageTelemetry])
return null

View File

@@ -49,6 +49,7 @@ export const post: typeof _post = async (url, init) => {
const headers = await constructHeaders(init?.headers)
return await _post(url, {
credentials: 'include',
...init,
headers,
})
@@ -58,6 +59,7 @@ export const unauthedAllowedPost: typeof _post = async (url, init) => {
const headers = await constructHeaders(init?.headers, { allowUnauthenticated: true })
return await _post(url, {
credentials: 'include',
...init,
headers,
})

View File

@@ -1,7 +1,6 @@
import { isBrowser } from 'common'
import { usePathname } from 'next/navigation'
import { useConsent } from 'ui-patterns/ConsentToast'
import { unauthedAllowedPost } from './fetch/fetchWrappers'
type TelemetryEvent = {
@@ -18,20 +17,36 @@ const noop = () => {}
* Checks for user consent to telemetry before sending.
*/
const useSendTelemetryEvent = () => {
const { hasAcceptedConsent } = useConsent()
const pathname = usePathname()
const { hasAcceptedConsent } = useConsent()
if (!hasAcceptedConsent) return noop
const title = typeof document !== 'undefined' ? document?.title : ''
const referrer = typeof document !== 'undefined' ? document?.referrer : ''
return (event: TelemetryEvent) =>
unauthedAllowedPost('/platform/telemetry/event', {
// @ts-ignore - endpoint will accept this just fine
body: {
...event,
page_title: document?.title,
// @ts-ignore [JOSHEN] To be fixed for PH
page_location: pathname,
pathname,
action: event.action,
page_url: isBrowser ? window.location.href : '',
page_title: title,
ph: {
referrer,
language: navigator.language ?? 'en-US',
user_agent: navigator.userAgent,
search: isBrowser ? window.location.search : '',
viewport_height: isBrowser ? window.innerHeight : 0,
viewport_width: isBrowser ? window.innerWidth : 0,
},
custom_properties: {
category: event.category,
label: event.label,
} as any,
},
headers: { Version: '2' },
credentials: 'include',
})
.then(({ error }) => {
if (error) console.error(error)

View File

@@ -25,11 +25,13 @@ export async function get(url: string, options?: RequestInit) {
}
export async function post(url: string, data: DataProps, options?: RequestInit) {
const { headers: optionHeaders, ...otherOptions } = options || {}
const accessToken = await getAccessToken()
let headers = new Headers({
'Content-Type': 'application/json',
Accept: 'application/json',
...optionHeaders,
})
if (accessToken) {
@@ -39,8 +41,9 @@ export async function post(url: string, data: DataProps, options?: RequestInit)
return fetch(url, {
method: 'POST',
headers,
credentials: 'include',
referrerPolicy: 'no-referrer-when-downgrade',
body: JSON.stringify(data),
...options,
...otherOptions,
})
}

View File

@@ -1,7 +1,7 @@
import { post } from '~/lib/fetchWrapper'
import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants'
import { LOCAL_STORAGE_KEYS, TelemetryProps } from 'common'
import { API_URL, IS_PREVIEW, IS_PROD } from 'lib/constants'
import { NextRouter } from 'next/router'
import { LOCAL_STORAGE_KEYS } from 'common'
import { post } from '~/lib/fetchWrapper'
export interface TelemetryEvent {
category: string
@@ -10,17 +10,12 @@ export interface TelemetryEvent {
value?: string
}
export interface TelemetryProps {
screenResolution?: string
language: string
}
const noop = () => {}
// This event is the same as in studio/lib/telemetry.tx
// but uses different ENV variables for www
const sendEvent = (event: TelemetryEvent, gaProps: TelemetryProps, router: NextRouter) => {
const sendEvent = (event: TelemetryEvent, telemetryProps: TelemetryProps, router: NextRouter) => {
const consent =
typeof window !== 'undefined'
? localStorage.getItem(LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT)
@@ -32,22 +27,30 @@ const sendEvent = (event: TelemetryEvent, gaProps: TelemetryProps, router: NextR
if (blockEvent) return noop
const { category, action, label, value } = event
const title = typeof document !== 'undefined' ? document?.title : ''
const referrer = typeof document !== 'undefined' ? document?.referrer : ''
return post(`${API_URL}/telemetry/event`, {
action: action,
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,
const { page_url, search, language, viewport_height, viewport_width } = telemetryProps
return post(
`${API_URL}/telemetry/event`,
{
page_url,
action: action,
page_title: title,
pathname: router.pathname,
ph: {
search,
referrer,
language,
viewport_height,
viewport_width,
user_agent: navigator.userAgent,
},
custom_properties: { category, label, value } as any,
},
})
{ headers: { Version: '2' }, credentials: 'include' }
)
}
export default {
sendEvent,
}
export default { sendEvent }

View File

@@ -3,7 +3,14 @@ import 'config/code-hike.scss'
import '../styles/index.css'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { AuthProvider, IS_PROD, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common'
import {
AuthProvider,
IS_PROD,
isBrowser,
ThemeProvider,
useTelemetryProps,
useThemeSandbox,
} from 'common'
import { DefaultSeo } from 'next-seo'
import { AppProps } from 'next/app'
import Head from 'next/head'
@@ -30,25 +37,38 @@ export default function App({ Component, pageProps }: AppProps) {
const IS_DEV = !IS_PROD && !IS_PREVIEW
const blockEvents = IS_DEV || !hasAcceptedConsent
const title = typeof document !== 'undefined' ? document?.title : ''
const referrer = typeof document !== 'undefined' ? document?.referrer : ''
const { search, language, viewport_height, viewport_width } = telemetryProps
useThemeSandbox()
function handlePageTelemetry(route: string) {
return post(`${API_URL}/telemetry/page`, {
referrer: document.referrer,
title: document.title,
route,
ga: {
screen_resolution: telemetryProps?.screenResolution,
language: telemetryProps?.language,
function handlePageTelemetry(url: string) {
return post(
`${API_URL}/telemetry/page`,
{
page_url: url,
page_title: title,
pathname: router.pathname,
ph: {
referrer,
language,
search,
viewport_height,
viewport_width,
user_agent: navigator.userAgent,
},
},
})
{ headers: { Version: '2' }, credentials: 'include' }
)
}
useEffect(() => {
if (blockEvents) return
function handleRouteChange(url: string) {
handlePageTelemetry(url)
function handleRouteChange() {
handlePageTelemetry(window.location.href)
}
// Listen for page changes after a navigation or when the query changes
@@ -64,7 +84,7 @@ export default function App({ Component, pageProps }: AppProps) {
* Send page telemetry on first page load
*/
if (router.isReady) {
handlePageTelemetry(router.asPath)
handlePageTelemetry(window.location.href)
}
}, [router.isReady, consentValue])

View File

@@ -1,5 +1,5 @@
export const LOCAL_STORAGE_KEYS = {
TELEMETRY_CONSENT: 'supabase-consent',
TELEMETRY_CONSENT: 'supabase-consent-ph',
HIDE_PROMO_TOAST: 'supabase-hide-promo-toast',
BLOG_VIEW: 'supabase-blog-view',
}

View File

@@ -2,16 +2,19 @@ interface DataProps {
[prop: string]: any
}
export const post = (url: string, data: DataProps, options = {}) => {
export const post = (url: string, data: DataProps, options = {} as { [key: string]: any }) => {
const { optionHeaders, ...otherOptions } = options
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...optionHeaders,
},
credentials: 'include',
referrerPolicy: 'no-referrer-when-downgrade',
body: JSON.stringify(data),
...options,
...otherOptions,
}).catch((error) => {
console.error('Error at fetchWrapper - post:', error)
})

View File

@@ -3,12 +3,25 @@
import { useRouter } from 'next/compat/router'
import { isBrowser } from '../helpers'
export function useTelemetryProps() {
export interface TelemetryProps {
screenResolution?: string
page_url: string
search: string
language: string
viewport_height: number
viewport_width: number
}
export function useTelemetryProps(): TelemetryProps {
const router = useRouter()
const locale = router ? router.locale : undefined
return {
screenResolution: isBrowser ? `${window.innerWidth}x${window.innerHeight}` : undefined,
page_url: isBrowser ? window.location.href : '',
search: isBrowser ? window.location.search : '',
viewport_height: isBrowser ? window.innerHeight : 0,
viewport_width: isBrowser ? window.innerWidth : 0,
language: locale ?? 'en-US',
}
}

View File

@@ -1,13 +1,28 @@
import { post } from './fetchWrappers'
export function handlePageTelemetry(API_URL: string, route: string, telemetryProps: any) {
return post(`${API_URL}/telemetry/page`, {
referrer: document.referrer,
title: document.title,
route,
ga: {
screen_resolution: telemetryProps?.screenResolution,
language: telemetryProps?.language,
const { page_url, search, language, viewport_height, viewport_width } = telemetryProps
return post(
`${API_URL}/telemetry/page`,
{
page_url,
page_title: document.title,
pathname: route,
ph: {
referrer: document.referrer,
user_agent: navigator.userAgent,
search,
language,
viewport_height,
viewport_width,
},
},
})
{
headers: { Version: '2' },
}
)
}
export function handleResetTelemetry(API_URL: string) {
return post(`${API_URL}/telemetry/reset`, {})
}

View File

@@ -1,6 +1,6 @@
'use client'
import { LOCAL_STORAGE_KEYS } from 'common'
import { handleResetTelemetry, LOCAL_STORAGE_KEYS } from 'common'
import Link from 'next/link'
import { PropsWithChildren, useEffect, useState } from 'react'
import { Modal, Toggle } from 'ui'
@@ -30,6 +30,10 @@ export const PrivacySettings = ({
setIsOpen(false)
}
const handleOptOutTelemetry = async () => {
handleResetTelemetry(process.env.NEXT_PUBLIC_API_URL!)
}
return (
<>
<button {...props} onClick={() => setIsOpen(true)}>
@@ -77,7 +81,13 @@ export const PrivacySettings = ({
<Modal.Content>
<Toggle
checked={telemetryValue}
onChange={() => setTelemetryValue((prev) => !prev)}
onChange={() => {
if (telemetryValue) {
// [Joshen] Will be toggle off, so trigger reset event
handleOptOutTelemetry()
}
setTelemetryValue((prev) => !prev)
}}
label="Telemetry"
descriptionText={
<>