diff --git a/apps/web-tss/src/api/buster_rest/users/permissions/queryRequests.ts b/apps/web-tss/src/api/buster_rest/users/permissions/queryRequests.ts index f1e666baa..6246d6fe1 100644 --- a/apps/web-tss/src/api/buster_rest/users/permissions/queryRequests.ts +++ b/apps/web-tss/src/api/buster_rest/users/permissions/queryRequests.ts @@ -68,10 +68,13 @@ export const prefetchGetUserAttributes = async (userId: string, queryClientProp? export const useGetUserTeams = ({ userId }: { userId: string }) => { const queryFn = async () => getUserTeams({ userId }); - return useQuery({ + const { data } = useQuery({ ...userQueryKeys.userGetUserTeams(userId), queryFn, + enabled: !!userId, + notifyOnChangeProps: ['data'], }); + return data; }; export const prefetchGetUserTeams = async (userId: string, queryClientProp?: QueryClient) => { diff --git a/apps/web-tss/src/components/ui/report/elements/MediaEmbedNode.tsx b/apps/web-tss/src/components/ui/report/elements/MediaEmbedNode.tsx index 130ecb1f7..0da7d8ccd 100644 --- a/apps/web-tss/src/components/ui/report/elements/MediaEmbedNode.tsx +++ b/apps/web-tss/src/components/ui/report/elements/MediaEmbedNode.tsx @@ -38,7 +38,7 @@ const parseGenericUrl: EmbedUrlParser = (url: string) => { const urlParsers: EmbedUrlParser[] = [parseTwitterUrl, parseVideoUrl, parseGenericUrl]; const ACCEPTED_DOMAINS = [ - process.env.NEXT_PUBLIC_URL, + process.env.VITE_PUBLIC_URL, 'twitter.com', 'x.com', 'youtube.com', diff --git a/apps/web-tss/src/context/Posthog/BusterPosthogProvider.tsx b/apps/web-tss/src/context/Posthog/BusterPosthogProvider.tsx new file mode 100644 index 000000000..edf911f58 --- /dev/null +++ b/apps/web-tss/src/context/Posthog/BusterPosthogProvider.tsx @@ -0,0 +1,112 @@ +import { isServer } from '@tanstack/react-query'; +import { ClientOnly } from '@tanstack/react-router'; +import type { PostHogConfig } from 'posthog-js'; +import React, { type PropsWithChildren, useEffect, useState } from 'react'; +import { useGetUserTeams } from '@/api/buster_rest/users'; +import { + useGetUserBasicInfo, + useGetUserOrganization, +} from '@/api/buster_rest/users/useGetUserInfo'; +import { isDev } from '@/config/dev'; + +const POSTHOG_KEY = process.env.VITE_PUBLIC_POSTHOG_KEY; +const DEBUG_POSTHOG = false; + +export const BusterPosthogProvider: React.FC = ({ children }) => { + if ((isDev && !DEBUG_POSTHOG) || !POSTHOG_KEY) { + return <>{children}; + } + + return {children}; +}; +BusterPosthogProvider.displayName = 'BusterPosthogProvider'; + +const options: Partial = { + api_host: process.env.VITE_PUBLIC_POSTHOG_HOST, + person_profiles: 'always', + session_recording: { + recordBody: true, + }, + + loaded: () => { + console.log( + '%c🚀 Welcome to Buster', + 'background: linear-gradient(to right, #a21caf, #8b1cb1, #6b21a8); color: white; font-size: 16px; font-weight: bold; padding: 10px; border-radius: 5px;' + ); + console.log( + '%cBuster is your open-source data analytics platform. Found a bug? The code is open-source! Report it at https://github.com/buster-so/buster. Better yet, fix it yourself and send a PR.', + 'background: #6b21a8; color: white; font-size: 10px; font-weight: normal; padding: 8px; border-radius: 4px;' + ); + }, +}; + +const PosthogWrapper: React.FC = ({ children }) => { + const user = useGetUserBasicInfo(); + const userTeams = useGetUserTeams({ userId: user?.id ?? '' }); + const userOrganizations = useGetUserOrganization(); + const team = userTeams?.[0]; + + const [posthogModules, setPosthogModules] = useState<{ + posthog: typeof import('posthog-js').default; + PostHogProvider: typeof import('posthog-js/react').PostHogProvider; + } | null>(null); + const [isLoading, setIsLoading] = useState(true); + + // Async load posthog-js dependencies + useEffect(() => { + const loadPosthogModules = async () => { + try { + const [{ default: posthog }, { PostHogProvider }] = await Promise.all([ + import('posthog-js'), + import('posthog-js/react'), + ]); + + setPosthogModules({ posthog, PostHogProvider }); + } catch (error) { + console.error('Failed to load PostHog modules:', error); + } finally { + setIsLoading(false); + } + }; + + loadPosthogModules(); + }, []); + + // Initialize PostHog when modules are loaded and user data is available + useEffect(() => { + if (POSTHOG_KEY && !isServer && user && posthogModules?.posthog && team) { + const { posthog } = posthogModules; + + if (posthog.__loaded) { + return; + } + + posthog.init(POSTHOG_KEY, options); + + const email = user.email; + posthog.identify(email, { + user, + organization: userOrganizations, + team, + }); + posthog.group(team?.id, team?.name); + } + }, [user?.id, team?.id, posthogModules]); + + // Show children while loading or if modules failed to load + if (isLoading || !posthogModules) { + return <>{children}; + } + + const { PostHogProvider } = posthogModules; + + if (isServer) { + return <>{children}; + } + + return ( + + {children} + + ); +}; diff --git a/apps/web-tss/src/context/Posthog/index.ts b/apps/web-tss/src/context/Posthog/index.ts new file mode 100644 index 000000000..4654ce884 --- /dev/null +++ b/apps/web-tss/src/context/Posthog/index.ts @@ -0,0 +1 @@ +export * from './BusterPosthogProvider'; diff --git a/apps/web-tss/src/context/Providers.tsx b/apps/web-tss/src/context/Providers.tsx index bbe0ab25f..bffff375f 100644 --- a/apps/web-tss/src/context/Providers.tsx +++ b/apps/web-tss/src/context/Providers.tsx @@ -1,6 +1,7 @@ import type { QueryClient } from '@tanstack/react-query'; import type React from 'react'; import type { PropsWithChildren } from 'react'; +import { BusterPosthogProvider } from '@/context/Posthog'; import { BusterStyleProvider } from './BusterStyles'; import { QueryPersister } from './Query/QueryProvider'; import { @@ -8,22 +9,6 @@ import { type SupabaseContextType, } from './Supabase/SupabaseContextProvider'; -// import type { UseSupabaseUserContextType } from '@/lib/supabase'; -// import { BusterAssetsProvider } from './Assets/BusterAssetsProvider'; -// import { AppLayoutProvider } from './BusterAppLayout'; -// import { BusterReactQueryProvider } from './BusterReactQuery/BusterReactQueryAndApi'; -// import { BusterNewChatProvider } from './Chats'; -// import { BusterPosthogProvider } from './Posthog'; -// import { RoutePrefetcher } from './RoutePrefetcher'; -// import { SupabaseContextProvider } from './Supabase/SupabaseContextProvider'; -// import { BusterUserConfigProvider } from './Users/BusterUserConfigProvider'; - -// scan({ -// enabled: true, -// log: true, // logs render info to console (default: false) -// clearLog: false // clears the console per group of renders (default: false) -// }); - type RootProvidersProps = PropsWithChildren; export const RootProviders: React.FC = ({ @@ -35,21 +20,9 @@ export const RootProviders: React.FC = ({ return ( - {children} - {/* - - - - - - {children} - - - - - - - */} + + {children} + ); diff --git a/apps/web-tss/src/env.ts b/apps/web-tss/src/env.ts index c6d2ad68d..d9f9f4f45 100644 --- a/apps/web-tss/src/env.ts +++ b/apps/web-tss/src/env.ts @@ -15,7 +15,6 @@ export const env = createEnv({ clientPrefix: 'VITE_', client: { - VITE_APP_TITLE: z.string().min(1).optional(), VITE_PUBLIC_API_URL: z.string().url(), VITE_PUBLIC_API2_URL: z.string().url(), VITE_PUBLIC_WEB_SOCKET_URL: z.string().url().optional(),