Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
958b10ca05 | ||
|
|
0d15e9a825 | ||
|
|
616ea918bf | ||
|
|
568da2ed75 | ||
|
|
961e54ac5c | ||
|
|
540a1f9fec |
@@ -0,0 +1,222 @@
|
||||
import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { createStore } from 'jotai'
|
||||
import { queryClientAtom } from 'jotai-tanstack-query'
|
||||
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
|
||||
import { flushRegistrationSuccess } from '@/app/components/base/amplitude/registration-tracking'
|
||||
import { amplitudeIdentitySyncAtom } from '../amplitude-identity-sync'
|
||||
|
||||
type ProfileQueryData = {
|
||||
profile: GetAccountProfileResponse
|
||||
meta: {
|
||||
currentVersion: string | null
|
||||
currentEnv: string | null
|
||||
}
|
||||
}
|
||||
|
||||
const profileQueryState = vi.hoisted(() => {
|
||||
const state = {
|
||||
resolve: (_value: unknown) => {},
|
||||
queryFn: () =>
|
||||
new Promise<unknown>((resolve) => {
|
||||
state.resolve = resolve
|
||||
}),
|
||||
}
|
||||
return state
|
||||
})
|
||||
|
||||
const mockCurrentWorkspaceResponse = vi.hoisted(() => ({
|
||||
id: 'workspace-1',
|
||||
name: 'Workspace',
|
||||
plan: 'sandbox',
|
||||
status: 'normal',
|
||||
created_at: 1704067200,
|
||||
role: 'editor',
|
||||
trial_credits: 200,
|
||||
trial_credits_used: 0,
|
||||
next_credit_reset_date: 1706745600,
|
||||
custom_config: {},
|
||||
}))
|
||||
|
||||
vi.mock('@/features/account-profile/client', () => ({
|
||||
userProfileQueryOptions: () => ({
|
||||
queryKey: ['user-profile'],
|
||||
queryFn: profileQueryState.queryFn,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleQuery: {
|
||||
workspaces: {
|
||||
current: {
|
||||
post: {
|
||||
key: () => ['current-workspace'],
|
||||
queryOptions: (options: {
|
||||
select?: (workspace?: typeof mockCurrentWorkspaceResponse) => unknown
|
||||
}) => ({
|
||||
queryKey: ['current-workspace'],
|
||||
queryFn: async () => mockCurrentWorkspaceResponse,
|
||||
...options,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/amplitude', () => ({
|
||||
setUserId: vi.fn(),
|
||||
setUserProperties: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/amplitude/registration-tracking', () => ({
|
||||
flushRegistrationSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
function createProfileQueryData(
|
||||
overrides: Partial<GetAccountProfileResponse> = {},
|
||||
): ProfileQueryData {
|
||||
return {
|
||||
profile: {
|
||||
id: 'user-1',
|
||||
name: 'User',
|
||||
email: 'user@example.com',
|
||||
avatar: '',
|
||||
avatar_url: '',
|
||||
is_password_set: true,
|
||||
...overrides,
|
||||
} as GetAccountProfileResponse,
|
||||
meta: {
|
||||
currentVersion: '1.0.0',
|
||||
currentEnv: 'cloud',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createTestStore() {
|
||||
const store = createStore()
|
||||
store.set(
|
||||
queryClientAtom,
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
return store
|
||||
}
|
||||
|
||||
async function flushAsync() {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 0)
|
||||
})
|
||||
}
|
||||
|
||||
// Regression for langgenius/dify#38840: reading a resolved-suspense atom throws while pending.
|
||||
describe('amplitudeIdentitySyncAtom', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('cold profile cache', () => {
|
||||
it('should not throw and should skip sync while the profile query is pending', async () => {
|
||||
const store = createTestStore()
|
||||
|
||||
expect(() => store.sub(amplitudeIdentitySyncAtom, () => {})).not.toThrow()
|
||||
await flushAsync()
|
||||
|
||||
expect(setUserId).not.toHaveBeenCalled()
|
||||
expect(setUserProperties).not.toHaveBeenCalled()
|
||||
expect(flushRegistrationSuccess).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should sync identity when the pending profile query resolves', async () => {
|
||||
const store = createTestStore()
|
||||
store.sub(amplitudeIdentitySyncAtom, () => {})
|
||||
await flushAsync()
|
||||
|
||||
profileQueryState.resolve(createProfileQueryData())
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(setUserId).toHaveBeenCalledWith('user@example.com')
|
||||
})
|
||||
expect(setUserProperties).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email: 'user@example.com',
|
||||
workspace_id: 'workspace-1',
|
||||
workspace_role: 'editor',
|
||||
}),
|
||||
)
|
||||
expect(flushRegistrationSuccess).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should skip sync when the resolved profile has no id', async () => {
|
||||
const store = createTestStore()
|
||||
store.sub(amplitudeIdentitySyncAtom, () => {})
|
||||
await flushAsync()
|
||||
|
||||
profileQueryState.resolve(createProfileQueryData({ id: '', email: '', name: '' }))
|
||||
await flushAsync()
|
||||
|
||||
expect(setUserId).not.toHaveBeenCalled()
|
||||
expect(setUserProperties).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('warm profile cache', () => {
|
||||
it('should sync identity when the profile query is already cached', async () => {
|
||||
const store = createStore()
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
staleTime: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
},
|
||||
})
|
||||
queryClient.setQueryData(['user-profile'], createProfileQueryData())
|
||||
store.set(queryClientAtom, queryClient)
|
||||
|
||||
store.sub(amplitudeIdentitySyncAtom, () => {})
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(setUserId).toHaveBeenCalledWith('user@example.com')
|
||||
})
|
||||
})
|
||||
|
||||
it('should not re-sync identity when the effect re-runs with the same profile', async () => {
|
||||
const store = createStore()
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
staleTime: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
},
|
||||
})
|
||||
queryClient.setQueryData(['user-profile'], createProfileQueryData())
|
||||
queryClient.setQueryData(['current-workspace'], mockCurrentWorkspaceResponse)
|
||||
store.set(queryClientAtom, queryClient)
|
||||
|
||||
store.sub(amplitudeIdentitySyncAtom, () => {})
|
||||
await vi.waitFor(() => {
|
||||
expect(setUserProperties).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ workspace_id: 'workspace-1' }),
|
||||
)
|
||||
})
|
||||
await flushAsync()
|
||||
const settledCallCount = vi.mocked(setUserId).mock.calls.length
|
||||
|
||||
queryClient.setQueryData(
|
||||
['user-profile'],
|
||||
createProfileQueryData({ avatar: 'changed-avatar' }),
|
||||
)
|
||||
await flushAsync()
|
||||
|
||||
expect(setUserId).toHaveBeenCalledTimes(settledCallCount)
|
||||
expect(setUserProperties).toHaveBeenCalledTimes(settledCallCount)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,233 @@
|
||||
import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { createStore } from 'jotai'
|
||||
import { queryClientAtom } from 'jotai-tanstack-query'
|
||||
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
|
||||
import { ZENDESK_FIELD_IDS } from '@/config'
|
||||
import { zendeskConversationSyncAtom } from '../zendesk-conversation-sync'
|
||||
|
||||
type ProfileQueryData = {
|
||||
profile: GetAccountProfileResponse
|
||||
meta: {
|
||||
currentVersion: string | null
|
||||
currentEnv: string | null
|
||||
}
|
||||
}
|
||||
|
||||
const profileQueryState = vi.hoisted(() => {
|
||||
const state = {
|
||||
resolve: (_value: unknown) => {},
|
||||
queryFn: () =>
|
||||
new Promise<unknown>((resolve) => {
|
||||
state.resolve = resolve
|
||||
}),
|
||||
}
|
||||
return state
|
||||
})
|
||||
|
||||
const mockCurrentWorkspaceQueryState = vi.hoisted(() => ({
|
||||
isPending: true,
|
||||
}))
|
||||
|
||||
const mockCurrentWorkspaceResponse = vi.hoisted(() => ({
|
||||
id: 'workspace-1',
|
||||
name: 'Workspace',
|
||||
plan: 'sandbox',
|
||||
status: 'normal',
|
||||
created_at: 1704067200,
|
||||
role: 'editor',
|
||||
trial_credits: 200,
|
||||
trial_credits_used: 0,
|
||||
next_credit_reset_date: 1706745600,
|
||||
custom_config: {},
|
||||
}))
|
||||
|
||||
const mockSystemFeaturesState = vi.hoisted(() => ({
|
||||
data: {
|
||||
branding: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const mockLangGeniusVersionState = vi.hoisted(() => ({
|
||||
data: {
|
||||
version: '1.0.1',
|
||||
release_date: '',
|
||||
release_notes: '',
|
||||
features: {
|
||||
can_replace_logo: false,
|
||||
model_load_balancing_enabled: false,
|
||||
},
|
||||
can_auto_update: false,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/config', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/config')>()
|
||||
return {
|
||||
...actual,
|
||||
ZENDESK_FIELD_IDS: {
|
||||
ENVIRONMENT: 'environment-field',
|
||||
VERSION: 'version-field',
|
||||
EMAIL: 'email-field',
|
||||
WORKSPACE_ID: 'workspace-id-field',
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@/features/account-profile/client', () => ({
|
||||
userProfileQueryOptions: () => ({
|
||||
queryKey: ['user-profile'],
|
||||
queryFn: profileQueryState.queryFn,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/features/system-features/client', () => ({
|
||||
systemFeaturesQueryOptions: () => ({
|
||||
queryKey: ['system-features'],
|
||||
queryFn: async () => mockSystemFeaturesState.data,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleQuery: {
|
||||
workspaces: {
|
||||
current: {
|
||||
post: {
|
||||
key: () => ['current-workspace'],
|
||||
queryOptions: (options: {
|
||||
select?: (workspace?: typeof mockCurrentWorkspaceResponse) => unknown
|
||||
}) => ({
|
||||
queryKey: ['current-workspace'],
|
||||
queryFn: async () => {
|
||||
if (mockCurrentWorkspaceQueryState.isPending) return new Promise(() => {})
|
||||
|
||||
return mockCurrentWorkspaceResponse
|
||||
},
|
||||
...options,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
version: {
|
||||
get: {
|
||||
queryOptions: (options: {
|
||||
enabled?: boolean
|
||||
input?: {
|
||||
query: {
|
||||
current_version: string
|
||||
}
|
||||
}
|
||||
}) => ({
|
||||
queryKey: ['version', options.input?.query.current_version],
|
||||
queryFn: async () => mockLangGeniusVersionState.data,
|
||||
...options,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/zendesk/utils', () => ({
|
||||
setZendeskConversationFields: vi.fn(),
|
||||
}))
|
||||
|
||||
function createProfileQueryData(): ProfileQueryData {
|
||||
return {
|
||||
profile: {
|
||||
id: 'user-1',
|
||||
name: 'User',
|
||||
email: 'user@example.com',
|
||||
avatar: '',
|
||||
avatar_url: '',
|
||||
is_password_set: true,
|
||||
} as GetAccountProfileResponse,
|
||||
meta: {
|
||||
currentVersion: '1.0.0',
|
||||
currentEnv: 'cloud',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createTestStore() {
|
||||
const store = createStore()
|
||||
store.set(
|
||||
queryClientAtom,
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
return store
|
||||
}
|
||||
|
||||
async function flushAsync() {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 0)
|
||||
})
|
||||
}
|
||||
|
||||
// Regression for langgenius/dify#38840: reading resolved-suspense atoms throws while pending.
|
||||
describe('zendeskConversationSyncAtom', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockCurrentWorkspaceQueryState.isPending = true
|
||||
})
|
||||
|
||||
describe('cold caches', () => {
|
||||
it('should not throw and should skip all field syncs while queries are pending', async () => {
|
||||
const store = createTestStore()
|
||||
|
||||
expect(() => store.sub(zendeskConversationSyncAtom, () => {})).not.toThrow()
|
||||
await flushAsync()
|
||||
|
||||
expect(setZendeskConversationFields).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should sync all fields when the pending queries resolve', async () => {
|
||||
mockCurrentWorkspaceQueryState.isPending = false
|
||||
const store = createTestStore()
|
||||
store.sub(zendeskConversationSyncAtom, () => {})
|
||||
await flushAsync()
|
||||
|
||||
profileQueryState.resolve(createProfileQueryData())
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(setZendeskConversationFields).toHaveBeenCalledWith([
|
||||
{
|
||||
id: ZENDESK_FIELD_IDS.EMAIL,
|
||||
value: 'user@example.com',
|
||||
},
|
||||
])
|
||||
})
|
||||
await vi.waitFor(() => {
|
||||
expect(setZendeskConversationFields).toHaveBeenCalledWith([
|
||||
{
|
||||
id: ZENDESK_FIELD_IDS.ENVIRONMENT,
|
||||
value: 'cloud',
|
||||
},
|
||||
])
|
||||
})
|
||||
await vi.waitFor(() => {
|
||||
expect(setZendeskConversationFields).toHaveBeenCalledWith([
|
||||
{
|
||||
id: ZENDESK_FIELD_IDS.VERSION,
|
||||
value: '1.0.1',
|
||||
},
|
||||
])
|
||||
})
|
||||
await vi.waitFor(() => {
|
||||
expect(setZendeskConversationFields).toHaveBeenCalledWith([
|
||||
{
|
||||
id: ZENDESK_FIELD_IDS.WORKSPACE_ID,
|
||||
value: 'workspace-1',
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,12 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import { atom } from 'jotai'
|
||||
import { queryClientAtom } from 'jotai-tanstack-query'
|
||||
import { atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { atomWithResolvedSuspenseQuery } from '@/utils/query-atoms'
|
||||
|
||||
const accountProfileQueryAtom = atomWithResolvedSuspenseQuery(() => userProfileQueryOptions())
|
||||
|
||||
const accountProfilePendingQueryAtom = atomWithQuery(() => userProfileQueryOptions())
|
||||
|
||||
/** Render-path only — throws while pending; effects use `userProfileOrNullAtom`. */
|
||||
export const userProfileAtom = atom((get) => {
|
||||
return get(accountProfileQueryAtom).data.profile
|
||||
})
|
||||
@@ -19,10 +22,19 @@ export const userProfileEmailAtom = atom((get) => {
|
||||
return get(userProfileAtom).email
|
||||
})
|
||||
|
||||
/** Render-path only — throws while pending; effects use `accountProfileMetaOrNullAtom`. */
|
||||
export const accountProfileMetaAtom = atom((get) => {
|
||||
return get(accountProfileQueryAtom).data.meta
|
||||
})
|
||||
|
||||
export const userProfileOrNullAtom = atom((get) => {
|
||||
return get(accountProfilePendingQueryAtom).data?.profile ?? null
|
||||
})
|
||||
|
||||
export const accountProfileMetaOrNullAtom = atom((get) => {
|
||||
return get(accountProfilePendingQueryAtom).data?.meta ?? null
|
||||
})
|
||||
|
||||
export const refreshUserProfileAtom = atom(null, (get) => {
|
||||
const queryClient = get(queryClientAtom)
|
||||
queryClient.invalidateQueries({ queryKey: userProfileQueryOptions().queryKey })
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 { userProfileOrNullAtom } from './account-state'
|
||||
import { currentWorkspaceAtom } from './workspace-state'
|
||||
|
||||
type AmplitudeProperties = Record<string, string | number | boolean>
|
||||
@@ -38,10 +38,10 @@ function buildAmplitudeProperties({
|
||||
}
|
||||
|
||||
export const amplitudeIdentitySyncAtom = atomEffect((get, set) => {
|
||||
const userProfile = get(userProfileAtom)
|
||||
const userProfile = get(userProfileOrNullAtom)
|
||||
const currentWorkspace = get(currentWorkspaceAtom)
|
||||
|
||||
if (!userProfile.id) return
|
||||
if (!userProfile?.id) return
|
||||
|
||||
const properties = buildAmplitudeProperties({
|
||||
currentWorkspace,
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
'use client'
|
||||
|
||||
import { atom } from 'jotai'
|
||||
import { atomWithQuery } from 'jotai-tanstack-query'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { atomWithResolvedSuspenseQuery } from '@/utils/query-atoms'
|
||||
|
||||
const systemFeaturesQueryAtom = atomWithResolvedSuspenseQuery(() => systemFeaturesQueryOptions())
|
||||
|
||||
export const systemFeaturesAtom = atom((get) => {
|
||||
const systemFeaturesPendingQueryAtom = atomWithQuery(() => systemFeaturesQueryOptions())
|
||||
|
||||
/** Render-path only — throws while pending; effects use `systemFeaturesOrNullAtom`. */
|
||||
const systemFeaturesAtom = atom((get) => {
|
||||
return get(systemFeaturesQueryAtom).data
|
||||
})
|
||||
|
||||
export const systemFeaturesOrNullAtom = atom((get) => {
|
||||
return get(systemFeaturesPendingQueryAtom).data ?? null
|
||||
})
|
||||
|
||||
export const datasetRbacEnabledAtom = atom((get) => {
|
||||
return get(systemFeaturesAtom).rbac_enabled
|
||||
})
|
||||
|
||||
@@ -3,26 +3,29 @@
|
||||
import { atom } from 'jotai'
|
||||
import { atomWithQuery } from 'jotai-tanstack-query'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { accountProfileMetaAtom } from './account-state'
|
||||
import { accountProfileMetaAtom, accountProfileMetaOrNullAtom } from './account-state'
|
||||
import { initialLangGeniusVersionInfo } from './app-context-defaults'
|
||||
import { getLangGeniusVersionInfo } from './app-context-normalizers'
|
||||
import { systemFeaturesAtom } from './system-features-state'
|
||||
import { systemFeaturesOrNullAtom } from './system-features-state'
|
||||
|
||||
const versionQueryAtom = atomWithQuery((get) => {
|
||||
const meta = get(accountProfileMetaAtom)
|
||||
const systemFeatures = get(systemFeaturesAtom)
|
||||
const enabled = Boolean(meta.currentVersion && !systemFeatures.branding.enabled)
|
||||
const meta = get(accountProfileMetaOrNullAtom)
|
||||
const systemFeatures = get(systemFeaturesOrNullAtom)
|
||||
const enabled = Boolean(
|
||||
meta?.currentVersion && systemFeatures && !systemFeatures.branding.enabled,
|
||||
)
|
||||
|
||||
return consoleQuery.version.get.queryOptions({
|
||||
input: {
|
||||
query: {
|
||||
current_version: meta.currentVersion ?? '',
|
||||
current_version: meta?.currentVersion ?? '',
|
||||
},
|
||||
},
|
||||
enabled,
|
||||
})
|
||||
})
|
||||
|
||||
/** Render-path only — throws while pending; effects use `langGeniusVersionInfoOrDefaultAtom`. */
|
||||
export const langGeniusVersionInfoAtom = atom((get) => {
|
||||
const meta = get(accountProfileMetaAtom)
|
||||
const versionData = get(versionQueryAtom).data
|
||||
@@ -35,6 +38,18 @@ export const langGeniusVersionInfoAtom = atom((get) => {
|
||||
})
|
||||
})
|
||||
|
||||
export const langGeniusVersionInfoOrDefaultAtom = atom((get) => {
|
||||
const meta = get(accountProfileMetaOrNullAtom)
|
||||
const versionData = get(versionQueryAtom).data
|
||||
|
||||
if (!meta || !versionData) return initialLangGeniusVersionInfo
|
||||
|
||||
return getLangGeniusVersionInfo({
|
||||
meta,
|
||||
versionData,
|
||||
})
|
||||
})
|
||||
|
||||
export const langGeniusCurrentVersionAtom = atom((get) => {
|
||||
return get(langGeniusVersionInfoAtom).current_version
|
||||
})
|
||||
|
||||
@@ -4,8 +4,8 @@ import { atom } from 'jotai'
|
||||
import { atomEffect } from 'jotai-effect'
|
||||
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
|
||||
import { ZENDESK_FIELD_IDS } from '@/config'
|
||||
import { userProfileAtom } from './account-state'
|
||||
import { langGeniusVersionInfoAtom } from './version-state'
|
||||
import { userProfileOrNullAtom } from './account-state'
|
||||
import { langGeniusVersionInfoOrDefaultAtom } from './version-state'
|
||||
import { currentWorkspaceAtom } from './workspace-state'
|
||||
|
||||
type ZendeskSyncState = {
|
||||
@@ -42,9 +42,9 @@ function syncZendeskField({
|
||||
}
|
||||
|
||||
export const zendeskConversationSyncAtom = atomEffect((get, set) => {
|
||||
const userProfile = get(userProfileAtom)
|
||||
const userProfile = get(userProfileOrNullAtom)
|
||||
const currentWorkspace = get(currentWorkspaceAtom)
|
||||
const langGeniusVersionInfo = get(langGeniusVersionInfoAtom)
|
||||
const langGeniusVersionInfo = get(langGeniusVersionInfoOrDefaultAtom)
|
||||
const state = get.peek(zendeskConversationSyncStateAtom)
|
||||
const nextState = { ...state }
|
||||
|
||||
@@ -70,7 +70,7 @@ export const zendeskConversationSyncAtom = atomEffect((get, set) => {
|
||||
didSync =
|
||||
syncZendeskField({
|
||||
fieldId: ZENDESK_FIELD_IDS.EMAIL,
|
||||
value: userProfile.email,
|
||||
value: userProfile?.email ?? '',
|
||||
previousValue: state.email,
|
||||
setNextValue: (value) => {
|
||||
nextState.email = value
|
||||
|
||||
Reference in New Issue
Block a user