77 TenantScope ,
88} from '@logto/schemas' ;
99import { conditionalArray } from '@silverhand/essentials' ;
10- import { useContext , useMemo } from 'react' ;
10+ import { PostHogProvider , usePostHog } from 'posthog-js/react' ;
11+ import { useContext , useEffect , useMemo } from 'react' ;
1112import { Helmet } from 'react-helmet' ;
1213import { createBrowserRouter , RouterProvider } from 'react-router-dom' ;
1314
@@ -21,7 +22,7 @@ import 'react-color-palette/css';
2122
2223import CloudAppRoutes from '@/cloud/AppRoutes' ;
2324import AppLoading from '@/components/AppLoading' ;
24- import { isCloud } from '@/consts/env' ;
25+ import { isCloud , postHogHost , postHogKey } from '@/consts/env' ;
2526import { cloudApi , getManagementApi , meApi } from '@/consts/resources' ;
2627import { ConsoleRoutes } from '@/containers/ConsoleRoutes' ;
2728
@@ -71,8 +72,6 @@ export default App;
7172 * different components.
7273 */
7374function Providers ( ) {
74- const { currentTenantId } = useContext ( TenantsContext ) ;
75-
7675 // For Cloud, we use Management API proxy for accessing tenant data.
7776 // For OSS, we directly call the tenant API with the default tenant API resource.
7877 const resources = useMemo (
@@ -103,58 +102,85 @@ function Providers() {
103102 ) ;
104103
105104 return (
106- < LogtoProvider
107- unstable_enableCache
108- config = { {
109- endpoint : adminTenantEndpoint . href ,
110- appId : adminConsoleApplicationId ,
111- resources,
112- scopes,
113- prompt : [ Prompt . Login , Prompt . Consent ] ,
105+ < PostHogProvider
106+ apiKey = { postHogKey ?? '' } // Empty key will disable PostHog
107+ options = { {
108+ api_host : postHogHost ,
109+ defaults : '2025-05-24' ,
114110 } }
115111 >
116- < AppThemeProvider >
117- < Helmet titleTemplate = { `%s - ${ mainTitle } ` } defaultTitle = { mainTitle } />
118- < Toast />
119- < AppConfirmModalProvider >
120- < ErrorBoundary >
121- < LogtoErrorBoundary >
122- { /**
123- * If it's not Cloud (OSS), render the tenant app container directly since only default tenant is available;
124- * if it's Cloud, render the tenant app container only when a tenant ID is available (in a tenant context).
125- */ }
126- { ! isCloud || currentTenantId ? (
112+ < LogtoProvider
113+ unstable_enableCache
114+ config = { {
115+ endpoint : adminTenantEndpoint . href ,
116+ appId : adminConsoleApplicationId ,
117+ resources,
118+ scopes,
119+ prompt : [ Prompt . Login , Prompt . Consent ] ,
120+ } }
121+ >
122+ < AppThemeProvider >
123+ < Helmet titleTemplate = { `%s - ${ mainTitle } ` } defaultTitle = { mainTitle } />
124+ < Toast />
125+ < AppConfirmModalProvider >
126+ < ErrorBoundary >
127+ < LogtoErrorBoundary >
127128 < AppDataProvider >
128- < AppRoutes />
129+ < GlobalScripts />
130+ < Content />
129131 </ AppDataProvider >
130- ) : (
131- < CloudAppRoutes />
132- ) }
133- </ LogtoErrorBoundary >
134- </ ErrorBoundary >
135- </ AppConfirmModalProvider >
136- </ AppThemeProvider >
137- </ LogtoProvider >
132+ </ LogtoErrorBoundary >
133+ </ ErrorBoundary >
134+ </ AppConfirmModalProvider >
135+ </ AppThemeProvider >
136+ </ LogtoProvider >
137+ </ PostHogProvider >
138138 ) ;
139139}
140140
141- /** Renders different routes based on the user's onboarding status. */
142- function AppRoutes ( ) {
141+ function Content ( ) {
143142 const { tenantEndpoint } = useContext ( AppDataContext ) ;
144- const { isLoaded } = useCurrentUser ( ) ;
143+ const { isLoaded, user } = useCurrentUser ( ) ;
145144 const { isAuthenticated } = useLogto ( ) ;
145+ const { currentTenantId, currentTenant } = useContext ( TenantsContext ) ;
146+ const postHog = usePostHog ( ) ;
147+
148+ useEffect ( ( ) => {
149+ if ( isLoaded ) {
150+ postHog . identify ( user ?. id ) ;
151+ }
152+ // We don't reset user info here because this component includes some anonymous pages.
153+ // Resetting user info may cause issues when the user switches between anonymous and
154+ // authenticated pages.
155+ // Reset user info in the sign-out logic instead.
156+ } , [ isLoaded , postHog , user ] ) ;
157+
158+ useEffect ( ( ) => {
159+ if ( currentTenant ) {
160+ postHog . group ( 'tenant' , currentTenantId , {
161+ name : currentTenant . name ,
162+ } ) ;
163+ } else if ( currentTenantId ) {
164+ postHog . group ( 'tenant' , currentTenantId ) ;
165+ } else {
166+ postHog . resetGroups ( ) ;
167+ }
168+ } , [ postHog , currentTenantId , currentTenant ] ) ;
146169
147- // Authenticated user should load onboarding data before rendering the app.
148- // This looks weird and it will be refactored soon by merging the onboarding
149- // routes with the console routes.
150- if ( ! tenantEndpoint || ( isCloud && isAuthenticated && ! isLoaded ) ) {
151- return < AppLoading /> ;
170+ /**
171+ * If it's not Cloud (OSS), render the tenant app container directly since only default tenant is available;
172+ * if it's Cloud, render the tenant app container only when a tenant ID is available (in a tenant context).
173+ */
174+ if ( ! isCloud || currentTenantId ) {
175+ // Authenticated user should load onboarding data before rendering the app.
176+ // This looks weird and it can be refactored by merging the onboarding
177+ // routes with the console routes.
178+ if ( ! tenantEndpoint || ( isCloud && isAuthenticated && ! isLoaded ) ) {
179+ return < AppLoading /> ;
180+ }
181+
182+ return < ConsoleRoutes /> ;
152183 }
153184
154- return (
155- < >
156- < GlobalScripts />
157- < ConsoleRoutes />
158- </ >
159- ) ;
185+ return < CloudAppRoutes /> ;
160186}
0 commit comments