mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-26 11:58:53 +00:00
⚡️(frontend) load PostHog dynamically
posthog-js was bundled in the main chunk. Loading it dynamically saves 200ko on the initial load and around 4s of loading time on slow 4G connections. PostHog, and analytics in general, should not block the initial rendering. The risk is that some feature-flagged features may not be instantaneously available to the user, but for most users on high-speed connections this will be imperceptible. Other places in the code rely on a direct import from posthog, I only verified the delay import of posthog for the home page, and did not for the /room route. I'm pretty sure there is room for improvements on other parts of the app.
This commit is contained in:
committed by
aleb_the_flash
parent
04ec967a99
commit
c1d30f6923
@@ -1,17 +1,28 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'wouter'
|
||||
import posthog from 'posthog-js'
|
||||
import { ApiUser } from '@/features/auth/api/ApiUser'
|
||||
import { type PostHog } from 'posthog-js'
|
||||
import { type ApiUser } from '@/features/auth/api/ApiUser'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
|
||||
export const startAnalyticsSession = (data: ApiUser) => {
|
||||
if (posthog._isIdentified()) return
|
||||
const { id, email } = data
|
||||
posthog.identify(id, { email })
|
||||
let posthog: PostHog | null = null
|
||||
|
||||
const getPosthog = async () => {
|
||||
if (!posthog) posthog = (await import('posthog-js')).default
|
||||
return posthog
|
||||
}
|
||||
|
||||
export const terminateAnalyticsSession = () => {
|
||||
if (!posthog._isIdentified()) return
|
||||
posthog.reset()
|
||||
export const startAnalyticsSession = (data: ApiUser) => {
|
||||
getPosthog().then((ph) => {
|
||||
if (ph._isIdentified()) return
|
||||
const { id, email } = data
|
||||
ph.identify(id, { email })
|
||||
})
|
||||
}
|
||||
|
||||
export const terminateAnalyticsSession = async () => {
|
||||
const ph = await getPosthog()
|
||||
if (!ph._isIdentified()) return
|
||||
ph.reset()
|
||||
}
|
||||
|
||||
export type useAnalyticsProps = {
|
||||
@@ -22,18 +33,26 @@ export type useAnalyticsProps = {
|
||||
|
||||
export const useAnalytics = ({ id, host, isDisabled }: useAnalyticsProps) => {
|
||||
const [location] = useLocation()
|
||||
const { user } = useUser()
|
||||
|
||||
useEffect(() => {
|
||||
if (!id || !host || isDisabled) return
|
||||
if (posthog.__loaded) return
|
||||
posthog.init(id, {
|
||||
api_host: host,
|
||||
person_profiles: 'always',
|
||||
getPosthog().then((ph) => {
|
||||
if (ph.__loaded) return
|
||||
ph.init(id, { api_host: host, person_profiles: 'always' })
|
||||
})
|
||||
}, [id, host, isDisabled])
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return
|
||||
startAnalyticsSession(user)
|
||||
}, [user])
|
||||
|
||||
// From PostHog tutorial on PageView tracking in a Single Page Application (SPA) context.
|
||||
useEffect(() => {
|
||||
posthog.capture('$pageview')
|
||||
getPosthog().then((ph) => {
|
||||
ph.capture('$pageview')
|
||||
})
|
||||
}, [location])
|
||||
|
||||
return null
|
||||
|
||||
@@ -2,16 +2,7 @@ import { useQuery } from '@tanstack/react-query'
|
||||
import { keys } from '@/api/queryKeys'
|
||||
import { fetchUser } from './fetchUser'
|
||||
import { type ApiUser } from './ApiUser'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import {
|
||||
startAnalyticsSession,
|
||||
terminateAnalyticsSession,
|
||||
} from '@/features/analytics/hooks/useAnalytics'
|
||||
import {
|
||||
initializeSupportSession,
|
||||
terminateSupportSession,
|
||||
} from '@/features/support/hooks/useSupport'
|
||||
import { logoutUrl } from '../utils/logoutUrl'
|
||||
import { useMemo } from 'react'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
|
||||
/**
|
||||
@@ -45,19 +36,6 @@ export const useUser = (
|
||||
enabled: !isConfigLoading,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (query?.data) {
|
||||
startAnalyticsSession(query.data)
|
||||
initializeSupportSession(query.data)
|
||||
}
|
||||
}, [query.data])
|
||||
|
||||
const logout = () => {
|
||||
terminateAnalyticsSession()
|
||||
terminateSupportSession()
|
||||
window.location.href = logoutUrl()
|
||||
}
|
||||
|
||||
const isLoggedIn =
|
||||
query.status === 'success' ? query.data !== false : undefined
|
||||
const isLoggedOut = isLoggedIn === false
|
||||
@@ -67,6 +45,5 @@ export const useUser = (
|
||||
user: isLoggedOut ? undefined : (query.data as ApiUser | undefined),
|
||||
isLoggedIn,
|
||||
isLoading: query.isLoading,
|
||||
logout,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { apiUrl } from '@/api/apiUrl'
|
||||
import { terminateSupportSession } from '@/features/support/hooks/useSupport'
|
||||
import { terminateAnalyticsSession } from '@/features/analytics/hooks/useAnalytics'
|
||||
|
||||
const logoutUrl = () => {
|
||||
return apiUrl('/logout')
|
||||
}
|
||||
|
||||
export const logout = async () => {
|
||||
await terminateAnalyticsSession()
|
||||
terminateSupportSession()
|
||||
window.location.href = logoutUrl()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { apiUrl } from '@/api/apiUrl'
|
||||
|
||||
export const logoutUrl = () => {
|
||||
return apiUrl('/logout')
|
||||
}
|
||||
@@ -3,12 +3,13 @@ import { useLanguageLabels } from '@/i18n/useLanguageLabels'
|
||||
import { A, Badge, Dialog, type DialogProps, Field, H, P } from '@/primitives'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
import { LoginButton } from '@/components/LoginButton'
|
||||
import { logout } from '@/features/auth/utils/logout'
|
||||
|
||||
export type SettingsDialogProps = Pick<DialogProps, 'isOpen' | 'onOpenChange'>
|
||||
|
||||
export const SettingsDialog = (props: SettingsDialogProps) => {
|
||||
const { t, i18n } = useTranslation('settings')
|
||||
const { user, isLoggedIn, logout } = useUser()
|
||||
const { user, isLoggedIn } = useUser()
|
||||
const { languagesList, currentLanguage } = useLanguageLabels()
|
||||
const userDisplay =
|
||||
user?.full_name && user?.email
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useState } from 'react'
|
||||
import { LoginButton } from '@/components/LoginButton'
|
||||
import { useRenameParticipant } from '@/features/rooms/api/renameParticipant'
|
||||
import { saveUsername } from '@/stores/userChoices'
|
||||
import { logout } from '@/features/auth/utils/logout'
|
||||
|
||||
export type AccountTabProps = Pick<DialogProps, 'onOpenChange'> &
|
||||
Pick<TabPanelProps, 'id'>
|
||||
@@ -16,7 +17,7 @@ export type AccountTabProps = Pick<DialogProps, 'onOpenChange'> &
|
||||
export const AccountTab = ({ id, onOpenChange }: AccountTabProps) => {
|
||||
const { t } = useTranslation('settings')
|
||||
const room = useRoomContext()
|
||||
const { user, isLoggedIn, logout } = useUser()
|
||||
const { user, isLoggedIn } = useUser()
|
||||
|
||||
const { renameParticipant } = useRenameParticipant()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Crisp } from 'crisp-sdk-web'
|
||||
import { ApiUser } from '@/features/auth/api/ApiUser'
|
||||
import { type ApiUser } from '@/features/auth/api/ApiUser'
|
||||
import { useUser } from '@/features/auth/api/useUser'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
|
||||
export const initializeSupportSession = (user: ApiUser) => {
|
||||
@@ -23,12 +24,19 @@ export type useSupportProps = {
|
||||
|
||||
// Configure Crisp chat for real-time support across all pages.
|
||||
export const useSupport = ({ id, isDisabled }: useSupportProps) => {
|
||||
const { user } = useUser()
|
||||
|
||||
useEffect(() => {
|
||||
if (!id || Crisp.isCrispInjected() || isDisabled) return
|
||||
Crisp.configure(id)
|
||||
Crisp.setHideOnMobile(true)
|
||||
}, [id, isDisabled])
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return
|
||||
initializeSupportSession(user)
|
||||
}, [user])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { LoginButton } from '@/components/LoginButton'
|
||||
import { VisualOnlyTooltip } from '@/primitives/VisualOnlyTooltip'
|
||||
|
||||
import { useLoginHint } from '@/hooks/useLoginHint'
|
||||
import { logout } from '@/features/auth/utils/logout'
|
||||
|
||||
const Logo = () => (
|
||||
<img
|
||||
@@ -90,7 +91,7 @@ export const Header = () => {
|
||||
const isAccessibility = useMatchesRoute('accessibility')
|
||||
const isTermsOfService = useMatchesRoute('termsOfService')
|
||||
const isRoom = useMatchesRoute('room')
|
||||
const { user, isLoggedIn, logout } = useUser()
|
||||
const { user, isLoggedIn } = useUser()
|
||||
const userLabel = user?.full_name || user?.email
|
||||
const loggedInTooltip = t('loggedInUserTooltip')
|
||||
const loggedInAriaLabel = userLabel
|
||||
|
||||
Reference in New Issue
Block a user