✨(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:
committed by
aleb_the_flash
parent
e591d09b00
commit
8cc2cc83c6
@@ -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>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const keys = {
|
||||
user: 'user',
|
||||
room: 'room',
|
||||
config: 'config',
|
||||
}
|
||||
|
||||
26
src/frontend/src/api/useConfig.ts
Normal file
26
src/frontend/src/api/useConfig.ts
Normal 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,
|
||||
})
|
||||
}
|
||||
20
src/frontend/src/components/AppInitialization.tsx
Normal file
20
src/frontend/src/components/AppInitialization.tsx
Normal 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
|
||||
}
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user