✨(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 { NotFoundScreen } from './components/NotFoundScreen'
|
||||||
import { routes } from './routes'
|
import { routes } from './routes'
|
||||||
import './i18n/init'
|
import './i18n/init'
|
||||||
import { silenceLiveKitLogs } from '@/utils/livekit.ts'
|
|
||||||
import { queryClient } from '@/api/queryClient'
|
import { queryClient } from '@/api/queryClient'
|
||||||
import { useAnalytics } from '@/features/analytics/hooks/useAnalytics'
|
import { AppInitialization } from '@/components/AppInitialization'
|
||||||
import { useSupport } from '@/features/support/hooks/useSupport'
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
useLang(i18n.language)
|
useLang(i18n.language)
|
||||||
|
|
||||||
const isProduction = import.meta.env.PROD
|
|
||||||
silenceLiveKitLogs(isProduction)
|
|
||||||
|
|
||||||
useAnalytics()
|
|
||||||
useSupport()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<AppInitialization />
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<I18nProvider locale={i18n.language}>
|
<I18nProvider locale={i18n.language}>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export const keys = {
|
export const keys = {
|
||||||
user: 'user',
|
user: 'user',
|
||||||
room: 'room',
|
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 posthog from 'posthog-js'
|
||||||
import { ApiUser } from '@/features/auth/api/ApiUser'
|
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) => {
|
export const startAnalyticsSession = (data: ApiUser) => {
|
||||||
if (posthog._isIdentified()) return
|
if (posthog._isIdentified()) return
|
||||||
const { id, email } = data
|
const { id, email } = data
|
||||||
@@ -17,21 +14,21 @@ export const terminateAnalyticsSession = () => {
|
|||||||
posthog.reset()
|
posthog.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAnalytics = () => {
|
export type useAnalyticsProps = {
|
||||||
|
id?: string
|
||||||
|
host?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAnalytics = ({ id, host }: useAnalyticsProps) => {
|
||||||
const [location] = useLocation()
|
const [location] = useLocation()
|
||||||
|
|
||||||
const isProduction = import.meta.env.PROD
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// We're on a free tier, so we need to limit the number of events we send to PostHog.
|
if (!id || !host) return
|
||||||
// Be frugal with event tracking, even though we could filter them out later if necessary.
|
|
||||||
if (!isProduction) return
|
|
||||||
if (posthog.__loaded) return
|
if (posthog.__loaded) return
|
||||||
posthog.init(ANALYTICS_ID, {
|
posthog.init(id, {
|
||||||
api_host: ANALYTICS_HOST,
|
api_host: host,
|
||||||
person_profiles: 'always',
|
person_profiles: 'always',
|
||||||
})
|
})
|
||||||
}, [isProduction])
|
}, [id, host])
|
||||||
|
|
||||||
// From PostHog tutorial on PageView tracking in a Single Page Application (SPA) context.
|
// From PostHog tutorial on PageView tracking in a Single Page Application (SPA) context.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { useEffect } from 'react'
|
|||||||
import { Crisp } from 'crisp-sdk-web'
|
import { Crisp } from 'crisp-sdk-web'
|
||||||
import { ApiUser } from '@/features/auth/api/ApiUser'
|
import { ApiUser } from '@/features/auth/api/ApiUser'
|
||||||
|
|
||||||
const SUPPORT_ID = '58ea6697-8eba-4492-bc59-ad6562585041'
|
|
||||||
|
|
||||||
export const initializeSupportSession = (user: ApiUser) => {
|
export const initializeSupportSession = (user: ApiUser) => {
|
||||||
if (!Crisp.isCrispInjected()) return
|
if (!Crisp.isCrispInjected()) return
|
||||||
const { id, email } = user
|
const { id, email } = user
|
||||||
@@ -17,17 +15,17 @@ export const terminateSupportSession = () => {
|
|||||||
Crisp.session.reset()
|
Crisp.session.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type useSupportProps = {
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
|
||||||
// Configure Crisp chat for real-time support across all pages.
|
// Configure Crisp chat for real-time support across all pages.
|
||||||
export const useSupport = () => {
|
export const useSupport = ({ id }: useSupportProps) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!SUPPORT_ID) {
|
if (!id || Crisp.isCrispInjected()) return
|
||||||
console.warn('Crisp Website ID is not set')
|
Crisp.configure(id)
|
||||||
return
|
|
||||||
}
|
|
||||||
if (Crisp.isCrispInjected()) return
|
|
||||||
Crisp.configure(SUPPORT_ID)
|
|
||||||
Crisp.setHideOnMobile(true)
|
Crisp.setHideOnMobile(true)
|
||||||
}, [])
|
}, [id])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user