From 28ba7033fac3859d66f6759a217aa45f05ec9260 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Wed, 12 Aug 2026 12:05:57 +0800 Subject: [PATCH] refactor(web): remove profile bootstrap atom layer (#40474) --- .../billing/billing-integration.test.tsx | 6 +- .../education-verification-flow.test.tsx | 6 +- .../billing/pricing-modal-flow.test.tsx | 6 +- .../__tests__/profile-bootstrap-gate.spec.tsx | 92 ------------------- .../(commonLayout)/external-service-sync.tsx | 51 +++++++--- .../(commonLayout)/profile-bootstrap-gate.tsx | 11 --- web/app/(commonLayout)/providers.tsx | 7 +- .../app/overview/embedded/index.tsx | 13 +-- .../billing/apps-full-in-dialog/index.tsx | 16 ++-- .../__tests__/use-web-app-brand.spec.tsx | 10 -- .../detail-sidebar/__tests__/index.spec.tsx | 33 +------ web/app/components/detail-sidebar/index.tsx | 10 +- .../account-setting/__tests__/index.spec.tsx | 12 --- .../header/env-nav/__tests__/index.spec.tsx | 46 ++-------- web/app/components/header/env-nav/index.tsx | 19 ++-- .../main-nav/__tests__/index.spec.tsx | 45 +++++---- .../__tests__/support-menu.spec.tsx | 8 +- .../main-nav/components/help-menu.tsx | 20 +++- .../main-nav/components/support-menu.tsx | 14 +-- web/app/components/main-nav/index.tsx | 11 ++- ...orkspace-plugin-install-permission.spec.ts | 40 +++----- ...use-workspace-plugin-install-permission.ts | 10 +- .../steps/__tests__/install.spec.tsx | 19 ++-- .../steps/install.tsx | 18 ++-- .../steps/__tests__/install.spec.tsx | 19 ++-- .../steps/install.tsx | 15 +-- .../plugin-item/__tests__/index.spec.tsx | 18 ++-- .../components/plugins/plugin-item/index.tsx | 14 +-- .../__tests__/use-reference-setting.spec.ts | 19 ++-- .../plugin-page/use-reference-setting.ts | 11 ++- .../__tests__/console-bootstrap.spec.tsx | 13 +-- web/context/account-state.ts | 11 --- web/context/version-state.ts | 40 -------- .../__tests__/access-surface-cards.spec.tsx | 16 ---- web/features/system-features/state.ts | 4 - web/test/console/account-profile.ts | 21 +++-- web/test/console/query-data.ts | 11 ++- web/test/console/state-fixture.ts | 29 +----- 38 files changed, 273 insertions(+), 491 deletions(-) delete mode 100644 web/app/(commonLayout)/__tests__/profile-bootstrap-gate.spec.tsx delete mode 100644 web/app/(commonLayout)/profile-bootstrap-gate.tsx delete mode 100644 web/context/account-state.ts delete mode 100644 web/context/version-state.ts diff --git a/web/__tests__/billing/billing-integration.test.tsx b/web/__tests__/billing/billing-integration.test.tsx index 8fd5afeb2b4..b15f485f40c 100644 --- a/web/__tests__/billing/billing-integration.test.tsx +++ b/web/__tests__/billing/billing-integration.test.tsx @@ -45,6 +45,7 @@ const render = (ui: ReactElement, options: RenderOptions = {}, vectorSpaceUsageU seedEducationStatus(queryClient, mockEducationStatus) const { wrapper } = createConsoleQueryWrapper({ accountProfile: mockConsoleState.userProfile as { email?: string }, + accountProfileMeta: { currentVersion: '1.0.0' }, systemFeatures: { deployment_edition: 'CLOUD' }, queryClient, }) @@ -61,11 +62,6 @@ vi.mock('@/context/workspace-state', async () => { const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture') return createWorkspaceStateModuleMock(() => mockConsoleState) }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => mockConsoleState) -}) - vi.mock('@/context/modal-context', () => ({ useModalContext: () => ({ setShowPricingModal: mockSetShowPricingModal, diff --git a/web/__tests__/billing/education-verification-flow.test.tsx b/web/__tests__/billing/education-verification-flow.test.tsx index e1e4ead6884..c4d33fb45f7 100644 --- a/web/__tests__/billing/education-verification-flow.test.tsx +++ b/web/__tests__/billing/education-verification-flow.test.tsx @@ -32,6 +32,7 @@ const render = (ui: ReactElement, options: RenderOptions = {}) => { seedEducationStatus(queryClient, mockEducationStatus) const { wrapper } = createConsoleQueryWrapper({ accountProfile: mockConsoleState.userProfile as { email?: string }, + accountProfileMeta: { currentVersion: '1.0.0' }, systemFeatures: { deployment_edition: 'CLOUD' }, queryClient, }) @@ -50,11 +51,6 @@ vi.mock('@/context/workspace-state', async () => { const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture') return createWorkspaceStateModuleMock(() => mockConsoleState) }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => mockConsoleState) -}) - vi.mock('@/context/modal-context', () => ({ useModalContext: () => ({ setShowPricingModal: mockSetShowPricingModal, diff --git a/web/__tests__/billing/pricing-modal-flow.test.tsx b/web/__tests__/billing/pricing-modal-flow.test.tsx index 29be63615ef..df4ee8a0db6 100644 --- a/web/__tests__/billing/pricing-modal-flow.test.tsx +++ b/web/__tests__/billing/pricing-modal-flow.test.tsx @@ -26,6 +26,7 @@ const mockFetchSubscriptionUrls = vi.hoisted(() => vi.fn()) const render = (ui: React.ReactElement) => { const { wrapper } = createConsoleQueryWrapper({ accountProfile: mockConsoleState.userProfile as { email?: string }, + accountProfileMeta: { currentVersion: '1.0.0' }, educationStatus: mockEducationStatus, }) return renderWithConsoleState(ui, { wrapper }) @@ -40,11 +41,6 @@ vi.mock('@/context/workspace-state', async () => { const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture') return createWorkspaceStateModuleMock(() => mockConsoleState) }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => mockConsoleState) -}) - vi.mock('@/context/i18n', () => ({ useGetLanguage: () => 'en-US', useGetPricingPageLanguage: () => 'en', diff --git a/web/app/(commonLayout)/__tests__/profile-bootstrap-gate.spec.tsx b/web/app/(commonLayout)/__tests__/profile-bootstrap-gate.spec.tsx deleted file mode 100644 index ca33736de85..00000000000 --- a/web/app/(commonLayout)/__tests__/profile-bootstrap-gate.spec.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import type { ReactNode } from 'react' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { act, render, screen, waitFor } from '@testing-library/react' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { ProfileBootstrapGate } from '../profile-bootstrap-gate' - -const profileQueryKey = ['console', 'account', 'profile', 'get'] - -type Deferred = { - promise: Promise - resolve: (value: T) => void - reject: (reason?: unknown) => void -} - -function createDeferred(): Deferred { - let resolve!: (value: T) => void - let reject!: (reason?: unknown) => void - const promise = new Promise((nextResolve, nextReject) => { - resolve = nextResolve - reject = nextReject - }) - - return { promise, resolve, reject } -} - -const mocks = vi.hoisted(() => ({ - profileQuery: undefined as Deferred<{ id: string }> | undefined, -})) - -vi.mock('@/features/account-profile/client', () => ({ - userProfileQueryOptions: () => ({ - queryKey: profileQueryKey, - queryFn: () => mocks.profileQuery!.promise, - }), -})) - -function createQueryClient() { - return new QueryClient({ - defaultOptions: { - queries: { retry: false }, - }, - }) -} - -function renderGate(children: ReactNode, queryClient = createQueryClient()) { - render( - - {children} - , - ) - - return queryClient -} - -describe('ProfileBootstrapGate', () => { - beforeEach(() => { - mocks.profileQuery = createDeferred() - }) - - it('waits for the profile before mounting atom consumers', async () => { - renderGate(
Console shell
) - - expect(screen.queryByText('Console shell')).not.toBeInTheDocument() - - await act(async () => { - mocks.profileQuery!.resolve({ id: 'user-1' }) - }) - - expect(await screen.findByText('Console shell')).toBeInTheDocument() - }) - - it('keeps atom consumers mounted when a cached profile background refetch fails', async () => { - const queryClient = createQueryClient() - queryClient.setQueryData(profileQueryKey, { id: 'user-1' }, { updatedAt: 1 }) - - renderGate(
Console shell
, queryClient) - - expect(screen.getByText('Console shell')).toBeInTheDocument() - await waitFor(() => { - expect(queryClient.getQueryState(profileQueryKey)?.fetchStatus).toBe('fetching') - }) - - await act(async () => { - mocks.profileQuery!.reject(new Error('profile refetch failed')) - }) - - await waitFor(() => { - expect(queryClient.getQueryState(profileQueryKey)?.status).toBe('error') - }) - expect(screen.getByText('Console shell')).toBeInTheDocument() - }) -}) diff --git a/web/app/(commonLayout)/external-service-sync.tsx b/web/app/(commonLayout)/external-service-sync.tsx index 05f37a2f5d4..cec7155626e 100644 --- a/web/app/(commonLayout)/external-service-sync.tsx +++ b/web/app/(commonLayout)/external-service-sync.tsx @@ -3,7 +3,7 @@ import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen' import type { DeploymentEdition } from '@dify/contracts/api/console/system-features/types.gen' import type { GetWorkspacesCurrentSummaryResponse } from '@dify/contracts/api/console/workspaces/types.gen' -import { useSuspenseQuery } from '@tanstack/react-query' +import { skipToken, useQuery, useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' import { Fragment, useEffect, useRef } from 'react' import { setUserId, setUserProperties } from '@/app/components/base/amplitude' @@ -12,10 +12,11 @@ import { useAmplitudeInitialized } from '@/app/components/base/amplitude/use-amp import { useAnalyticsConsent } from '@/app/components/base/analytics-consent/consent-store' import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' import { ZENDESK_FIELD_IDS } from '@/config' -import { langGeniusVersionInfoAtom } from '@/context/version-state' +import { getLangGeniusVersionInfo } from '@/context/app-context-normalizers' import { currentWorkspaceAtom } from '@/context/workspace-state' import { userProfileQueryOptions } from '@/features/account-profile/client' -import { deploymentEditionAtom } from '@/features/system-features/state' +import { systemFeaturesQueryOptions } from '@/features/system-features/client' +import { consoleQuery } from '@/service/client' type AmplitudeProperties = Record @@ -108,13 +109,37 @@ function syncZendeskField({ } function ZendeskConversationSync() { - const { data: userProfile } = useSuspenseQuery({ + const { data: accountProfile } = useSuspenseQuery({ ...userProfileQueryOptions(), - select: (data) => data.profile, + select: (data) => ({ + email: data.profile.email, + meta: data.meta, + }), + }) + const { data: systemFeatures } = useSuspenseQuery({ + ...systemFeaturesQueryOptions(), + select: (data) => ({ + brandingEnabled: data.branding.enabled, + deploymentEdition: data.deployment_edition, + }), }) const currentWorkspace = useAtomValue(currentWorkspaceAtom) - const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom) - const deploymentEdition = useAtomValue(deploymentEditionAtom) + const { data: versionData } = useQuery( + consoleQuery.version.get.queryOptions({ + input: accountProfile.meta.currentVersion + ? { + query: { + current_version: accountProfile.meta.currentVersion, + }, + } + : skipToken, + enabled: !systemFeatures.brandingEnabled, + }), + ) + const langGeniusVersionInfo = getLangGeniusVersionInfo({ + meta: accountProfile.meta, + versionData, + }) const syncStateRef = useRef({}) useEffect(() => { @@ -122,7 +147,7 @@ function ZendeskConversationSync() { syncZendeskField({ fieldId: ZENDESK_FIELD_IDS.ENVIRONMENT, - deploymentEdition, + deploymentEdition: systemFeatures.deploymentEdition, value: langGeniusVersionInfo.current_env.toLowerCase(), previousValue: syncStateRef.current.environment, setNextValue: (value) => { @@ -131,7 +156,7 @@ function ZendeskConversationSync() { }) syncZendeskField({ fieldId: ZENDESK_FIELD_IDS.VERSION, - deploymentEdition, + deploymentEdition: systemFeatures.deploymentEdition, value: langGeniusVersionInfo.version, previousValue: syncStateRef.current.version, setNextValue: (value) => { @@ -140,8 +165,8 @@ function ZendeskConversationSync() { }) syncZendeskField({ fieldId: ZENDESK_FIELD_IDS.EMAIL, - deploymentEdition, - value: userProfile.email, + deploymentEdition: systemFeatures.deploymentEdition, + value: accountProfile.email, previousValue: syncStateRef.current.email, setNextValue: (value) => { nextState.email = value @@ -149,7 +174,7 @@ function ZendeskConversationSync() { }) syncZendeskField({ fieldId: ZENDESK_FIELD_IDS.WORKSPACE_ID, - deploymentEdition, + deploymentEdition: systemFeatures.deploymentEdition, value: currentWorkspace.id, previousValue: syncStateRef.current.workspaceId, setNextValue: (value) => { @@ -158,7 +183,7 @@ function ZendeskConversationSync() { }) syncStateRef.current = nextState - }, [currentWorkspace.id, deploymentEdition, langGeniusVersionInfo, userProfile.email]) + }, [accountProfile.email, currentWorkspace.id, langGeniusVersionInfo, systemFeatures]) return null } diff --git a/web/app/(commonLayout)/profile-bootstrap-gate.tsx b/web/app/(commonLayout)/profile-bootstrap-gate.tsx deleted file mode 100644 index 35471dc635a..00000000000 --- a/web/app/(commonLayout)/profile-bootstrap-gate.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import type { ReactNode } from 'react' -import { useSuspenseQuery } from '@tanstack/react-query' -import { userProfileQueryOptions } from '@/features/account-profile/client' - -export function ProfileBootstrapGate({ children }: { children: ReactNode }) { - useSuspenseQuery(userProfileQueryOptions()) - - return children -} diff --git a/web/app/(commonLayout)/providers.tsx b/web/app/(commonLayout)/providers.tsx index e0cc043fc2a..f414de22036 100644 --- a/web/app/(commonLayout)/providers.tsx +++ b/web/app/(commonLayout)/providers.tsx @@ -5,17 +5,14 @@ import { ModalContextProvider } from '@/context/modal-context-provider' import { ProviderContextProvider } from '@/context/provider-context-provider' import { ExternalServiceSync } from './external-service-sync' import { CommonLayoutHydrationBoundary } from './hydration-boundary' -import { ProfileBootstrapGate } from './profile-bootstrap-gate' export async function ConsoleRuntimeProviders({ children }: { children: ReactNode }) { return ( <> - - - {children} - + + {children} ) diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index 810efbca6d6..c36adf362ac 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -8,14 +8,14 @@ import type { SiteInfo } from '@/models/share' import { cn } from '@langgenius/dify-ui/cn' import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' +import { useSuspenseQuery } from '@tanstack/react-query' import copy from 'copy-to-clipboard' -import { useAtomValue } from 'jotai' import { Suspense, use, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { createTheme } from '@/app/components/base/chat/embedded-chatbot/theme/theme' import { InputVarType } from '@/app/components/workflow/types' -import { langGeniusVersionInfoAtom } from '@/context/version-state' +import { userProfileQueryOptions } from '@/features/account-profile/client' import { basePath } from '@/utils/var' import { compressAndEncodeBase64, @@ -141,14 +141,15 @@ const EmbeddedContent = ({ ) const latestResolvedIframeUrlRef = useRef('') - const langGeniusVersionInfo = useAtomValue(langGeniusVersionInfoAtom) + const { data: currentEnv } = useSuspenseQuery({ + ...userProfileQueryOptions(), + select: (data) => data.meta.currentEnv, + }) const theme = createTheme( siteInfo?.chat_color_theme ?? null, siteInfo?.chat_color_theme_inverted ?? false, ) - const isTestEnv = - langGeniusVersionInfo.current_env === 'TESTING' || - langGeniusVersionInfo.current_env === 'DEVELOPMENT' + const isTestEnv = currentEnv === 'TESTING' || currentEnv === 'DEVELOPMENT' const handleHiddenInputValueChange = (variable: string, value: WorkflowLaunchInputValue) => { const nextHiddenInputValues = { diff --git a/web/app/components/billing/apps-full-in-dialog/index.tsx b/web/app/components/billing/apps-full-in-dialog/index.tsx index 68d24c158da..2aa458683c1 100644 --- a/web/app/components/billing/apps-full-in-dialog/index.tsx +++ b/web/app/components/billing/apps-full-in-dialog/index.tsx @@ -5,13 +5,11 @@ import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { Meter, MeterIndicator, MeterTrack } from '@langgenius/dify-ui/meter' import { useSuspenseQuery } from '@tanstack/react-query' -import { useAtomValue } from 'jotai' import * as React from 'react' import { useTranslation } from 'react-i18next' import { Plan } from '@/app/components/billing/type' import { mailToSupport } from '@/app/components/header/utils/util' import { useProviderContext } from '@/context/provider-context' -import { langGeniusCurrentVersionAtom } from '@/context/version-state' import { userProfileQueryOptions } from '@/features/account-profile/client' import UpgradeBtn from '../upgrade-btn' import s from './style.module.css' @@ -19,11 +17,13 @@ import s from './style.module.css' const AppsFull: FC<{ loc: string; className?: string }> = ({ loc, className }) => { const { t } = useTranslation() const { plan } = useProviderContext() - const { data: userProfileEmail } = useSuspenseQuery({ + const { data: accountProfile } = useSuspenseQuery({ ...userProfileQueryOptions(), - select: (data) => data.profile.email, + select: (data) => ({ + email: data.profile.email, + currentVersion: data.meta.currentVersion, + }), }) - const currentVersion = useAtomValue(langGeniusCurrentVersionAtom) const isTeam = plan.type === Plan.team const usage = plan.usage.buildApps const total = plan.total.buildApps @@ -66,7 +66,11 @@ const AppsFull: FC<{ loc: string; className?: string }> = ({ loc, className }) = {t(($) => $['apps.contactUs'], { ns: 'billing' })} diff --git a/web/app/components/custom/custom-web-app-brand/hooks/__tests__/use-web-app-brand.spec.tsx b/web/app/components/custom/custom-web-app-brand/hooks/__tests__/use-web-app-brand.spec.tsx index 4068ab31955..2b4e7fa05de 100644 --- a/web/app/components/custom/custom-web-app-brand/hooks/__tests__/use-web-app-brand.spec.tsx +++ b/web/app/components/custom/custom-web-app-brand/hooks/__tests__/use-web-app-brand.spec.tsx @@ -7,7 +7,6 @@ import { createMockProviderContextValue } from '@/__mocks__/provider-context' import { getImageUploadErrorMessage, imageUpload } from '@/app/components/base/image-uploader/utils' import { defaultPlan } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' -import { initialLangGeniusVersionInfo } from '@/context/app-context-defaults' import { useProviderContext } from '@/context/provider-context' import { createConsoleQueryClient, renderHookWithConsoleQuery } from '@/test/console/query-data' import useWebAppBrand from '../use-web-app-brand' @@ -114,14 +113,6 @@ vi.mock('@/context/permission-state', async () => { refreshCurrentWorkspace: consoleStateRef.value?.refreshCurrentWorkspace, })) }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => ({ - ...consoleStateRef.value, - refreshCurrentWorkspace: consoleStateRef.value?.refreshCurrentWorkspace, - })) -}) - vi.mock('@/context/provider-context', () => ({ useProviderContext: vi.fn(), })) @@ -168,7 +159,6 @@ const createConsoleState = (overrides: Partial = {}): Conso isCurrentWorkspaceDatasetOperator: false, workspacePermissionKeys: ['customization.manage'], refreshCurrentWorkspace: vi.fn(), - langGeniusVersionInfo: initialLangGeniusVersionInfo, isLoadingCurrentWorkspace: false, ...overrides, } diff --git a/web/app/components/detail-sidebar/__tests__/index.spec.tsx b/web/app/components/detail-sidebar/__tests__/index.spec.tsx index 174375c0230..72666a9b5cd 100644 --- a/web/app/components/detail-sidebar/__tests__/index.spec.tsx +++ b/web/app/components/detail-sidebar/__tests__/index.spec.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, screen } from '@testing-library/react' -import { render } from '@/test/console/render' +import { renderWithConsoleQuery } from '@/test/console/query-data' import { DetailSidebarFrame } from '..' import { DETAIL_SIDEBAR_STORAGE_KEY } from '../storage' @@ -12,14 +12,6 @@ const { hotkeyRegistrations } = vi.hoisted(() => ({ } >(), })) -const mockConsoleState = vi.hoisted(() => ({ - current: { - langGeniusVersionInfo: { - current_env: '', - }, - }, -})) - vi.mock('@tanstack/react-hotkeys', async (importOriginal) => { const actual = await importOriginal() return { @@ -34,11 +26,6 @@ vi.mock('@tanstack/react-hotkeys', async (importOriginal) => { } }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => mockConsoleState.current) -}) - vi.mock('@/app/components/main-nav/components/account-section', () => ({ default: ({ compact }: { compact?: boolean }) => ( @@ -368,7 +361,7 @@ describe('Console bootstrap', () => { }) describe('Bootstrap atoms', () => { - it('should provide profile, workspace, permissions, loading state, and version metadata', async () => { + it('should provide workspace, permissions, and loading state', async () => { renderConsoleBootstrap() expect(await screen.findByText('workspace:Workspace')).toBeInTheDocument() @@ -376,10 +369,9 @@ describe('Console bootstrap', () => { expect(screen.getByText('dataset keys:dataset.acl.edit')).toBeInTheDocument() expect(screen.getByText('permission loading:false')).toBeInTheDocument() expect(screen.getByText('workspace loading:false')).toBeInTheDocument() - expect(await screen.findByText('version:1.0.0/1.0.1/cloud')).toBeInTheDocument() }) - it('should fall back to placeholder values when workspace, permission, or version data is missing', async () => { + it('should fall back to placeholder values when workspace or permission data is missing', async () => { mockCurrentWorkspaceQueryState.data = undefined mockPermissionKeysState.datasetPermissionKeys = [] mockPermissionKeysState.permissionKeys = [] @@ -393,7 +385,6 @@ describe('Console bootstrap', () => { expect(screen.getByText(`role:${initialWorkspaceSummary.role}`)).toBeInTheDocument() expect(screen.getByText('keys:')).toBeInTheDocument() expect(screen.getByText('dataset keys:')).toBeInTheDocument() - expect(screen.getByText('version://')).toBeInTheDocument() }) it('should normalize invalid workspace roles to the initial workspace role', async () => { diff --git a/web/context/account-state.ts b/web/context/account-state.ts deleted file mode 100644 index 746e7e7f959..00000000000 --- a/web/context/account-state.ts +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import { atom } from 'jotai' -import { userProfileQueryOptions } from '@/features/account-profile/client' -import { atomWithResolvedSuspenseQuery } from '@/utils/query-atoms' - -const accountProfileQueryAtom = atomWithResolvedSuspenseQuery(() => userProfileQueryOptions()) - -export const accountProfileMetaAtom = atom((get) => { - return get(accountProfileQueryAtom).data.meta -}) diff --git a/web/context/version-state.ts b/web/context/version-state.ts deleted file mode 100644 index d1ee8475bcb..00000000000 --- a/web/context/version-state.ts +++ /dev/null @@ -1,40 +0,0 @@ -'use client' - -import { atom } from 'jotai' -import { atomWithQuery } from 'jotai-tanstack-query' -import { brandingEnabledAtom } from '@/features/system-features/state' -import { consoleQuery } from '@/service/client' -import { accountProfileMetaAtom } from './account-state' -import { initialLangGeniusVersionInfo } from './app-context-defaults' -import { getLangGeniusVersionInfo } from './app-context-normalizers' - -const versionQueryAtom = atomWithQuery((get) => { - const meta = get(accountProfileMetaAtom) - const brandingEnabled = get(brandingEnabledAtom) - const enabled = Boolean(meta.currentVersion && !brandingEnabled) - - return consoleQuery.version.get.queryOptions({ - input: { - query: { - current_version: meta.currentVersion ?? '', - }, - }, - enabled, - }) -}) - -export const langGeniusVersionInfoAtom = atom((get) => { - const meta = get(accountProfileMetaAtom) - const versionData = get(versionQueryAtom).data - - if (!versionData) return initialLangGeniusVersionInfo - - return getLangGeniusVersionInfo({ - meta, - versionData, - }) -}) - -export const langGeniusCurrentVersionAtom = atom((get) => { - return get(langGeniusVersionInfoAtom).current_version -}) diff --git a/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-cards.spec.tsx b/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-cards.spec.tsx index c54a8b5c104..e90ba2880ef 100644 --- a/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-cards.spec.tsx +++ b/web/features/agent-v2/agent-detail/access/components/__tests__/access-surface-cards.spec.tsx @@ -75,22 +75,6 @@ vi.mock('@/context/permission-state', async () => { }, })) }) -vi.mock('@/context/version-state', async () => { - const { createVersionStateModuleMock } = await import('@/test/console/state-fixture') - return createVersionStateModuleMock(() => ({ - userProfile: { id: 'user-1' }, - currentWorkspace: { id: 'workspace-1' }, - workspacePermissionKeys: ['app.acl.edit'], - langGeniusVersionInfo: { - current_env: 'PRODUCTION', - current_version: '', - latest_version: '', - version: '', - release_notes: '', - }, - })) -}) - vi.mock('@/service/client', () => ({ consoleQuery: { account: { diff --git a/web/features/system-features/state.ts b/web/features/system-features/state.ts index 1bb2153212d..95decd09a58 100644 --- a/web/features/system-features/state.ts +++ b/web/features/system-features/state.ts @@ -13,7 +13,3 @@ const systemFeaturesAtom = atom((get) => { export const deploymentEditionAtom = atom((get) => { return get(systemFeaturesAtom).deployment_edition }) - -export const brandingEnabledAtom = atom((get) => { - return get(systemFeaturesAtom).branding.enabled -}) diff --git a/web/test/console/account-profile.ts b/web/test/console/account-profile.ts index 0d3f84bbb13..73ea4698e7e 100644 --- a/web/test/console/account-profile.ts +++ b/web/test/console/account-profile.ts @@ -7,6 +7,8 @@ import { render } from '@testing-library/react' import { createQueryClientWrapper } from '@/test/console/query-client' import { createTestQueryClient } from '@/test/query-client' +type AccountProfileMeta = UserProfileWithMeta['meta'] + const accountProfileQueryKey = [ ['console', 'account', 'profile', 'get'], { type: 'query' }, @@ -27,19 +29,22 @@ const createAccountProfileFixture = ( const createMockUserProfileResponse = ( profile: Partial = {}, + meta: Partial = {}, ): UserProfileWithMeta => ({ profile: createAccountProfileFixture(profile), meta: { currentVersion: null, currentEnv: null, + ...meta, }, }) export const seedAccountProfileQuery = ( queryClient: QueryClient, profile: Partial = {}, + meta: Partial = {}, ) => { - const data = createMockUserProfileResponse(profile) + const data = createMockUserProfileResponse(profile, meta) queryClient.setQueryData(accountProfileQueryKey, data) return data } @@ -47,27 +52,30 @@ export const seedAccountProfileQuery = ( export const ensureAccountProfileQuery = ( queryClient: QueryClient, profile: Partial = {}, + meta: Partial = {}, ) => { const existingProfile = queryClient.getQueryData(accountProfileQueryKey) - if (existingProfile === undefined) return seedAccountProfileQuery(queryClient, profile) + if (existingProfile === undefined) return seedAccountProfileQuery(queryClient, profile, meta) return existingProfile } export const createAccountProfileQueryClient = ( profile: Partial = {}, + meta: Partial = {}, ) => { const queryClient = createTestQueryClient() - seedAccountProfileQuery(queryClient, profile) + seedAccountProfileQuery(queryClient, profile, meta) return queryClient } export const createAccountProfileQueryWrapper = ( profile: Partial = {}, + meta: Partial = {}, ) => { - const queryClient = createAccountProfileQueryClient(profile) + const queryClient = createAccountProfileQueryClient(profile, meta) return createQueryClientWrapper(queryClient) } @@ -75,11 +83,12 @@ export const renderWithAccountProfile = ( ui: ReactElement, options: Omit & { accountProfile?: Partial + accountProfileMeta?: Partial } = {}, ) => { - const { accountProfile, ...renderOptions } = options + const { accountProfile, accountProfileMeta, ...renderOptions } = options return render(ui, { ...renderOptions, - wrapper: createAccountProfileQueryWrapper(accountProfile), + wrapper: createAccountProfileQueryWrapper(accountProfile, accountProfileMeta), }) } diff --git a/web/test/console/query-data.ts b/web/test/console/query-data.ts index e8b7c43523f..3d7c53058c4 100644 --- a/web/test/console/query-data.ts +++ b/web/test/console/query-data.ts @@ -16,6 +16,7 @@ import type { RenderResult, } from '@testing-library/react' import type { ReactElement, ReactNode } from 'react' +import type { UserProfileWithMeta } from '@/features/account-profile/client' import type { DeepPartial } from '@/test/console/system-features' import { zGetFeaturesResponse } from '@dify/contracts/api/console/features/zod.gen' import { render, renderHook } from '@testing-library/react' @@ -170,6 +171,7 @@ export type ConsoleQueryTestOptions = { */ systemFeatures?: DeepPartial | null accountProfile?: Partial | null + accountProfileMeta?: Partial educationStatus?: Partial currentWorkspace?: Partial | null trialModels?: readonly string[] | null @@ -193,8 +195,9 @@ export const createConsoleQueryWrapper = ( ): ConsoleQueryWrapper => { const queryClient = options.queryClient ?? createConsoleQueryClient() if (options.accountProfile !== null) { - if (options.accountProfile) seedAccountProfileQuery(queryClient, options.accountProfile) - else ensureAccountProfileQuery(queryClient, { timezone: 'UTC' }) + if (options.accountProfile) + seedAccountProfileQuery(queryClient, options.accountProfile, options.accountProfileMeta) + else ensureAccountProfileQuery(queryClient, { timezone: 'UTC' }, options.accountProfileMeta) } if (options.educationStatus) seedEducationStatus(queryClient, options.educationStatus) if (options.currentWorkspace !== null) { @@ -235,6 +238,7 @@ export const renderWithConsoleQuery = ( const { systemFeatures: sf, accountProfile, + accountProfileMeta, educationStatus, currentWorkspace, trialModels, @@ -246,6 +250,7 @@ export const renderWithConsoleQuery = ( const { wrapper, queryClient, systemFeatures } = createConsoleQueryWrapper({ systemFeatures: sf, accountProfile, + accountProfileMeta, educationStatus, currentWorkspace, trialModels, @@ -267,6 +272,7 @@ export const renderHookWithConsoleQuery = ( const { systemFeatures: sf, accountProfile, + accountProfileMeta, educationStatus, currentWorkspace, trialModels, @@ -278,6 +284,7 @@ export const renderHookWithConsoleQuery = ( const { wrapper, queryClient, systemFeatures } = createConsoleQueryWrapper({ systemFeatures: sf, accountProfile, + accountProfileMeta, educationStatus, currentWorkspace, trialModels, diff --git a/web/test/console/state-fixture.ts b/web/test/console/state-fixture.ts index a689e6f51de..331069b7312 100644 --- a/web/test/console/state-fixture.ts +++ b/web/test/console/state-fixture.ts @@ -1,6 +1,5 @@ import type { GetWorkspacesCurrentSummaryResponse } from '@dify/contracts/api/console/workspaces/types.gen' import type { createStore } from 'jotai' -import type { LangGeniusVersionInfo } from '@/context/app-context-types' import { atom } from 'jotai' import { createSystemFeaturesFixture } from '@/test/console/system-features' @@ -25,13 +24,12 @@ export type ConsoleStateFixture = { knowledgeFsEnabled?: boolean deploymentEdition?: 'COMMUNITY' | 'ENTERPRISE' | 'CLOUD' brandingEnabled?: boolean - langGeniusVersionInfo?: Partial refreshCurrentWorkspace?: () => void } type ConsoleStateFixtureResolver = () => ConsoleStateFixture type JotaiStore = ReturnType -type ConsoleStateOwner = 'workspace' | 'permission' | 'systemFeatures' | 'version' +type ConsoleStateOwner = 'workspace' | 'permission' | 'systemFeatures' const defaultCurrentWorkspace = { id: 'workspace-1', @@ -41,14 +39,6 @@ const defaultCurrentWorkspace = { role: 'owner', } satisfies GetWorkspacesCurrentSummaryResponse -const defaultLangGeniusVersionInfo = { - current_env: 'CLOUD', - current_version: '', - latest_version: '', - version: '', - release_notes: '', -} satisfies LangGeniusVersionInfo - const currentWorkspaceAtom = atom(defaultCurrentWorkspace) const currentWorkspaceIdAtom = atom((get) => get(currentWorkspaceAtom).id) const isCurrentWorkspaceManagerAtom = atom(false) @@ -68,9 +58,6 @@ const systemFeaturesAtom = atom(createSystemFeaturesFixture()) const deploymentEditionAtom = atom((get) => get(systemFeaturesAtom).deployment_edition) const brandingEnabledAtom = atom((get) => get(systemFeaturesAtom).branding.enabled) -const langGeniusVersionInfoAtom = atom(defaultLangGeniusVersionInfo) -const langGeniusCurrentVersionAtom = atom((get) => get(langGeniusVersionInfoAtom).current_version) - const consoleStateFixtureResolvers: Partial< Record > = {} @@ -109,10 +96,6 @@ export const seedRegisteredConsoleStateFixture = (store: JotaiStore) => { }, }), ) - store.set(langGeniusVersionInfoAtom, { - ...defaultLangGeniusVersionInfo, - ...state.langGeniusVersionInfo, - }) store.set(refreshCurrentWorkspaceCallbackAtom, { callback: state.refreshCurrentWorkspace ?? (() => {}), }) @@ -169,13 +152,3 @@ export const createSystemFeaturesStateModuleMock = (getState: ConsoleStateFixtur brandingEnabledAtom, } } - -export const createVersionStateModuleMock = (getState: ConsoleStateFixtureResolver) => { - registerConsoleStateFixture('version', () => ({ - langGeniusVersionInfo: getState().langGeniusVersionInfo, - })) - return { - langGeniusVersionInfoAtom, - langGeniusCurrentVersionAtom, - } -}