diff --git a/apps/web/src/hooks/useAuth.ts b/apps/web/src/hooks/useAuth.ts index 4ec18293df7..08248436d1c 100644 --- a/apps/web/src/hooks/useAuth.ts +++ b/apps/web/src/hooks/useAuth.ts @@ -116,7 +116,17 @@ export function useAuth() { }, [navigate, queryClient, segment]); const redirectTo = useCallback( - ({ url, redirectURL, origin }: { url: string; redirectURL?: string; origin?: string }) => { + ({ + url, + redirectURL, + origin, + anonymousId, + }: { + url: string; + redirectURL?: string; + origin?: string; + anonymousId?: string | null; + }) => { const finalURL = new URL(url, window.location.origin); if (redirectURL) { @@ -127,6 +137,10 @@ export function useAuth() { finalURL.searchParams.append('origin', origin); } + if (anonymousId) { + finalURL.searchParams.append('anonymous_id', anonymousId); + } + // Note: Do not use react-router-dom. The version we have doesn't do instant cross origin redirects. window.location.replace(finalURL.href); }, @@ -139,8 +153,12 @@ export function useAuth() { ); const redirectToSignUp = useCallback( - ({ redirectURL, origin }: { redirectURL?: string; origin?: string } = {}) => - redirectTo({ url: ROUTES.AUTH_SIGNUP, redirectURL, origin }), + ({ + redirectURL, + origin, + anonymousId, + }: { redirectURL?: string; origin?: string; anonymousId?: string | null } = {}) => + redirectTo({ url: ROUTES.AUTH_SIGNUP, redirectURL, origin, anonymousId }), [redirectTo] ); diff --git a/apps/web/src/pages/auth/components/SignUpForm.tsx b/apps/web/src/pages/auth/components/SignUpForm.tsx index 46c56f3d6a2..c20b105c2a3 100644 --- a/apps/web/src/pages/auth/components/SignUpForm.tsx +++ b/apps/web/src/pages/auth/components/SignUpForm.tsx @@ -14,6 +14,8 @@ import { useAcceptInvite } from './useAcceptInvite'; import { PasswordRequirementPopover } from './PasswordRequirementPopover'; import { ROUTES } from '../../../constants/routes'; import { OAuth } from './OAuth'; +import { useSegment } from '../../../components/providers/SegmentProvider'; +import { useStudioState } from '../../../studio/hooks'; type SignUpFormProps = { invitationToken?: string; @@ -37,6 +39,8 @@ export function SignUpForm({ invitationToken, email }: SignUpFormProps) { const { isLoading: isAcceptInviteLoading, acceptInvite } = useAcceptInvite(); const { params, isFromVercel } = useVercelParams(); const loginLink = isFromVercel ? `${ROUTES.AUTH_LOGIN}?${params.toString()}` : ROUTES.AUTH_LOGIN; + const segment = useSegment(); + const state = useStudioState(); const { isLoading, mutateAsync, isError, error } = useMutation< { token: string }, @@ -52,6 +56,7 @@ export function SignUpForm({ invitationToken, email }: SignUpFormProps) { const onSubmit = async (data) => { const parsedSearchParams = new URLSearchParams(location.search); const origin = parsedSearchParams.get('origin'); + const anonymousId = parsedSearchParams.get('anonymous_id'); const [firstName, lastName] = data?.fullName.trim().split(' '); const itemData = { @@ -66,6 +71,10 @@ export function SignUpForm({ invitationToken, email }: SignUpFormProps) { const token = (response as any).token; await login(token); + if (state?.anonymousId && anonymousId) { + segment.alias(anonymousId, (response as any).user?.id); + } + if (invitationToken) { const updatedToken = await acceptInvite(invitationToken); if (updatedToken) { diff --git a/apps/web/src/studio/LocalStudioAuthenticator.tsx b/apps/web/src/studio/LocalStudioAuthenticator.tsx index 59f8a2950c9..6eb84726527 100644 --- a/apps/web/src/studio/LocalStudioAuthenticator.tsx +++ b/apps/web/src/studio/LocalStudioAuthenticator.tsx @@ -31,6 +31,7 @@ export function LocalStudioAuthenticator() { // TODO: Refactor this to a smaller size function useEffect(() => { const parsedSearchParams = new URLSearchParams(location.search); + const anonymousId = parsedSearchParams.get('anonymous_id'); // Get the redirect URL of the Local Studio server const redirectURL = parsedSearchParams.get('redirect_url'); @@ -67,7 +68,7 @@ export function LocalStudioAuthenticator() { */ // currentURL.searchParams.append('studio_path_hint', ROUTES.STUDIO_ONBOARDING); - return redirectToSignUp({ redirectURL: currentURL.href, origin: 'cli' }); + return redirectToSignUp({ redirectURL: currentURL.href, origin: 'cli', anonymousId }); } return; @@ -104,7 +105,6 @@ export function LocalStudioAuthenticator() { const localBridgeURL = buildBridgeURL(parsedApplicationOrigin.origin, tunnelPath); const tunnelBridgeURL = buildBridgeURL(tunnelOrigin, tunnelPath); - const anonymousId = parsedSearchParams.get('anonymous_id'); // TODO: Add apiKeys to the IEnvironment interface as they exist in the response // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/apps/web/src/utils/segment.ts b/apps/web/src/utils/segment.ts index 727b5620b56..9d6ac3035cb 100644 --- a/apps/web/src/utils/segment.ts +++ b/apps/web/src/utils/segment.ts @@ -53,6 +53,18 @@ export class SegmentService { this._segment?.identify(user?._id); } + alias(anonymousId: string, userId: string) { + if (!this.isSegmentEnabled()) { + return; + } + + if (this._mixpanelEnabled) { + mixpanel.alias(userId, anonymousId); + } + + this._segment?.alias(userId, anonymousId); + } + setAnonymousId(anonymousId: string) { if (!this.isSegmentEnabled() || !anonymousId) { return; diff --git a/libs/application-generic/src/services/analytics.service.ts b/libs/application-generic/src/services/analytics.service.ts index ef83aa01a6c..e4af7fb75c7 100644 --- a/libs/application-generic/src/services/analytics.service.ts +++ b/libs/application-generic/src/services/analytics.service.ts @@ -109,7 +109,12 @@ export class AnalyticsService { } } - track(name: string, userId: string, data: Record = {}) { + track( + name: string, + userId: string, + data: Record = {}, + anonymousId?: string + ) { if (this.segmentEnabled) { Logger.log( 'Tracking event: ' + name, @@ -123,6 +128,7 @@ export class AnalyticsService { try { this.segment.track({ + anonymousId: userId, userId: userId, event: name, properties: data,