mirror of
https://github.com/langgenius/dify.git
synced 2026-09-19 02:07:44 +08:00
refactor(web): sync amplitude from profile query (#40472)
This commit is contained in:
@@ -1,13 +1,70 @@
|
||||
'use client'
|
||||
|
||||
import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
|
||||
import type { GetWorkspacesCurrentSummaryResponse } from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
|
||||
import { flushRegistrationSuccess } from '@/app/components/base/amplitude/registration-tracking'
|
||||
import { useAmplitudeInitialized } from '@/app/components/base/amplitude/use-amplitude-initialized'
|
||||
import { useAnalyticsConsent } from '@/app/components/base/analytics-consent/consent-store'
|
||||
import { amplitudeIdentitySyncAtom } from '@/context/amplitude-identity-sync'
|
||||
import { currentWorkspaceAtom } from '@/context/workspace-state'
|
||||
import { zendeskConversationSyncAtom } from '@/context/zendesk-conversation-sync'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
|
||||
type AmplitudeProperties = Record<string, string | number | boolean>
|
||||
|
||||
function buildAmplitudeProperties({
|
||||
currentWorkspace,
|
||||
userProfile,
|
||||
}: {
|
||||
currentWorkspace: GetWorkspacesCurrentSummaryResponse
|
||||
userProfile: GetAccountProfileResponse
|
||||
}) {
|
||||
const properties: AmplitudeProperties = {
|
||||
email: userProfile.email,
|
||||
name: userProfile.name,
|
||||
has_password: userProfile.is_password_set,
|
||||
}
|
||||
|
||||
if (currentWorkspace.id) {
|
||||
properties.workspace_id = currentWorkspace.id
|
||||
properties.workspace_name = currentWorkspace.name
|
||||
if (currentWorkspace.plan) properties.workspace_plan = currentWorkspace.plan
|
||||
properties.workspace_role = currentWorkspace.role
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
function AmplitudeIdentitySync() {
|
||||
useAtomValue(amplitudeIdentitySyncAtom)
|
||||
const { data: userProfile } = useSuspenseQuery({
|
||||
...userProfileQueryOptions(),
|
||||
select: (data) => data.profile,
|
||||
})
|
||||
const currentWorkspace = useAtomValue(currentWorkspaceAtom)
|
||||
const lastIdentityRef = useRef<string | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
if (!userProfile.id) return
|
||||
|
||||
const properties = buildAmplitudeProperties({
|
||||
currentWorkspace,
|
||||
userProfile,
|
||||
})
|
||||
const identity = JSON.stringify({
|
||||
userId: userProfile.email,
|
||||
properties,
|
||||
})
|
||||
|
||||
if (identity === lastIdentityRef.current) return
|
||||
|
||||
setUserId(userProfile.email)
|
||||
setUserProperties(properties)
|
||||
flushRegistrationSuccess()
|
||||
lastIdentityRef.current = identity
|
||||
}, [currentWorkspace, userProfile])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -565,5 +565,42 @@ describe('Console bootstrap', () => {
|
||||
expect(flushRegistrationSuccess).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('should resync Amplitude only when identity properties change', async () => {
|
||||
const { queryClient, rerender } = renderConsoleBootstrap()
|
||||
|
||||
await waitFor(() => expect(setUserProperties).toHaveBeenCalledTimes(1))
|
||||
|
||||
rerender(
|
||||
<JotaiProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TestQueryClientHydrator queryClient={queryClient}>
|
||||
<Suspense fallback={<span>loading</span>}>
|
||||
<ExternalServiceSync />
|
||||
<ConsoleBootstrapProbe />
|
||||
</Suspense>
|
||||
</TestQueryClientHydrator>
|
||||
</QueryClientProvider>
|
||||
</JotaiProvider>,
|
||||
)
|
||||
expect(setUserProperties).toHaveBeenCalledTimes(1)
|
||||
|
||||
act(() => {
|
||||
queryClient.setQueryData(['user-profile'], {
|
||||
...mockUserProfileResponseState.data,
|
||||
profile: {
|
||||
...mockUserProfileResponseState.data.profile,
|
||||
name: 'Updated User',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(setUserProperties).toHaveBeenCalledTimes(2)
|
||||
expect(setUserProperties).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ name: 'Updated User' }),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
|
||||
import type { GetWorkspacesCurrentSummaryResponse } from '@dify/contracts/api/console/workspaces/types.gen'
|
||||
import { atom } from 'jotai'
|
||||
import { atomEffect } from 'jotai-effect'
|
||||
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
|
||||
import { flushRegistrationSuccess } from '@/app/components/base/amplitude/registration-tracking'
|
||||
import { userProfileAtom } from './account-state'
|
||||
import { currentWorkspaceAtom } from './workspace-state'
|
||||
|
||||
type AmplitudeProperties = Record<string, string | number | boolean>
|
||||
|
||||
const amplitudeIdentityAtom = atom<string | undefined>(undefined)
|
||||
|
||||
function buildAmplitudeProperties({
|
||||
currentWorkspace,
|
||||
userProfile,
|
||||
}: {
|
||||
currentWorkspace: GetWorkspacesCurrentSummaryResponse
|
||||
userProfile: GetAccountProfileResponse
|
||||
}) {
|
||||
const properties: AmplitudeProperties = {
|
||||
email: userProfile.email,
|
||||
name: userProfile.name,
|
||||
has_password: userProfile.is_password_set,
|
||||
}
|
||||
|
||||
if (currentWorkspace.id) {
|
||||
properties.workspace_id = currentWorkspace.id
|
||||
properties.workspace_name = currentWorkspace.name
|
||||
if (currentWorkspace.plan) properties.workspace_plan = currentWorkspace.plan
|
||||
properties.workspace_role = currentWorkspace.role
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
export const amplitudeIdentitySyncAtom = atomEffect((get, set) => {
|
||||
const userProfile = get(userProfileAtom)
|
||||
const currentWorkspace = get(currentWorkspaceAtom)
|
||||
|
||||
if (!userProfile.id) return
|
||||
|
||||
const properties = buildAmplitudeProperties({
|
||||
currentWorkspace,
|
||||
userProfile,
|
||||
})
|
||||
const identity = JSON.stringify({
|
||||
userId: userProfile.email,
|
||||
properties,
|
||||
})
|
||||
|
||||
if (identity === get.peek(amplitudeIdentityAtom)) return
|
||||
|
||||
setUserId(userProfile.email)
|
||||
setUserProperties(properties)
|
||||
flushRegistrationSuccess()
|
||||
set(amplitudeIdentityAtom, identity)
|
||||
})
|
||||
Reference in New Issue
Block a user