| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- import * as Sentry from '@sentry/nextjs'
- import { LOCAL_STORAGE_KEYS, PageTelemetry, posthogClient, useUser } from 'common'
- import { useEffect, useRef } from 'react'
- import { useConsentToast } from 'ui-patterns/consent'
- import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
- import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
- import { API_URL, IS_PLATFORM } from '@/lib/constants'
- const getAnonId = async (id: string) => {
- const encoder = new TextEncoder()
- const data = encoder.encode(id)
- const hashBuffer = await crypto.subtle.digest('SHA-256', data)
- const hashArray = Array.from(new Uint8Array(hashBuffer))
- const base64String = btoa(hashArray.map((byte) => String.fromCharCode(byte)).join(''))
- return base64String
- }
- export function Telemetry() {
- // Although this is "technically" breaking the rules of hooks
- // IS_PLATFORM never changes within a session, so this won't cause any issues
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const { hasAcceptedConsent } = IS_PLATFORM ? useConsentToast() : { hasAcceptedConsent: true }
- // Get org from selected organization query because it's not
- // always available in the URL params
- const { data: organization } = useSelectedOrganizationQuery()
- const user = useUser()
- // Mirror the user's org-list length into a PostHog person property so feature
- // flags and analytics can segment by current org membership. signup_timestamp
- // is set on the same identify so flag audiences requiring both properties see
- // them together on /decide. Only fires when the value changes.
- const { data: organizations } = useOrganizationsQuery()
- const lastSentRef = useRef<{
- userId: string
- orgCount: number
- signupTimestamp?: string
- } | null>(null)
- useEffect(() => {
- if (!user?.id || !organizations) return
- const orgCount = organizations.length
- const signupTimestamp = user.created_at ?? undefined
- const last = lastSentRef.current
- if (
- last?.userId === user.id &&
- last.orgCount === orgCount &&
- last.signupTimestamp === signupTimestamp
- ) {
- return
- }
- lastSentRef.current = { userId: user.id, orgCount, signupTimestamp }
- posthogClient.identify(user.id, {
- org_count: orgCount,
- ...(signupTimestamp && { signup_timestamp: signupTimestamp }),
- })
- }, [user?.id, user?.created_at, organizations])
- useEffect(() => {
- // don't set the sentry user id if the user hasn't logged in (so that Sentry errors show null user id instead of anonymous id)
- if (!user?.id) {
- return
- }
- const setSentryId = async () => {
- let sentryUserId = localStorage.getItem(LOCAL_STORAGE_KEYS.SENTRY_USER_ID)
- if (!sentryUserId) {
- sentryUserId = await getAnonId(user?.id)
- localStorage.setItem(LOCAL_STORAGE_KEYS.SENTRY_USER_ID, sentryUserId)
- }
- Sentry.setUser({ id: sentryUserId })
- }
- // if an error happens, continue without setting a sentry id
- setSentryId().catch((e) => console.error(e))
- }, [user?.id])
- return (
- <PageTelemetry
- API_URL={API_URL}
- hasAcceptedConsent={hasAcceptedConsent}
- enabled={IS_PLATFORM}
- organizationSlug={organization?.slug}
- />
- )
- }
|