mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? - Noticing our code we have many patterns of calling localstorage and handling those errors - We should add those in a single well tested file - Handle those errors in the singleton which makes it easier for us to debug customer issues. Logger is outputing local storage warnings for feature we expose - Side effect of this is random crashes on studio when local storage isn't available or handled correctly <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved browser storage handling across the app for more reliable persistence and graceful behavior in restricted or non-browser environments (settings, previews, charts, tabs, sign-in/session flows, integrations, and UI state). * **New Features** * Introduced a safe storage layer to standardize and harden local/session persistence. * **Tests** * Added comprehensive tests covering the new safe storage behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
import * as Sentry from '@sentry/nextjs'
|
|
import { LOCAL_STORAGE_KEYS, PageTelemetry, posthogClient, safeLocalStorage, 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 = safeLocalStorage.getItem(LOCAL_STORAGE_KEYS.SENTRY_USER_ID)
|
|
if (!sentryUserId) {
|
|
sentryUserId = await getAnonId(user?.id)
|
|
safeLocalStorage.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}
|
|
/>
|
|
)
|
|
}
|