(frontend) initialize application from remote configs

Query API to get the App's configs.
Replacement for env variables, thus with a single image,
we can dynamically update frontend's behavior.

Code inspired by Marsha. Not sure having a single component
returning null is a good idea, to be discussed.
This commit is contained in:
lebaudantoine
2024-09-24 23:00:01 +02:00
committed by aleb_the_flash
parent e591d09b00
commit 8cc2cc83c6
6 changed files with 67 additions and 33 deletions

View File

@@ -11,23 +11,15 @@ import { Layout } from './layout/Layout'
import { NotFoundScreen } from './components/NotFoundScreen'
import { routes } from './routes'
import './i18n/init'
import { silenceLiveKitLogs } from '@/utils/livekit.ts'
import { queryClient } from '@/api/queryClient'
import { useAnalytics } from '@/features/analytics/hooks/useAnalytics'
import { useSupport } from '@/features/support/hooks/useSupport'
import { AppInitialization } from '@/components/AppInitialization'
function App() {
const { i18n } = useTranslation()
useLang(i18n.language)
const isProduction = import.meta.env.PROD
silenceLiveKitLogs(isProduction)
useAnalytics()
useSupport()
return (
<QueryClientProvider client={queryClient}>
<AppInitialization />
<Suspense fallback={null}>
<I18nProvider locale={i18n.language}>
<Layout>

View File

@@ -1,4 +1,5 @@
export const keys = {
user: 'user',
room: 'room',
config: 'config',
}

View File

@@ -0,0 +1,26 @@
import { fetchApi } from './fetchApi'
import { keys } from './queryKeys'
import { useQuery } from '@tanstack/react-query'
export interface ApiConfig {
analytics?: {
id: string
host: string
}
support?: {
id: string
}
silence_livekit_debug_logs?: boolean
}
const fetchConfig = (): Promise<ApiConfig> => {
return fetchApi<ApiConfig>(`config/`)
}
export const useConfig = () => {
return useQuery({
queryKey: [keys.config],
queryFn: fetchConfig,
staleTime: Infinity,
})
}

View File

@@ -0,0 +1,20 @@
import { silenceLiveKitLogs } from '@/utils/livekit'
import { useConfig } from '@/api/useConfig'
import { useAnalytics } from '@/features/analytics/hooks/useAnalytics'
import { useSupport } from '@/features/support/hooks/useSupport'
export const AppInitialization = () => {
const { data } = useConfig()
const {
analytics = {},
support = {},
silence_livekit_debug_logs = false,
} = data || {}
useAnalytics(analytics)
useSupport(support)
silenceLiveKitLogs(silence_livekit_debug_logs)
return null
}

View File

@@ -3,9 +3,6 @@ import { useLocation } from 'wouter'
import posthog from 'posthog-js'
import { ApiUser } from '@/features/auth/api/ApiUser'
const ANALYTICS_ID = 'phc_RPYko028Oqtj0c9exLIWwrlrjLxSdxT0ntW0Lam4iom'
const ANALYTICS_HOST = 'https://eu.i.posthog.com'
export const startAnalyticsSession = (data: ApiUser) => {
if (posthog._isIdentified()) return
const { id, email } = data
@@ -17,21 +14,21 @@ export const terminateAnalyticsSession = () => {
posthog.reset()
}
export const useAnalytics = () => {
export type useAnalyticsProps = {
id?: string
host?: string
}
export const useAnalytics = ({ id, host }: useAnalyticsProps) => {
const [location] = useLocation()
const isProduction = import.meta.env.PROD
useEffect(() => {
// We're on a free tier, so we need to limit the number of events we send to PostHog.
// Be frugal with event tracking, even though we could filter them out later if necessary.
if (!isProduction) return
if (!id || !host) return
if (posthog.__loaded) return
posthog.init(ANALYTICS_ID, {
api_host: ANALYTICS_HOST,
posthog.init(id, {
api_host: host,
person_profiles: 'always',
})
}, [isProduction])
}, [id, host])
// From PostHog tutorial on PageView tracking in a Single Page Application (SPA) context.
useEffect(() => {

View File

@@ -2,8 +2,6 @@ import { useEffect } from 'react'
import { Crisp } from 'crisp-sdk-web'
import { ApiUser } from '@/features/auth/api/ApiUser'
const SUPPORT_ID = '58ea6697-8eba-4492-bc59-ad6562585041'
export const initializeSupportSession = (user: ApiUser) => {
if (!Crisp.isCrispInjected()) return
const { id, email } = user
@@ -17,17 +15,17 @@ export const terminateSupportSession = () => {
Crisp.session.reset()
}
export type useSupportProps = {
id?: string
}
// Configure Crisp chat for real-time support across all pages.
export const useSupport = () => {
export const useSupport = ({ id }: useSupportProps) => {
useEffect(() => {
if (!SUPPORT_ID) {
console.warn('Crisp Website ID is not set')
return
}
if (Crisp.isCrispInjected()) return
Crisp.configure(SUPPORT_ID)
if (!id || Crisp.isCrispInjected()) return
Crisp.configure(id)
Crisp.setHideOnMobile(true)
}, [])
}, [id])
return null
}