diff --git a/web/__tests__/embedded-user-id-store.test.tsx b/web/__tests__/embedded-user-id-store.test.tsx index 901218e76b..aa6bb5e4fb 100644 --- a/web/__tests__/embedded-user-id-store.test.tsx +++ b/web/__tests__/embedded-user-id-store.test.tsx @@ -26,37 +26,11 @@ vi.mock('@/app/components/base/chat/utils', () => ({ getProcessedSystemVariablesFromUrlParams: (...args: any[]) => mockGetProcessedSystemVariablesFromUrlParams(...args), })) -// Use vi.hoisted to define mock state before vi.mock hoisting -const { mockGlobalStoreState } = vi.hoisted(() => ({ - mockGlobalStoreState: { - isGlobalPending: false, - setIsGlobalPending: vi.fn(), - systemFeatures: {}, - setSystemFeatures: vi.fn(), - }, +vi.mock('@/context/global-public-context', () => ({ + useSystemFeatures: vi.fn(() => ({})), + useIsSystemFeaturesPending: () => false, })) -vi.mock('@/context/global-public-context', () => { - const useGlobalPublicStore = Object.assign( - (selector?: (state: typeof mockGlobalStoreState) => any) => - selector ? selector(mockGlobalStoreState) : mockGlobalStoreState, - { - setState: (updater: any) => { - if (typeof updater === 'function') - Object.assign(mockGlobalStoreState, updater(mockGlobalStoreState) ?? {}) - - else - Object.assign(mockGlobalStoreState, updater) - }, - __mockState: mockGlobalStoreState, - }, - ) - return { - useGlobalPublicStore, - useIsSystemFeaturesPending: () => false, - } -}) - const TestConsumer = () => { const embeddedUserId = useWebAppStore(state => state.embeddedUserId) const embeddedConversationId = useWebAppStore(state => state.embeddedConversationId) @@ -91,7 +65,6 @@ const initialWebAppStore = (() => { })() beforeEach(() => { - mockGlobalStoreState.isGlobalPending = false mockGetProcessedSystemVariablesFromUrlParams.mockReset() useWebAppStore.setState(initialWebAppStore, true) }) diff --git a/web/app/(shareLayout)/webapp-reset-password/layout.tsx b/web/app/(shareLayout)/webapp-reset-password/layout.tsx index a71317c8b9..f73aa17c8b 100644 --- a/web/app/(shareLayout)/webapp-reset-password/layout.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/layout.tsx @@ -1,11 +1,11 @@ 'use client' +import type * as React from 'react' import Header from '@/app/signin/_header' - -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' -export default function SignInLayout({ children }: any) { - const { systemFeatures } = useGlobalPublicStore() +export default function SignInLayout({ children }: { children: React.ReactNode }) { + const systemFeatures = useSystemFeatures() return ( <>
diff --git a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx index 0776df036d..01539e6ab9 100644 --- a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx @@ -5,12 +5,12 @@ import { useCallback, useEffect } from 'react' import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' import Toast from '@/app/components/base/toast' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share' import { SSOProtocol } from '@/types/feature' const ExternalMemberSSOAuth = () => { - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const searchParams = useSearchParams() const router = useRouter() diff --git a/web/app/(shareLayout)/webapp-signin/layout.tsx b/web/app/(shareLayout)/webapp-signin/layout.tsx index 21cb0e1f57..6c07bdd8f6 100644 --- a/web/app/(shareLayout)/webapp-signin/layout.tsx +++ b/web/app/(shareLayout)/webapp-signin/layout.tsx @@ -2,13 +2,13 @@ import type { PropsWithChildren } from 'react' import { useTranslation } from 'react-i18next' -import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' export default function SignInLayout({ children }: PropsWithChildren) { const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() useDocumentTitle(t('webapp.login', { ns: 'login' })) return ( <> diff --git a/web/app/(shareLayout)/webapp-signin/normalForm.tsx b/web/app/(shareLayout)/webapp-signin/normalForm.tsx index b15145346f..e5cba6c9b7 100644 --- a/web/app/(shareLayout)/webapp-signin/normalForm.tsx +++ b/web/app/(shareLayout)/webapp-signin/normalForm.tsx @@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { IS_CE_EDITION } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { LicenseStatus } from '@/types/feature' import { cn } from '@/utils/classnames' import MailAndCodeAuth from './components/mail-and-code-auth' @@ -17,7 +17,7 @@ const NormalForm = () => { const { t } = useTranslation() const [isLoading, setIsLoading] = useState(true) - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const [authType, updateAuthType] = useState<'code' | 'password'>('password') const [showORLine, setShowORLine] = useState(false) const [allMethodsAreDisabled, setAllMethodsAreDisabled] = useState(false) diff --git a/web/app/(shareLayout)/webapp-signin/page.tsx b/web/app/(shareLayout)/webapp-signin/page.tsx index b3ad1d48a6..c64d6e10c1 100644 --- a/web/app/(shareLayout)/webapp-signin/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/page.tsx @@ -5,8 +5,8 @@ import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import AppUnavailable from '@/app/components/base/app-unavailable' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useWebAppStore } from '@/context/web-app-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { webAppLogout } from '@/service/webapp-auth' import ExternalMemberSsoAuth from './components/external-member-sso-auth' @@ -14,7 +14,7 @@ import NormalForm from './normalForm' const WebSSOForm: FC = () => { const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const webAppAccessMode = useWebAppStore(s => s.webAppAccessMode) const searchParams = useSearchParams() const router = useRouter() diff --git a/web/app/account/(commonLayout)/account-page/index.tsx b/web/app/account/(commonLayout)/account-page/index.tsx index f01efc002c..0f6ea06195 100644 --- a/web/app/account/(commonLayout)/account-page/index.tsx +++ b/web/app/account/(commonLayout)/account-page/index.tsx @@ -16,8 +16,8 @@ import { ToastContext } from '@/app/components/base/toast' import Collapse from '@/app/components/header/account-setting/collapse' import { IS_CE_EDITION, validPassword } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useProviderContext } from '@/context/provider-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { updateUserProfile } from '@/service/common' import { useAppList } from '@/service/use-apps' import DeleteAccount from '../delete-account' @@ -34,7 +34,7 @@ const descriptionClassName = ` export default function AccountPage() { const { t } = useTranslation() - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const { data: appList } = useAppList({ page: 1, limit: 100, name: '' }) const apps = appList?.data || [] const { mutateUserProfile, userProfile } = useAppContext() diff --git a/web/app/account/(commonLayout)/header.tsx b/web/app/account/(commonLayout)/header.tsx index bb58be87a8..bc75c34334 100644 --- a/web/app/account/(commonLayout)/header.tsx +++ b/web/app/account/(commonLayout)/header.tsx @@ -5,13 +5,13 @@ import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import DifyLogo from '@/app/components/base/logo/dify-logo' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import Avatar from './avatar' const Header = () => { const { t } = useTranslation() const router = useRouter() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const goToStudio = useCallback(() => { router.push('/apps') diff --git a/web/app/account/oauth/authorize/layout.tsx b/web/app/account/oauth/authorize/layout.tsx index 189971b16f..f4332a7c00 100644 --- a/web/app/account/oauth/authorize/layout.tsx +++ b/web/app/account/oauth/authorize/layout.tsx @@ -3,13 +3,13 @@ import Loading from '@/app/components/base/loading' import Header from '@/app/signin/_header' import { AppContextProvider } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useIsLogin } from '@/service/use-common' import { cn } from '@/utils/classnames' export default function SignInLayout({ children }: any) { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() useDocumentTitle('') const { isLoading, data: loginData } = useIsLogin() const isLoggedIn = loginData?.logged_in diff --git a/web/app/activate/page.tsx b/web/app/activate/page.tsx index 5852ef54e4..ef7f7bbf22 100644 --- a/web/app/activate/page.tsx +++ b/web/app/activate/page.tsx @@ -1,12 +1,12 @@ 'use client' import * as React from 'react' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Header from '../signin/_header' import ActivateForm from './activateForm' const Activate = () => { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() return (
diff --git a/web/app/components/app-initializer.tsx b/web/app/components/app-initializer.tsx index fb6ac1c6da..a958e5d87a 100644 --- a/web/app/components/app-initializer.tsx +++ b/web/app/components/app-initializer.tsx @@ -9,7 +9,7 @@ import { EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION, EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, } from '@/app/education-apply/constants' -import { useSetupStatusQuery } from '@/context/global-public-context' +import { useSetupStatusQuery } from '@/hooks/use-global-public' import { sendGAEvent } from '@/utils/gtag' import { resolvePostLoginRedirect } from '../signin/utils/post-login-redirect' import { trackEvent } from './base/amplitude' diff --git a/web/app/components/app/app-access-control/index.tsx b/web/app/components/app/app-access-control/index.tsx index 8d46e41a11..d1972114b8 100644 --- a/web/app/components/app/app-access-control/index.tsx +++ b/web/app/components/app/app-access-control/index.tsx @@ -5,7 +5,7 @@ import { Description as DialogDescription, DialogTitle } from '@headlessui/react import { RiBuildingLine, RiGlobalLine, RiVerifiedBadgeLine } from '@remixicon/react' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode, SubjectType } from '@/models/access-control' import { useUpdateAccessMode } from '@/service/access-control' import useAccessControlStore from '../../../../context/access-control-store' @@ -24,7 +24,7 @@ type AccessControlProps = { export default function AccessControl(props: AccessControlProps) { const { app, onClose, onConfirm } = props const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const setAppId = useAccessControlStore(s => s.setAppId) const specificGroups = useAccessControlStore(s => s.specificGroups) const specificMembers = useAccessControlStore(s => s.specificMembers) diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index ab7f442ebf..22c71bdee9 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -42,9 +42,9 @@ import { collaborationManager } from '@/app/components/workflow/collaboration/co import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager' import { WorkflowContext } from '@/app/components/workflow/context' import { appDefaultIconBackground } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useAsyncWindowOpen } from '@/hooks/use-async-window-open' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control' import { fetchAppDetailDirect } from '@/service/apps' @@ -162,7 +162,7 @@ const AppPublisher = ({ const workflowStore = useContext(WorkflowContext) const appDetail = useAppStore(state => state.appDetail) const setAppDetail = useAppStore(s => s.setAppDetail) - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { formatTimeFromNow } = useFormatTimeFromNow() const { app_base_url: appBaseURL = '', access_token: accessToken = '' } = appDetail?.site ?? {} diff --git a/web/app/components/app/create-app-dialog/app-card/index.tsx b/web/app/components/app/create-app-dialog/app-card/index.tsx index 15cfbd5411..47c2dab7c5 100644 --- a/web/app/components/app/create-app-dialog/app-card/index.tsx +++ b/web/app/components/app/create-app-dialog/app-card/index.tsx @@ -8,7 +8,7 @@ import { useContextSelector } from 'use-context-selector' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' import AppListContext from '@/context/app-list-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { AppTypeIcon, AppTypeLabel } from '../../type-selector' @@ -25,7 +25,7 @@ const AppCard = ({ }: AppCardProps) => { const { t } = useTranslation() const { app: appBasicInfo } = app - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const isTrialApp = app.can_trial && systemFeatures.enable_trial_app const setShowTryAppPanel = useContextSelector(AppListContext, ctx => ctx.setShowTryAppPanel) const showTryAPPPanel = useCallback((appId: string) => { diff --git a/web/app/components/app/overview/app-card.tsx b/web/app/components/app/overview/app-card.tsx index 9975c81b3e..8a45d124ff 100644 --- a/web/app/components/app/overview/app-card.tsx +++ b/web/app/components/app/overview/app-card.tsx @@ -31,8 +31,8 @@ import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-butt import Indicator from '@/app/components/header/indicator' import { BlockEnum } from '@/app/components/workflow/types' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useDocLink } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { useAppWhiteListSubjects } from '@/service/access-control' import { fetchAppDetailDirect } from '@/service/apps' @@ -85,7 +85,7 @@ function AppCard({ const [showConfirmDelete, setShowConfirmDelete] = useState(false) const [showAccessControl, setShowAccessControl] = useState(false) const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { data: appAccessSubjects } = useAppWhiteListSubjects(appDetail?.id, systemFeatures.webapp_auth.enabled && appDetail?.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS) const OPERATIONS_MAP = useMemo(() => { diff --git a/web/app/components/apps/app-card.spec.tsx b/web/app/components/apps/app-card.spec.tsx index feab513dc1..263da61f32 100644 --- a/web/app/components/apps/app-card.spec.tsx +++ b/web/app/components/apps/app-card.spec.tsx @@ -51,11 +51,9 @@ vi.mock('@/context/provider-context', () => ({ // Mock global public store - allow dynamic configuration let mockWebappAuthEnabled = false vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (s: any) => any) => selector({ - systemFeatures: { - webapp_auth: { enabled: mockWebappAuthEnabled }, - branding: { enabled: false }, - }, + useSystemFeatures: () => ({ + webapp_auth: { enabled: mockWebappAuthEnabled }, + branding: { enabled: false }, }), })) diff --git a/web/app/components/apps/app-card.tsx b/web/app/components/apps/app-card.tsx index 7415ba6d29..0a49f16c19 100644 --- a/web/app/components/apps/app-card.tsx +++ b/web/app/components/apps/app-card.tsx @@ -24,9 +24,9 @@ import Tooltip from '@/app/components/base/tooltip' import { UserAvatarList } from '@/app/components/base/user-avatar-list' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useProviderContext } from '@/context/provider-context' import { useAsyncWindowOpen } from '@/hooks/use-async-window-open' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { useGetUserCanAccessApp } from '@/service/access-control' import { copyApp, deleteApp, exportAppBundle, exportAppConfig, updateAppInfo } from '@/service/apps' @@ -67,7 +67,7 @@ export type AppCardProps = { const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { isCurrentWorkspaceEditor } = useAppContext() const { onPlanInfoChanged } = useProviderContext() const { push } = useRouter() diff --git a/web/app/components/apps/list.spec.tsx b/web/app/components/apps/list.spec.tsx index f086220fc6..5d05da76c2 100644 --- a/web/app/components/apps/list.spec.tsx +++ b/web/app/components/apps/list.spec.tsx @@ -27,10 +27,8 @@ vi.mock('@/context/app-context', () => ({ // Mock global public store vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: () => ({ - systemFeatures: { - branding: { enabled: false }, - }, + useSystemFeatures: () => ({ + branding: { enabled: false }, }), })) diff --git a/web/app/components/apps/list.tsx b/web/app/components/apps/list.tsx index d3fc916c10..c6608a9188 100644 --- a/web/app/components/apps/list.tsx +++ b/web/app/components/apps/list.tsx @@ -27,7 +27,7 @@ import { ToastContext } from '@/app/components/base/toast' import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { CheckModal } from '@/hooks/use-pay' import { DSLImportStatus } from '@/models/app' import { fetchWorkflowOnlineUsers, importAppBundle } from '@/service/apps' @@ -68,7 +68,7 @@ const List: FC = ({ }) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const router = useRouter() const { push } = useRouter() const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator, isLoadingCurrentWorkspace } = useAppContext() diff --git a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx index 73305f86db..b0f0ec16b2 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx @@ -17,7 +17,7 @@ import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/re import Confirm from '@/app/components/base/confirm' import DifyLogo from '@/app/components/base/logo/dify-logo' import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { useChatWithHistoryContext } from '../context' @@ -47,7 +47,7 @@ const Sidebar = ({ isPanel, panelVisible }: Props) => { isResponding, } = useChatWithHistoryContext() const isSidebarCollapsed = sidebarCollapseState - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const [showConfirm, setShowConfirm] = useState(null) const [showRename, setShowRename] = useState(null) diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index fe7afc9e22..f023eb0ec2 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -9,7 +9,7 @@ import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs import Divider from '@/app/components/base/divider' import DifyLogo from '@/app/components/base/logo/dify-logo' import Tooltip from '@/app/components/base/tooltip' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { isClient } from '@/utils/client' import { @@ -45,7 +45,7 @@ const Header: FC = ({ const [parentOrigin, setParentOrigin] = useState('') const [showToggleExpandButton, setShowToggleExpandButton] = useState(false) const [expanded, setExpanded] = useState(false) - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const handleMessageReceived = useCallback((event: MessageEvent) => { let currentParentOrigin = parentOrigin diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index 635e8d4aee..4573f22c6e 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -9,9 +9,9 @@ import Header from '@/app/components/base/chat/embedded-chatbot/header' import Loading from '@/app/components/base/loading' import DifyLogo from '@/app/components/base/logo/dify-logo' import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header' -import { useGlobalPublicStore } from '@/context/global-public-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AppSourceType } from '@/service/share' import { cn } from '@/utils/classnames' import { @@ -34,7 +34,7 @@ const Chatbot = () => { themeBuilder, } = useEmbeddedChatbotContext() const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const customConfig = appData?.custom_config const site = appData?.site diff --git a/web/app/components/custom/custom-web-app-brand/index.spec.tsx b/web/app/components/custom/custom-web-app-brand/index.spec.tsx index e50ca4e9b2..944d465cf3 100644 --- a/web/app/components/custom/custom-web-app-brand/index.spec.tsx +++ b/web/app/components/custom/custom-web-app-brand/index.spec.tsx @@ -4,8 +4,8 @@ import { getImageUploadErrorMessage, imageUpload } from '@/app/components/base/i import { useToastContext } from '@/app/components/base/toast' import { Plan } from '@/app/components/billing/type' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useProviderContext } from '@/context/provider-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { updateCurrentWorkspace } from '@/service/common' import CustomWebAppBrand from './index' @@ -22,7 +22,7 @@ vi.mock('@/context/provider-context', () => ({ useProviderContext: vi.fn(), })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn(), + useSystemFeatures: vi.fn(), })) vi.mock('@/app/components/base/image-uploader/utils', () => ({ imageUpload: vi.fn(), @@ -34,7 +34,7 @@ const mockUseToastContext = vi.mocked(useToastContext) const mockUpdateCurrentWorkspace = vi.mocked(updateCurrentWorkspace) const mockUseAppContext = vi.mocked(useAppContext) const mockUseProviderContext = vi.mocked(useProviderContext) -const mockUseGlobalPublicStore = vi.mocked(useGlobalPublicStore) +const mockUseSystemFeatures = vi.mocked(useSystemFeatures) const mockImageUpload = vi.mocked(imageUpload) const mockGetImageUploadErrorMessage = vi.mocked(getImageUploadErrorMessage) @@ -80,7 +80,7 @@ describe('CustomWebAppBrand', () => { workspace_logo: 'https://example.com/workspace-logo.png', }, } - mockUseGlobalPublicStore.mockImplementation(selector => selector ? selector({ systemFeatures: systemFeaturesState } as any) : { systemFeatures: systemFeaturesState }) + mockUseSystemFeatures.mockReturnValue(systemFeaturesState as ReturnType) mockGetImageUploadErrorMessage.mockReturnValue('upload error') }) diff --git a/web/app/components/custom/custom-web-app-brand/index.tsx b/web/app/components/custom/custom-web-app-brand/index.tsx index d9e80e80d1..15d3fa6a7e 100644 --- a/web/app/components/custom/custom-web-app-brand/index.tsx +++ b/web/app/components/custom/custom-web-app-brand/index.tsx @@ -19,8 +19,8 @@ import Switch from '@/app/components/base/switch' import { useToastContext } from '@/app/components/base/toast' import { Plan } from '@/app/components/billing/type' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useProviderContext } from '@/context/provider-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { updateCurrentWorkspace, } from '@/service/common' @@ -40,7 +40,7 @@ const CustomWebAppBrand = () => { const [fileId, setFileId] = useState('') const [imgKey, setImgKey] = useState(() => Date.now()) const [uploadProgress, setUploadProgress] = useState(0) - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const isSandbox = enableBilling && plan.type === Plan.sandbox const uploading = uploadProgress > 0 && uploadProgress < 100 const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || '' diff --git a/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.spec.tsx index 4650907c94..cad2f0fd1e 100644 --- a/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.spec.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.spec.tsx @@ -25,10 +25,7 @@ vi.mock('@/context/i18n', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn((selector) => { - const state = { systemFeatures: { enable_marketplace: true } } - return selector(state) - }), + useSystemFeatures: vi.fn(() => ({ enable_marketplace: true })), })) const mockUsePipelineTemplateList = vi.fn() diff --git a/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.tsx b/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.tsx index 31c62758c1..52155e892b 100644 --- a/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/built-in-pipeline-list.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useLocale } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { LanguagesSupported } from '@/i18n-config/language' import { usePipelineTemplateList } from '@/service/use-pipeline' import CreateCard from './create-card' @@ -13,7 +13,7 @@ const BuiltInPipelineList = () => { return locale return LanguagesSupported[0] }, [locale]) - const enableMarketplace = useGlobalPublicStore(s => s.systemFeatures.enable_marketplace) + const enableMarketplace = useSystemFeatures().enable_marketplace const { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'built-in', language }, enableMarketplace) const list = pipelineList?.pipeline_templates || [] diff --git a/web/app/components/datasets/list/index.spec.tsx b/web/app/components/datasets/list/index.spec.tsx index ff48774c87..59d8b2a9c0 100644 --- a/web/app/components/datasets/list/index.spec.tsx +++ b/web/app/components/datasets/list/index.spec.tsx @@ -34,10 +34,8 @@ vi.mock('@/context/app-context', () => ({ // Mock global public context vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: () => ({ - systemFeatures: { - branding: { enabled: false }, - }, + useSystemFeatures: () => ({ + branding: { enabled: false }, }), })) @@ -333,10 +331,8 @@ describe('List', () => { it('should not show DatasetFooter when branding is enabled', async () => { vi.doMock('@/context/global-public-context', () => ({ - useGlobalPublicStore: () => ({ - systemFeatures: { - branding: { enabled: true }, - }, + useSystemFeatures: () => ({ + branding: { enabled: true }, }), })) diff --git a/web/app/components/datasets/list/index.tsx b/web/app/components/datasets/list/index.tsx index fdbe33986a..11e3f86d4e 100644 --- a/web/app/components/datasets/list/index.tsx +++ b/web/app/components/datasets/list/index.tsx @@ -16,8 +16,8 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' import { useAppContext, useSelector as useAppContextSelector } from '@/context/app-context' import { useExternalApiPanel } from '@/context/external-api-panel-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useDatasetApiBaseUrl } from '@/service/knowledge/use-dataset' // Components import ExternalAPIPanel from '../external-api/external-api-panel' @@ -27,7 +27,7 @@ import Datasets from './datasets' const List = () => { const { t } = useTranslation() - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const router = useRouter() const { currentWorkspace, isCurrentWorkspaceOwner } = useAppContext() const showTagManagementModal = useTagStore(s => s.showTagManagementModal) diff --git a/web/app/components/explore/app-card/index.tsx b/web/app/components/explore/app-card/index.tsx index 15152e0695..09b7691cab 100644 --- a/web/app/components/explore/app-card/index.tsx +++ b/web/app/components/explore/app-card/index.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next' import { useContextSelector } from 'use-context-selector' import AppIcon from '@/app/components/base/app-icon' import ExploreContext from '@/context/explore-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AppModeEnum } from '@/types/app' import { cn } from '@/utils/classnames' import { AppTypeIcon } from '../../app/type-selector' @@ -28,7 +28,7 @@ const AppCard = ({ }: AppCardProps) => { const { t } = useTranslation() const { app: appBasicInfo } = app - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const isTrialApp = app.can_trial && systemFeatures.enable_trial_app const setShowTryAppPanel = useContextSelector(ExploreContext, ctx => ctx.setShowTryAppPanel) const showTryAPPPanel = useCallback((appId: string) => { diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index 04f75107da..ec58a31623 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -17,7 +17,7 @@ import Banner from '@/app/components/explore/banner/banner' import Category from '@/app/components/explore/category' import CreateAppModal from '@/app/components/explore/create-app-modal' import ExploreContext from '@/context/explore-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useImportDSL } from '@/hooks/use-import-dsl' import { DSLImportMode, @@ -36,7 +36,7 @@ const Apps = ({ onSuccess, }: AppsProps) => { const { t } = useTranslation() - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const { hasEditPermission } = useContext(ExploreContext) const allCategoriesEn = t('apps.allCategories', { ns: 'explore', lng: 'en' }) diff --git a/web/app/components/explore/try-app/index.tsx b/web/app/components/explore/try-app/index.tsx index c6f00ed08e..510b9326be 100644 --- a/web/app/components/explore/try-app/index.tsx +++ b/web/app/components/explore/try-app/index.tsx @@ -7,7 +7,7 @@ import * as React from 'react' import { useState } from 'react' import Loading from '@/app/components/base/loading' import Modal from '@/app/components/base/modal/index' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useGetTryAppInfo } from '@/service/use-try-app' import Button from '../../base/button' import App from './app' @@ -30,7 +30,7 @@ const TryApp: FC = ({ onClose, onCreate, }) => { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const isTrialApp = !!(app && app.can_trial && systemFeatures.enable_trial_app) const [type, setType] = useState(() => (app && !isTrialApp ? TypeEnum.DETAIL : TypeEnum.TRY)) const { data: appDetail, isLoading } = useGetTryAppInfo(appId) diff --git a/web/app/components/header/account-about/index.tsx b/web/app/components/header/account-about/index.tsx index b80cbb8f03..f257098a7e 100644 --- a/web/app/components/header/account-about/index.tsx +++ b/web/app/components/header/account-about/index.tsx @@ -9,7 +9,7 @@ import DifyLogo from '@/app/components/base/logo/dify-logo' import Modal from '@/app/components/base/modal' import { IS_CE_EDITION } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' type IAccountSettingProps = { langGeniusVersionInfo: LangGeniusVersionResponse @@ -22,7 +22,7 @@ export default function AccountAbout({ }: IAccountSettingProps) { const { t } = useTranslation() const isLatest = langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() return ( { - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { data } = useGetDataSourceListAuth() return ( diff --git a/web/app/components/header/account-setting/members-page/index.tsx b/web/app/components/header/account-setting/members-page/index.tsx index 5a8f3aebdb..83043858cb 100644 --- a/web/app/components/header/account-setting/members-page/index.tsx +++ b/web/app/components/header/account-setting/members-page/index.tsx @@ -9,10 +9,10 @@ import { NUM_INFINITE } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useLocale } from '@/context/i18n' import { useProviderContext } from '@/context/provider-context' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' +import { useSystemFeatures } from '@/hooks/use-global-public' import { LanguagesSupported } from '@/i18n-config/language' import { useMembers } from '@/service/use-common' import EditWorkspaceModal from './edit-workspace-modal' @@ -36,7 +36,7 @@ const MembersPage = () => { const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext() const { data, refetch } = useMembers() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { formatTimeFromNow } = useFormatTimeFromNow() const [inviteModalVisible, setInviteModalVisible] = useState(false) const [invitationResults, setInvitationResults] = useState([]) diff --git a/web/app/components/header/account-setting/members-page/invite-button.tsx b/web/app/components/header/account-setting/members-page/invite-button.tsx index fb5b5cdc5e..db159dc3e1 100644 --- a/web/app/components/header/account-setting/members-page/invite-button.tsx +++ b/web/app/components/header/account-setting/members-page/invite-button.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Loading from '@/app/components/base/loading' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useWorkspacePermissions } from '@/service/use-workspace' type InviteButtonProps = { @@ -14,7 +14,7 @@ type InviteButtonProps = { const InviteButton = (props: InviteButtonProps) => { const { t } = useTranslation() const { currentWorkspace } = useAppContext() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { data: workspacePermissions, isFetching: isFetchingWorkspacePermissions } = useWorkspacePermissions(currentWorkspace!.id, systemFeatures.branding.enabled) if (systemFeatures.branding.enabled) { if (isFetchingWorkspacePermissions) { diff --git a/web/app/components/header/account-setting/members-page/operation/transfer-ownership.tsx b/web/app/components/header/account-setting/members-page/operation/transfer-ownership.tsx index d7d7943c67..e3d0020db8 100644 --- a/web/app/components/header/account-setting/members-page/operation/transfer-ownership.tsx +++ b/web/app/components/header/account-setting/members-page/operation/transfer-ownership.tsx @@ -7,7 +7,7 @@ import { Fragment } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useWorkspacePermissions } from '@/service/use-workspace' import { cn } from '@/utils/classnames' @@ -18,7 +18,7 @@ type Props = { const TransferOwnership = ({ onOperate }: Props) => { const { t } = useTranslation() const { currentWorkspace } = useAppContext() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { data: workspacePermissions, isFetching: isFetchingWorkspacePermissions } = useWorkspacePermissions(currentWorkspace!.id, systemFeatures.branding.enabled) if (systemFeatures.branding.enabled) { if (isFetchingWorkspacePermissions) { diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 7606bbc04f..006887c6d0 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -10,8 +10,8 @@ import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { IS_CLOUD_EDITION } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useProviderContext } from '@/context/provider-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { CustomConfigurationStatusEnum, @@ -41,7 +41,7 @@ const ModelProviderPage = ({ searchText }: Props) => { const { data: speech2textDefaultModel, isLoading: isSpeech2textDefaultModelLoading } = useDefaultModel(ModelTypeEnum.speech2text) const { data: ttsDefaultModel, isLoading: isTTSDefaultModelLoading } = useDefaultModel(ModelTypeEnum.tts) const { modelProviders: providers } = useProviderContext() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const isDefaultModelLoading = isTextGenerationDefaultModelLoading || isEmbeddingsDefaultModelLoading || isRerankDefaultModelLoading diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index d44c201e0e..b48509e995 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -10,7 +10,7 @@ import Loading from '@/app/components/base/loading' import Tooltip from '@/app/components/base/tooltip' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import useTimestamp from '@/hooks/use-timestamp' import { ModelProviderQuotaGetPaid } from '@/types/model-provider' import { cn } from '@/utils/classnames' @@ -56,7 +56,7 @@ const QuotaPanel: FC = ({ }) => { const { t } = useTranslation() const { currentWorkspace } = useAppContext() - const { trial_models } = useGlobalPublicStore(s => s.systemFeatures) + const { trial_models } = useSystemFeatures() const credits = Math.max((currentWorkspace.trial_credits - currentWorkspace.trial_credits_used) || 0, 0) const providerMap = useMemo(() => new Map( providers.map(p => [p.provider, p.preferred_provider_type]), diff --git a/web/app/components/header/index.tsx b/web/app/components/header/index.tsx index 210c62b660..863a50bf2a 100644 --- a/web/app/components/header/index.tsx +++ b/web/app/components/header/index.tsx @@ -5,11 +5,11 @@ import DifyLogo from '@/app/components/base/logo/dify-logo' import WorkplaceSelector from '@/app/components/header/account-dropdown/workplace-selector' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import { WorkspaceProvider } from '@/context/workspace-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' +import { useSystemFeatures } from '@/hooks/use-global-public' import { Plan } from '../billing/type' import AccountDropdown from './account-dropdown' import AppNav from './app-nav' @@ -33,7 +33,7 @@ const Header = () => { const isMobile = media === MediaType.mobile const { enableBilling, plan } = useProviderContext() const { setShowPricingModal, setShowAccountSettingModal } = useModalContext() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const isFreePlan = plan.type === Plan.sandbox const isBrandingEnabled = systemFeatures.branding.enabled const handlePlanClick = useCallback(() => { diff --git a/web/app/components/header/license-env/index.tsx b/web/app/components/header/license-env/index.tsx index 292b3a2d30..2c89d2a83a 100644 --- a/web/app/components/header/license-env/index.tsx +++ b/web/app/components/header/license-env/index.tsx @@ -3,13 +3,13 @@ import { RiHourglass2Fill } from '@remixicon/react' import dayjs from 'dayjs' import { useTranslation } from 'react-i18next' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { LicenseStatus } from '@/types/feature' import PremiumBadge from '../../base/premium-badge' const LicenseNav = () => { const { t } = useTranslation() - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() if (systemFeatures.license?.status === LicenseStatus.EXPIRING) { const expiredAt = systemFeatures.license?.expired_at diff --git a/web/app/components/plugins/install-plugin/hooks/use-install-plugin-limit.tsx b/web/app/components/plugins/install-plugin/hooks/use-install-plugin-limit.tsx index 227c572a77..6f6f1c9a82 100644 --- a/web/app/components/plugins/install-plugin/hooks/use-install-plugin-limit.tsx +++ b/web/app/components/plugins/install-plugin/hooks/use-install-plugin-limit.tsx @@ -1,6 +1,6 @@ import type { Plugin, PluginManifestInMarket } from '../../types' import type { SystemFeatures } from '@/types/feature' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { InstallationScope } from '@/types/feature' type PluginProps = (Plugin | PluginManifestInMarket) & { from: 'github' | 'marketplace' | 'package' } @@ -41,6 +41,6 @@ export function pluginInstallLimit(plugin: PluginProps, systemFeatures: SystemFe } export default function usePluginInstallLimit(plugin: PluginProps) { - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() return pluginInstallLimit(plugin, systemFeatures) } diff --git a/web/app/components/plugins/install-plugin/install-bundle/index.spec.tsx b/web/app/components/plugins/install-plugin/install-bundle/index.spec.tsx index 1b70cfb5c7..c8ccba4d3f 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/index.spec.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/index.spec.tsx @@ -181,7 +181,7 @@ vi.mock('@/context/mitt-context', () => ({ // Mock global public context vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: () => ({}), + useSystemFeatures: () => ({}), })) // Mock useCanInstallPluginFromMarketplace diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.spec.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.spec.tsx index 48f0703a4b..1d303f389d 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.spec.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.spec.tsx @@ -56,9 +56,9 @@ vi.mock('@/app/components/plugins/install-plugin/hooks/use-check-installed', () }), })) -// Mock useGlobalPublicStore +// Mock useSystemFeatures vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: () => ({}), + useSystemFeatures: () => ({}), })) // Mock pluginInstallLimit diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx index 1b08ca5a04..f2c5c50775 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx @@ -4,7 +4,7 @@ import { produce } from 'immer' import * as React from 'react' import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useFetchPluginsInMarketPlaceByInfo } from '@/service/use-plugins' import LoadingError from '../../base/loading-error' import { pluginInstallLimit } from '../../hooks/use-install-plugin-limit' @@ -38,7 +38,7 @@ const InstallByDSLList = ({ isFromMarketPlace, ref, }: Props) => { - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() // DSL has id, to get plugin info to show more info const { isLoading: isFetchingMarketplaceDataById, data: infoGetById, error: infoByIdError } = useFetchPluginsInMarketPlaceByInfo(allPlugins.filter(d => d.type === 'marketplace').map((d) => { const dependecy = (d as GitHubItemAndMarketPlaceDependency).value diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.spec.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.spec.tsx index 49c3ef1058..4129cef114 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.spec.tsx @@ -66,8 +66,7 @@ vi.mock('@/context/i18n', () => ({ let mockEnableMarketplace = true vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (state: { systemFeatures: { enable_marketplace: boolean } }) => unknown) => - selector({ systemFeatures: { enable_marketplace: mockEnableMarketplace } }), + useSystemFeatures: () => ({ enable_marketplace: mockEnableMarketplace }), })) vi.mock('@/context/modal-context', () => ({ diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 7f7e11ad51..00d617493a 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -25,10 +25,10 @@ import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-m import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker' import { API_PREFIX } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useGetLanguage, useLocale } from '@/context/i18n' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import useTheme from '@/hooks/use-theme' import { uninstallPlugin } from '@/service/plugins' import { useAllToolProviders, useInvalidateAllToolProviders } from '@/service/use-tools' @@ -72,7 +72,7 @@ const DetailHeader = ({ const { setShowUpdatePluginModal } = useModalContext() const { refreshModelProviders } = useProviderContext() const invalidateAllToolProviders = useInvalidateAllToolProviders() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { id, diff --git a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.spec.tsx b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.spec.tsx index 5501526b12..ddcc0bf58d 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.spec.tsx @@ -11,8 +11,7 @@ vi.mock('react-i18next', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (state: { systemFeatures: { enable_marketplace: boolean } }) => T): T => - selector({ systemFeatures: { enable_marketplace: true } }), + useSystemFeatures: () => ({ enable_marketplace: true }), })) vi.mock('@/utils/classnames', () => ({ diff --git a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx index c32dc9ac58..aa896175eb 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx @@ -11,7 +11,7 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { PluginSource } from '../types' @@ -42,7 +42,7 @@ const OperationDropdown: FC = ({ setOpen(!openRef.current) }, [setOpen]) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() return ( ({ // Mock global public store const mockEnableMarketplace = vi.fn(() => true) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (s: any) => any) => - selector({ systemFeatures: { enable_marketplace: mockEnableMarketplace() } }), + useSystemFeatures: () => ({ enable_marketplace: mockEnableMarketplace() }), })) // Mock Action component diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index 3f658c63a8..632e9193ed 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -16,7 +16,7 @@ import Tooltip from '@/app/components/base/tooltip' import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' import { API_PREFIX } from '@/config' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useRenderI18nObject } from '@/hooks/use-i18n' import useTheme from '@/hooks/use-theme' import { cn } from '@/utils/classnames' @@ -85,7 +85,7 @@ const PluginItem: FC = ({ const getValueFromI18nObject = useRenderI18nObject() const title = getValueFromI18nObject(label) const descriptionText = getValueFromI18nObject(description) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const iconFileName = theme === 'dark' && icon_dark ? icon_dark : icon const iconSrc = iconFileName ? (iconFileName.startsWith('http') ? iconFileName : `${API_PREFIX}/workspaces/current/plugin/icon?tenant_id=${tenant_id}&filename=${iconFileName}`) diff --git a/web/app/components/plugins/plugin-page/context.spec.tsx b/web/app/components/plugins/plugin-page/context.spec.tsx index ea52ae1dbd..0ce9c1ef33 100644 --- a/web/app/components/plugins/plugin-page/context.spec.tsx +++ b/web/app/components/plugins/plugin-page/context.spec.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' // Import mocks -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { PluginPageContext, PluginPageContextProvider, usePluginPageContext } from './context' @@ -11,7 +11,7 @@ vi.mock('nuqs', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn(), + useSystemFeatures: vi.fn(), })) vi.mock('../hooks', () => ({ @@ -25,12 +25,11 @@ vi.mock('../hooks', () => ({ ], })) -// Helper function to mock useGlobalPublicStore with marketplace setting +// Helper function to mock useSystemFeatures with marketplace setting const mockGlobalPublicStore = (enableMarketplace: boolean) => { - vi.mocked(useGlobalPublicStore).mockImplementation((selector) => { - const state = { systemFeatures: { enable_marketplace: enableMarketplace } } - return selector(state as Parameters[0]) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ + enable_marketplace: enableMarketplace, + } as ReturnType) } // Test component that uses the context diff --git a/web/app/components/plugins/plugin-page/context.tsx b/web/app/components/plugins/plugin-page/context.tsx index abc4408d62..98198d27f1 100644 --- a/web/app/components/plugins/plugin-page/context.tsx +++ b/web/app/components/plugins/plugin-page/context.tsx @@ -13,7 +13,7 @@ import { createContext, useContextSelector, } from 'use-context-selector' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { PLUGIN_PAGE_TABS_MAP, usePluginPageTabs } from '../hooks' export type PluginPageContextValue = { @@ -63,7 +63,7 @@ export const PluginPageContextProvider = ({ }) const [currentPluginID, setCurrentPluginID] = useState() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const tabs = usePluginPageTabs() const options = useMemo(() => { return enable_marketplace ? tabs : tabs.filter(tab => tab.value !== PLUGIN_PAGE_TABS_MAP.marketplace) diff --git a/web/app/components/plugins/plugin-page/empty/index.spec.tsx b/web/app/components/plugins/plugin-page/empty/index.spec.tsx index 51d4af919d..c6a2603285 100644 --- a/web/app/components/plugins/plugin-page/empty/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/empty/index.spec.tsx @@ -56,14 +56,10 @@ vi.mock('../context', () => ({ // Mock global public store (Zustand store) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (state: any) => any) => { - return selector({ - systemFeatures: { - ...defaultSystemFeatures, - ...mockState.systemFeatures, - }, - }) - }, + useSystemFeatures: () => ({ + ...defaultSystemFeatures, + ...mockState.systemFeatures, + }), })) // Mock useInstalledPluginList hook diff --git a/web/app/components/plugins/plugin-page/empty/index.tsx b/web/app/components/plugins/plugin-page/empty/index.tsx index 7149423d5f..cf097c5ad1 100644 --- a/web/app/components/plugins/plugin-page/empty/index.tsx +++ b/web/app/components/plugins/plugin-page/empty/index.tsx @@ -11,7 +11,7 @@ import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndD import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github' import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useInstalledPluginList } from '@/service/use-plugins' import Line from '../../marketplace/empty/line' import { usePluginPageContext } from '../context' @@ -27,7 +27,7 @@ const Empty = () => { const fileInputRef = useRef(null) const [selectedAction, setSelectedAction] = useState(null) const [selectedFile, setSelectedFile] = useState(null) - const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace, plugin_installation_permission } = useSystemFeatures() const setActiveTab = usePluginPageContext(v => v.setActiveTab) const handleFileChange = (event: React.ChangeEvent) => { diff --git a/web/app/components/plugins/plugin-page/index.spec.tsx b/web/app/components/plugins/plugin-page/index.spec.tsx index 9b7ada2a87..49aa302503 100644 --- a/web/app/components/plugins/plugin-page/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/index.spec.tsx @@ -28,14 +28,9 @@ vi.mock('@/context/i18n', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn((selector) => { - const state = { - systemFeatures: { - enable_marketplace: true, - }, - } - return selector(state) - }), + useSystemFeatures: vi.fn(() => ({ + enable_marketplace: true, + })), })) vi.mock('@/context/app-context', () => ({ @@ -629,14 +624,9 @@ describe('PluginPage Component', () => { it('should handle marketplace disabled', () => { // Mock marketplace disabled vi.mock('@/context/global-public-context', async () => ({ - useGlobalPublicStore: vi.fn((selector) => { - const state = { - systemFeatures: { - enable_marketplace: false, - }, - } - return selector(state) - }), + useSystemFeatures: vi.fn(() => ({ + enable_marketplace: false, + })), })) vi.mocked(useQueryState).mockReturnValue(['discover', vi.fn()]) diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index efb665197a..e47c20675e 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -16,9 +16,9 @@ import TabSlider from '@/app/components/base/tab-slider' import Tooltip from '@/app/components/base/tooltip' import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal' import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useDocLink } from '@/context/i18n' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { usePluginInstallation } from '@/hooks/use-query-params' import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins' import { sleep } from '@/utils' @@ -112,7 +112,7 @@ const PluginPage = ({ const options = usePluginPageContext(v => v.options) const activeTab = usePluginPageContext(v => v.activeTab) const setActiveTab = usePluginPageContext(v => v.setActiveTab) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const isPluginsTab = useMemo(() => activeTab === PLUGIN_PAGE_TABS_MAP.plugins, [activeTab]) const isExploringMarketplace = useMemo(() => { diff --git a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx index 322591a363..d867a7c83f 100644 --- a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx +++ b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx @@ -16,7 +16,7 @@ import { import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github' import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' type Props = { @@ -37,7 +37,7 @@ const InstallPluginDropdown = ({ const [isMenuOpen, setIsMenuOpen] = useState(false) const [selectedAction, setSelectedAction] = useState(null) const [selectedFile, setSelectedFile] = useState(null) - const { enable_marketplace, plugin_installation_permission } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace, plugin_installation_permission } = useSystemFeatures() const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0] diff --git a/web/app/components/plugins/plugin-page/use-reference-setting.spec.ts b/web/app/components/plugins/plugin-page/use-reference-setting.spec.ts index 9f64d3fac5..c18bd14253 100644 --- a/web/app/components/plugins/plugin-page/use-reference-setting.spec.ts +++ b/web/app/components/plugins/plugin-page/use-reference-setting.spec.ts @@ -2,7 +2,7 @@ import { renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' // Import mocks for assertions import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins' import Toast from '../../base/toast' @@ -21,7 +21,7 @@ vi.mock('@/context/app-context', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn(), + useSystemFeatures: vi.fn(), })) vi.mock('@/service/use-plugins', () => ({ @@ -309,14 +309,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => { }) it('should return true when marketplace is enabled and canManagement is true', () => { - vi.mocked(useGlobalPublicStore).mockImplementation((selector) => { - const state = { - systemFeatures: { - enable_marketplace: true, - }, - } - return selector(state as Parameters[0]) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ + enable_marketplace: true, + } as ReturnType) const { result } = renderHook(() => useCanInstallPluginFromMarketplace()) @@ -324,14 +319,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => { }) it('should return false when marketplace is disabled', () => { - vi.mocked(useGlobalPublicStore).mockImplementation((selector) => { - const state = { - systemFeatures: { - enable_marketplace: false, - }, - } - return selector(state as Parameters[0]) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ + enable_marketplace: false, + } as ReturnType) const { result } = renderHook(() => useCanInstallPluginFromMarketplace()) @@ -339,14 +329,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => { }) it('should return false when canManagement is false', () => { - vi.mocked(useGlobalPublicStore).mockImplementation((selector) => { - const state = { - systemFeatures: { - enable_marketplace: true, - }, - } - return selector(state as Parameters[0]) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ + enable_marketplace: true, + } as ReturnType) vi.mocked(useReferenceSettings).mockReturnValue({ data: { @@ -363,14 +348,9 @@ describe('useCanInstallPluginFromMarketplace Hook', () => { }) it('should return false when both marketplace is disabled and canManagement is false', () => { - vi.mocked(useGlobalPublicStore).mockImplementation((selector) => { - const state = { - systemFeatures: { - enable_marketplace: false, - }, - } - return selector(state as Parameters[0]) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ + enable_marketplace: false, + } as ReturnType) vi.mocked(useReferenceSettings).mockReturnValue({ data: { diff --git a/web/app/components/plugins/plugin-page/use-reference-setting.ts b/web/app/components/plugins/plugin-page/use-reference-setting.ts index 88591b4ced..37c1833d29 100644 --- a/web/app/components/plugins/plugin-page/use-reference-setting.ts +++ b/web/app/components/plugins/plugin-page/use-reference-setting.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins' import Toast from '../../base/toast' import { PermissionType } from '../types' @@ -48,7 +48,7 @@ const useReferenceSetting = () => { } export const useCanInstallPluginFromMarketplace = () => { - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { canManagement } = useReferenceSetting() const canInstallPluginFromMarketplace = useMemo(() => { diff --git a/web/app/components/plugins/reference-setting-modal/index.spec.tsx b/web/app/components/plugins/reference-setting-modal/index.spec.tsx index 43056b4e86..88ff96a3f2 100644 --- a/web/app/components/plugins/reference-setting-modal/index.spec.tsx +++ b/web/app/components/plugins/reference-setting-modal/index.spec.tsx @@ -36,9 +36,7 @@ vi.mock('react-i18next', () => ({ // Mock global public store const mockSystemFeatures = { enable_marketplace: true } vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: (selector: (s: { systemFeatures: typeof mockSystemFeatures }) => typeof mockSystemFeatures) => { - return selector({ systemFeatures: mockSystemFeatures }) - }, + useSystemFeatures: () => mockSystemFeatures, })) // Mock Modal component diff --git a/web/app/components/plugins/reference-setting-modal/index.tsx b/web/app/components/plugins/reference-setting-modal/index.tsx index 6283c080da..31b1918981 100644 --- a/web/app/components/plugins/reference-setting-modal/index.tsx +++ b/web/app/components/plugins/reference-setting-modal/index.tsx @@ -9,7 +9,7 @@ import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' import { PermissionType } from '@/app/components/plugins/types' import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import AutoUpdateSetting from './auto-update-setting' import { defaultValue as autoUpdateDefaultValue } from './auto-update-setting/config' import Label from './label' @@ -30,7 +30,7 @@ const PluginSettingModal: FC = ({ const { auto_upgrade: autoUpdateConfig, permission: privilege } = payload || {} const [tempPrivilege, setTempPrivilege] = useState(privilege) const [tempAutoUpdateConfig, setTempAutoUpdateConfig] = useState(autoUpdateConfig || autoUpdateDefaultValue) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const handlePrivilegeChange = useCallback((key: string) => { return (value: PermissionType) => { setTempPrivilege({ diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 90a2fb9277..967278dff4 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -27,11 +27,11 @@ import Toast from '@/app/components/base/toast' import Res from '@/app/components/share/text-generation/result' import RunOnce from '@/app/components/share/text-generation/run-once' import { appDefaultIconBackground, BATCH_CONCURRENCY } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useWebAppStore } from '@/context/web-app-context' import { useAppFavicon } from '@/hooks/use-app-favicon' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { changeLanguage } from '@/i18n-config/client' import { AccessMode } from '@/models/access-control' import { AppSourceType, fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share' @@ -91,7 +91,7 @@ const TextGeneration: FC = ({ doSetInputs(newInputs) inputsRef.current = newInputs }, []) - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const [appId, setAppId] = useState('') const [siteInfo, setSiteInfo] = useState(null) const [customConfig, setCustomConfig] = useState | null>(null) diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index 48fd4ef29d..7620441828 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -14,7 +14,7 @@ import LabelFilter from '@/app/components/tools/labels/filter' import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' import ProviderDetail from '@/app/components/tools/provider/detail' import WorkflowToolEmpty from '@/app/components/tools/provider/empty' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useAllToolProviders } from '@/service/use-tools' import { cn } from '@/utils/classnames' @@ -42,7 +42,7 @@ const ProviderList = () => { // searchParams.get('category') === 'workflow' const { t } = useTranslation() const { getTagLabel } = useTags() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const containerRef = useRef(null) const [activeTab, setActiveTab] = useQueryState('category', { diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts index ffb07e50c3..4864991daf 100644 --- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts @@ -9,7 +9,7 @@ import { useSerialAsyncCallback } from '@/app/components/workflow/hooks/use-seri import { useNodesReadOnly } from '@/app/components/workflow/hooks/use-workflow' import { useWorkflowStore } from '@/app/components/workflow/store' import { API_PREFIX } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { syncWorkflowDraft } from '@/service/workflow' import { useWorkflowRefreshDraft } from '.' @@ -20,7 +20,7 @@ export const useNodesSyncDraft = () => { const { getNodesReadOnly } = useNodesReadOnly() const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft() const params = useParams() - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const getPostParams = useCallback(() => { const { diff --git a/web/app/components/workflow/block-selector/all-start-blocks.tsx b/web/app/components/workflow/block-selector/all-start-blocks.tsx index d122faecf6..978e6d1f2c 100644 --- a/web/app/components/workflow/block-selector/all-start-blocks.tsx +++ b/web/app/components/workflow/block-selector/all-start-blocks.tsx @@ -18,7 +18,7 @@ import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useFeaturedTriggersRecommendations } from '@/service/use-plugins' import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers' import { cn } from '@/utils/classnames' @@ -54,7 +54,7 @@ const AllStartBlocks = ({ const { t } = useTranslation() const [hasStartBlocksContent, setHasStartBlocksContent] = useState(false) const [hasPluginContent, setHasPluginContent] = useState(false) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const pluginRef = useRef(null) const wrapElemRef = useRef(null) diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 29708f6937..5a803dd644 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -20,8 +20,8 @@ import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useGetLanguage } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { getMarketplaceUrl } from '@/utils/var' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' @@ -179,7 +179,7 @@ const AllTools = ({ plugins: notInstalledPlugins = [], } = useMarketplacePlugins() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() useEffect(() => { if (!enable_marketplace) diff --git a/web/app/components/workflow/block-selector/data-sources.tsx b/web/app/components/workflow/block-selector/data-sources.tsx index 5204f4e86c..51358690e6 100644 --- a/web/app/components/workflow/block-selector/data-sources.tsx +++ b/web/app/components/workflow/block-selector/data-sources.tsx @@ -11,8 +11,8 @@ import { useRef, } from 'react' import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useGetLanguage } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' import { PluginCategoryEnum } from '../../plugins/types' @@ -76,7 +76,7 @@ const DataSources = ({ onSelect(BlockEnum.DataSource, toolDefaultValue && defaultValue) }, [onSelect]) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { queryPluginsWithDebounced: fetchPlugins, diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx index b26980e04b..fbb25fad64 100644 --- a/web/app/components/workflow/block-selector/tabs.tsx +++ b/web/app/components/workflow/block-selector/tabs.tsx @@ -8,7 +8,7 @@ import type { import { memo, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useFeaturedToolsRecommendations } from '@/service/use-plugins' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools' import { cn } from '@/utils/classnames' @@ -64,7 +64,7 @@ const Tabs: FC = ({ const { data: workflowTools } = useAllWorkflowTools() const { data: mcpTools } = useAllMCPTools() const invalidateBuiltInTools = useInvalidateAllBuiltInTools() - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const workflowStore = useWorkflowStore() const inRAGPipeline = dataSources.length > 0 const { diff --git a/web/app/components/workflow/block-selector/tool-picker.tsx b/web/app/components/workflow/block-selector/tool-picker.tsx index 12502baf0b..6527ba6d02 100644 --- a/web/app/components/workflow/block-selector/tool-picker.tsx +++ b/web/app/components/workflow/block-selector/tool-picker.tsx @@ -20,7 +20,7 @@ import Toast from '@/app/components/base/toast' import SearchBox from '@/app/components/plugins/marketplace/search-box' import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' import AllTools from '@/app/components/workflow/block-selector/all-tools' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { createCustomCollection, } from '@/service/tools' @@ -94,7 +94,7 @@ const ToolPicker: FC = ({ } const [tags, setTags] = useState([]) - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { data: buildInTools } = useAllBuiltInTools() const { data: customTools } = useAllCustomTools() const invalidateCustomTools = useInvalidateAllCustomTools() diff --git a/web/app/components/workflow/collaboration/hooks/use-collaboration.ts b/web/app/components/workflow/collaboration/hooks/use-collaboration.ts index 4752a1edf7..92b432b3f3 100644 --- a/web/app/components/workflow/collaboration/hooks/use-collaboration.ts +++ b/web/app/components/workflow/collaboration/hooks/use-collaboration.ts @@ -7,7 +7,7 @@ import type { } from '../types/collaboration' import { useEffect, useRef, useState } from 'react' import Toast from '@/app/components/base/toast' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { collaborationManager } from '../core/collaboration-manager' import { CursorService } from '../services/cursor-service' @@ -33,7 +33,7 @@ export function useCollaboration(appId: string, reactFlowStore?: ReactFlowStore) const [state, setState] = useState(initialState) const cursorServiceRef = useRef(null) - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode useEffect(() => { if (!appId || !isCollaborationEnabled) { diff --git a/web/app/components/workflow/hooks/use-leader-restore.ts b/web/app/components/workflow/hooks/use-leader-restore.ts index 8e73bbf52c..c91aa8c552 100644 --- a/web/app/components/workflow/hooks/use-leader-restore.ts +++ b/web/app/components/workflow/hooks/use-leader-restore.ts @@ -6,7 +6,7 @@ import { useReactFlow } from 'reactflow' import { useStore as useAppStore } from '@/app/components/app/store' import { useFeaturesStore } from '@/app/components/base/features/hooks' import Toast from '@/app/components/base/toast' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { collaborationManager } from '../collaboration/core/collaboration-manager' import { useWorkflowStore } from '../store' import { useNodesSyncDraft } from './use-nodes-sync-draft' @@ -123,7 +123,7 @@ export const useLeaderRestore = () => { versionId: string callbacks: RestoreCallbacks | null } | null>(null) - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const requestRestore = useCallback((data: RestoreRequestData, callbacks?: RestoreCallbacks) => { if (!isCollaborationEnabled || !collaborationManager.isConnected() || collaborationManager.getIsLeader()) { diff --git a/web/app/components/workflow/hooks/use-workflow-comment.ts b/web/app/components/workflow/hooks/use-workflow-comment.ts index 54aed4217a..4687521c2b 100644 --- a/web/app/components/workflow/hooks/use-workflow-comment.ts +++ b/web/app/components/workflow/hooks/use-workflow-comment.ts @@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef } from 'react' import { useReactFlow } from 'reactflow' import { collaborationManager } from '@/app/components/workflow/collaboration' import { useAppContext } from '@/context/app-context' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { createWorkflowComment, createWorkflowCommentReply, deleteWorkflowComment, deleteWorkflowCommentReply, fetchWorkflowComment, fetchWorkflowComments, resolveWorkflowComment, updateWorkflowComment, updateWorkflowCommentReply } from '@/service/workflow-comment' import { useStore } from '../store' import { ControlMode } from '../types' @@ -50,7 +50,7 @@ export const useWorkflowComment = () => { appId ? state.mentionableUsersCache[appId] ?? EMPTY_USERS : EMPTY_USERS )) const { userProfile } = useAppContext() - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const commentDetailCacheRef = useRef>(commentDetailCache) const activeCommentIdRef = useRef(null) diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts index 9fc1c871a4..9b39a3dcbe 100644 --- a/web/app/components/workflow/hooks/use-workflow-interactions.ts +++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts @@ -8,7 +8,7 @@ import { useReactFlow } from 'reactflow' import { useStore as useAppStore } from '@/app/components/app/store' import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' import { useEventEmitterContextContext } from '@/context/event-emitter' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { CUSTOM_NODE, NODE_LAYOUT_HORIZONTAL_PADDING, @@ -73,7 +73,7 @@ export const useWorkflowMoveMode = () => { getNodesReadOnly, } = useNodesReadOnly() const { handleSelectionCancel } = useSelectionInteractions() - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const appDetail = useAppStore(state => state.appDetail) const isCommentModeAvailable = isCollaborationEnabled && (appDetail?.mode === 'workflow' || appDetail?.mode === 'advanced-chat') diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 4635a5575c..04f7e5d934 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -16,7 +16,7 @@ import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hook import { PluginCategoryEnum } from '@/app/components/plugins/types' import { CollectionType } from '@/app/components/tools/types' import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useStrategyProviders } from '@/service/use-strategy' import { cn } from '@/utils/classnames' import Tools from '../../../block-selector/tools' @@ -95,7 +95,7 @@ export type AgentStrategySelectorProps = { } export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => { - const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { enable_marketplace } = useSystemFeatures() const { value, onChange } = props const [open, setOpen] = useState(false) diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx index 60de39809e..42486a03a2 100644 --- a/web/app/components/workflow/operator/zoom-in-out.tsx +++ b/web/app/components/workflow/operator/zoom-in-out.tsx @@ -21,7 +21,7 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Divider from '../../base/divider' @@ -79,7 +79,7 @@ const ZoomInOut: FC = ({ workflowReadOnly, getWorkflowReadOnly, } = useWorkflowReadOnly() - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const ZOOM_IN_OUT_OPTIONS = [ [ diff --git a/web/app/components/workflow/skill/hooks/use-skill-save-manager.tsx b/web/app/components/workflow/skill/hooks/use-skill-save-manager.tsx index 9633c5bfec..9912c56c8c 100644 --- a/web/app/components/workflow/skill/hooks/use-skill-save-manager.tsx +++ b/web/app/components/workflow/skill/hooks/use-skill-save-manager.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { useWorkflowStore } from '@/app/components/workflow/store' import { extractToolConfigIds } from '@/app/components/workflow/utils' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { consoleQuery } from '@/service/client' import { useUpdateAppAssetFileContent } from '@/service/use-app-asset' import { skillCollaborationManager } from '../../collaboration/skills/skill-collaboration-manager' @@ -89,7 +89,7 @@ export const SkillSaveProvider = ({ const storeApi = useWorkflowStore() const queryClient = useQueryClient() const updateContent = useUpdateAppAssetFileContent() - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const queueRef = useRef>>(new Map()) const fallbackRegistryRef = useRef>(new Map()) diff --git a/web/app/components/workflow/skill/hooks/use-skill-tree-collaboration.ts b/web/app/components/workflow/skill/hooks/use-skill-tree-collaboration.ts index 4f2ab1e096..cc019e8836 100644 --- a/web/app/components/workflow/skill/hooks/use-skill-tree-collaboration.ts +++ b/web/app/components/workflow/skill/hooks/use-skill-tree-collaboration.ts @@ -4,13 +4,13 @@ import { useQueryClient } from '@tanstack/react-query' import { useCallback, useEffect } from 'react' import { useStore as useAppStore } from '@/app/components/app/store' import { skillCollaborationManager } from '@/app/components/workflow/collaboration/skills/skill-collaboration-manager' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { consoleQuery } from '@/service/client' export const useSkillTreeUpdateEmitter = () => { const appDetail = useAppStore(s => s.appDetail) const appId = appDetail?.id || '' - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode return useCallback((payload: Record = {}) => { if (!appId || !isCollaborationEnabled) @@ -22,7 +22,7 @@ export const useSkillTreeUpdateEmitter = () => { export const useSkillTreeCollaboration = () => { const appDetail = useAppStore(s => s.appDetail) const appId = appDetail?.id || '' - const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode) + const isCollaborationEnabled = useSystemFeatures().enable_collaboration_mode const queryClient = useQueryClient() useEffect(() => { diff --git a/web/app/forgot-password/page.tsx b/web/app/forgot-password/page.tsx index 338f4eaf13..f15b4037fb 100644 --- a/web/app/forgot-password/page.tsx +++ b/web/app/forgot-password/page.tsx @@ -2,8 +2,8 @@ import { useSearchParams } from 'next/navigation' import * as React from 'react' import ChangePasswordForm from '@/app/forgot-password/ChangePasswordForm' -import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Header from '../signin/_header' import ForgotPasswordForm from './ForgotPasswordForm' @@ -12,7 +12,7 @@ const ForgotPassword = () => { useDocumentTitle('') const searchParams = useSearchParams() const token = searchParams.get('token') - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() return (
diff --git a/web/app/install/page.tsx b/web/app/install/page.tsx index db30d5bc5a..06466d3348 100644 --- a/web/app/install/page.tsx +++ b/web/app/install/page.tsx @@ -1,12 +1,12 @@ 'use client' import * as React from 'react' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Header from '../signin/_header' import InstallForm from './installForm' const Install = () => { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() return (
diff --git a/web/app/reset-password/layout.tsx b/web/app/reset-password/layout.tsx index d9b665501d..f4aaab4fa1 100644 --- a/web/app/reset-password/layout.tsx +++ b/web/app/reset-password/layout.tsx @@ -1,11 +1,11 @@ 'use client' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Header from '../signin/_header' export default function SignInLayout({ children }: any) { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() return ( <>
diff --git a/web/app/signin/_header.tsx b/web/app/signin/_header.tsx index 63be6df674..21e9838464 100644 --- a/web/app/signin/_header.tsx +++ b/web/app/signin/_header.tsx @@ -3,8 +3,8 @@ import type { Locale } from '@/i18n-config' import dynamic from 'next/dynamic' import Divider from '@/app/components/base/divider' import LocaleSigninSelect from '@/app/components/base/select/locale-signin' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useLocale } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { setLocaleOnClient } from '@/i18n-config' import { languages } from '@/i18n-config/language' @@ -20,7 +20,7 @@ const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector const Header = () => { const locale = useLocale() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() return (
diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx index c16a580b3a..770b667fed 100644 --- a/web/app/signin/invite-settings/page.tsx +++ b/web/app/signin/invite-settings/page.tsx @@ -12,7 +12,7 @@ import Loading from '@/app/components/base/loading' import { SimpleSelect } from '@/app/components/base/select' import Toast from '@/app/components/base/toast' import { LICENSE_LINK } from '@/constants/link' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { setLocaleOnClient } from '@/i18n-config' import { languages, LanguagesSupported } from '@/i18n-config/language' import { activateMember } from '@/service/common' @@ -22,7 +22,7 @@ import { resolvePostLoginRedirect } from '../utils/post-login-redirect' export default function InviteSettingsPage() { const { t } = useTranslation() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const router = useRouter() const searchParams = useSearchParams() const token = decodeURIComponent(searchParams.get('invite_token') as string) diff --git a/web/app/signin/layout.tsx b/web/app/signin/layout.tsx index 4a1a2f4f58..d1c3d8875e 100644 --- a/web/app/signin/layout.tsx +++ b/web/app/signin/layout.tsx @@ -1,12 +1,12 @@ 'use client' -import { useGlobalPublicStore } from '@/context/global-public-context' - import useDocumentTitle from '@/hooks/use-document-title' + +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' import Header from './_header' export default function SignInLayout({ children }: any) { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() useDocumentTitle('') return ( <> diff --git a/web/app/signin/normal-form.tsx b/web/app/signin/normal-form.tsx index be0feea6c1..5348dc44b8 100644 --- a/web/app/signin/normal-form.tsx +++ b/web/app/signin/normal-form.tsx @@ -6,7 +6,7 @@ import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { IS_CE_EDITION } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { invitationCheck } from '@/service/common' import { useIsLogin } from '@/service/use-common' import { LicenseStatus } from '@/types/feature' @@ -30,7 +30,7 @@ const NormalForm = () => { const [isInitCheckLoading, setInitCheckLoading] = useState(true) const [isRedirecting, setIsRedirecting] = useState(false) const isLoading = isCheckLoading || isInitCheckLoading || isRedirecting - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const [authType, updateAuthType] = useState<'code' | 'password'>('password') const [showORLine, setShowORLine] = useState(false) const [allMethodsAreDisabled, setAllMethodsAreDisabled] = useState(false) diff --git a/web/app/signup/components/input-mail.spec.tsx b/web/app/signup/components/input-mail.spec.tsx index d5acc92153..044f314d62 100644 --- a/web/app/signup/components/input-mail.spec.tsx +++ b/web/app/signup/components/input-mail.spec.tsx @@ -2,8 +2,8 @@ import type { MockedFunction } from 'vitest' import type { SystemFeatures } from '@/types/feature' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useLocale } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useSendMail } from '@/service/use-common' import { defaultSystemFeatures } from '@/types/feature' import Form from './input-mail' @@ -33,7 +33,7 @@ vi.mock('next/link', () => ({ })) vi.mock('@/context/global-public-context', () => ({ - useGlobalPublicStore: vi.fn(), + useSystemFeatures: vi.fn(), })) vi.mock('@/context/i18n', () => ({ @@ -46,7 +46,7 @@ vi.mock('@/service/use-common', () => ({ type UseSendMailResult = ReturnType -const mockUseGlobalPublicStore = useGlobalPublicStore as unknown as MockedFunction +const mockUseSystemFeatures = useSystemFeatures as unknown as MockedFunction const mockUseLocale = useLocale as unknown as MockedFunction const mockUseSendMail = useSendMail as unknown as MockedFunction @@ -57,11 +57,9 @@ const renderForm = ({ brandingEnabled?: boolean isPending?: boolean } = {}) => { - mockUseGlobalPublicStore.mockReturnValue({ - systemFeatures: buildSystemFeatures({ - branding: { enabled: brandingEnabled }, - }), - }) + mockUseSystemFeatures.mockReturnValue(buildSystemFeatures({ + branding: { enabled: brandingEnabled }, + })) mockUseLocale.mockReturnValue('en-US') mockUseSendMail.mockReturnValue({ mutateAsync: mockSubmitMail, diff --git a/web/app/signup/components/input-mail.tsx b/web/app/signup/components/input-mail.tsx index 1b88007ce4..ac086d1d08 100644 --- a/web/app/signup/components/input-mail.tsx +++ b/web/app/signup/components/input-mail.tsx @@ -8,8 +8,8 @@ import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' import Split from '@/app/signin/split' import { emailRegex } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useLocale } from '@/context/i18n' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useSendMail } from '@/service/use-common' type Props = { @@ -21,7 +21,7 @@ export default function Form({ const { t } = useTranslation() const [email, setEmail] = useState('') const locale = useLocale() - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() const { mutateAsync: submitMail, isPending } = useSendMail() diff --git a/web/app/signup/layout.tsx b/web/app/signup/layout.tsx index 6728b66115..535316d8bf 100644 --- a/web/app/signup/layout.tsx +++ b/web/app/signup/layout.tsx @@ -1,12 +1,12 @@ 'use client' import Header from '@/app/signin/_header' -import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { useSystemFeatures } from '@/hooks/use-global-public' import { cn } from '@/utils/classnames' export default function RegisterLayout({ children }: any) { - const { systemFeatures } = useGlobalPublicStore() + const systemFeatures = useSystemFeatures() useDocumentTitle('') return ( <> diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 12000044d6..ca66e9cbf8 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -10,12 +10,12 @@ import { setUserId, setUserProperties } from '@/app/components/base/amplitude' import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' import MaintenanceNotice from '@/app/components/header/maintenance-notice' import { ZENDESK_FIELD_IDS } from '@/config' +import { useSystemFeatures } from '@/hooks/use-global-public' import { useCurrentWorkspace, useLangGeniusVersion, useUserProfile, } from '@/service/use-common' -import { useGlobalPublicStore } from './global-public-context' export type AppContextValue = { userProfile: UserProfileResponse @@ -89,7 +89,7 @@ export type AppContextProviderProps = { export const AppContextProvider: FC = ({ children }) => { const queryClient = useQueryClient() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const { data: userProfileResp } = useUserProfile() const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace, isFetching: isValidatingCurrentWorkspace } = useCurrentWorkspace() const langGeniusVersionQuery = useLangGeniusVersion( diff --git a/web/context/global-public-context.tsx b/web/context/global-public-context.tsx index e3d6902fd1..4e5ad2075d 100644 --- a/web/context/global-public-context.tsx +++ b/web/context/global-public-context.tsx @@ -1,64 +1,14 @@ 'use client' import type { FC, PropsWithChildren } from 'react' -import type { SystemFeatures } from '@/types/feature' -import { useQuery } from '@tanstack/react-query' -import { useEffect } from 'react' -import { create } from 'zustand' import Loading from '@/app/components/base/loading' -import { consoleClient } from '@/service/client' -import { defaultSystemFeatures } from '@/types/feature' -import { fetchSetupStatusWithCache } from '@/utils/setup-status' - -type GlobalPublicStore = { - systemFeatures: SystemFeatures - setSystemFeatures: (systemFeatures: SystemFeatures) => void -} - -export const useGlobalPublicStore = create(set => ({ - systemFeatures: defaultSystemFeatures, - setSystemFeatures: (systemFeatures: SystemFeatures) => set(() => ({ systemFeatures })), -})) - -const systemFeaturesQueryKey = ['systemFeatures'] as const -const setupStatusQueryKey = ['setupStatus'] as const - -async function fetchSystemFeatures() { - return consoleClient.systemFeatures() -} - -export function useSystemFeaturesQuery() { - return useQuery({ - queryKey: systemFeaturesQueryKey, - queryFn: fetchSystemFeatures, - }) -} - -export function useIsSystemFeaturesPending() { - const { isPending } = useSystemFeaturesQuery() - return isPending -} - -export function useSetupStatusQuery() { - return useQuery({ - queryKey: setupStatusQueryKey, - queryFn: fetchSetupStatusWithCache, - staleTime: Infinity, - }) -} +import { useSetupStatusQuery, useSystemFeaturesQuery } from '@/hooks/use-global-public' const GlobalPublicStoreProvider: FC = ({ children, }) => { - const { data, isPending } = useSystemFeaturesQuery() + const { isPending } = useSystemFeaturesQuery() useSetupStatusQuery() - useEffect(() => { - if (data) { - const { setSystemFeatures } = useGlobalPublicStore.getState() - setSystemFeatures({ ...defaultSystemFeatures, ...data }) - } - }, [data]) - if (isPending) return
return <>{children} diff --git a/web/context/web-app-context.tsx b/web/context/web-app-context.tsx index c5488a565c..ebff2d6ede 100644 --- a/web/context/web-app-context.tsx +++ b/web/context/web-app-context.tsx @@ -8,9 +8,9 @@ import { useEffect } from 'react' import { create } from 'zustand' import { getProcessedSystemVariablesFromUrlParams } from '@/app/components/base/chat/utils' import Loading from '@/app/components/base/loading' +import { useIsSystemFeaturesPending } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { useGetWebAppAccessModeByCode } from '@/service/use-share' -import { useIsSystemFeaturesPending } from './global-public-context' type WebAppStore = { shareCode: string | null diff --git a/web/contract/console/system.ts b/web/contract/console/system.ts index bce0a8226e..132018a8d0 100644 --- a/web/contract/console/system.ts +++ b/web/contract/console/system.ts @@ -1,3 +1,4 @@ +import type { SetupStatusResponse } from '@/models/common' import type { SystemFeatures } from '@/types/feature' import { type } from '@orpc/contract' import { base } from '../base' @@ -9,3 +10,11 @@ export const systemFeaturesContract = base }) .input(type()) .output(type()) + +export const setupStatusContract = base + .route({ + path: '/setup', + method: 'GET', + }) + .input(type()) + .output(type()) diff --git a/web/contract/router.ts b/web/contract/router.ts index 85f3ea1c7e..f7262af3ae 100644 --- a/web/contract/router.ts +++ b/web/contract/router.ts @@ -26,7 +26,7 @@ import { getSandboxProviderListContract, saveSandboxProviderConfigContract, } from './console/sandbox-provider' -import { systemFeaturesContract } from './console/system' +import { setupStatusContract, systemFeaturesContract } from './console/system' import { trialAppDatasetsContract, trialAppInfoContract, trialAppParametersContract, trialAppWorkflowsContract } from './console/try-app' import { workflowDraftEnvironmentVariablesContract, @@ -51,6 +51,7 @@ export const consoleRouterContract = { avatar: accountAvatarContract, }, systemFeatures: systemFeaturesContract, + setupStatus: setupStatusContract, trialApps: { info: trialAppInfoContract, datasets: trialAppDatasetsContract, diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index a0d2e2afab..c1671478be 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -24,7 +24,7 @@ }, "__tests__/embedded-user-id-store.test.tsx": { "ts/no-explicit-any": { - "count": 3 + "count": 1 } }, "__tests__/goto-anything/command-selector.test.tsx": { @@ -104,11 +104,6 @@ "count": 1 } }, - "app/(shareLayout)/webapp-reset-password/layout.tsx": { - "ts/no-explicit-any": { - "count": 1 - } - }, "app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx": { "ts/no-explicit-any": { "count": 2 @@ -653,7 +648,7 @@ }, "app/components/apps/app-card.spec.tsx": { "ts/no-explicit-any": { - "count": 22 + "count": 20 } }, "app/components/apps/app-card.tsx": { @@ -1671,7 +1666,7 @@ }, "app/components/custom/custom-web-app-brand/index.spec.tsx": { "ts/no-explicit-any": { - "count": 7 + "count": 6 } }, "app/components/custom/custom-web-app-brand/index.tsx": { @@ -2545,7 +2540,7 @@ }, "app/components/plugins/plugin-item/index.spec.tsx": { "ts/no-explicit-any": { - "count": 10 + "count": 8 } }, "app/components/plugins/plugin-item/index.tsx": { @@ -2568,7 +2563,7 @@ }, "app/components/plugins/plugin-page/empty/index.spec.tsx": { "ts/no-explicit-any": { - "count": 7 + "count": 5 } }, "app/components/plugins/plugin-page/empty/index.tsx": { @@ -4305,11 +4300,6 @@ "count": 1 } }, - "context/global-public-context.tsx": { - "react-refresh/only-export-components": { - "count": 4 - } - }, "context/hooks/use-trigger-events-limit-modal.ts": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 3 diff --git a/web/hooks/use-document-title.spec.ts b/web/hooks/use-document-title.spec.ts index 7ce1e693db..fcf98af73f 100644 --- a/web/hooks/use-document-title.spec.ts +++ b/web/hooks/use-document-title.spec.ts @@ -1,5 +1,5 @@ -import { act, renderHook } from '@testing-library/react' -import { useGlobalPublicStore, useIsSystemFeaturesPending } from '@/context/global-public-context' +import { renderHook } from '@testing-library/react' +import { useIsSystemFeaturesPending, useSystemFeatures } from '@/hooks/use-global-public' /** * Test suite for useDocumentTitle hook * @@ -15,13 +15,10 @@ import { useGlobalPublicStore, useIsSystemFeaturesPending } from '@/context/glob import { defaultSystemFeatures } from '@/types/feature' import useDocumentTitle from './use-document-title' -vi.mock('@/context/global-public-context', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - useIsSystemFeaturesPending: vi.fn(() => false), - } -}) +vi.mock('@/context/global-public-context', () => ({ + useSystemFeatures: vi.fn(() => ({ ...defaultSystemFeatures })), + useIsSystemFeaturesPending: vi.fn(() => false), +})) /** * Test behavior when system features are still loading @@ -30,11 +27,7 @@ vi.mock('@/context/global-public-context', async (importOriginal) => { describe('title should be empty if systemFeatures is pending', () => { beforeEach(() => { vi.mocked(useIsSystemFeaturesPending).mockReturnValue(true) - act(() => { - useGlobalPublicStore.setState({ - systemFeatures: { ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: false } }, - }) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: false } }) }) /** * Test that title stays empty during loading even when a title is provided @@ -59,11 +52,7 @@ describe('title should be empty if systemFeatures is pending', () => { describe('use default branding', () => { beforeEach(() => { vi.mocked(useIsSystemFeaturesPending).mockReturnValue(false) - act(() => { - useGlobalPublicStore.setState({ - systemFeatures: { ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: false } }, - }) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: false } }) }) /** * Test title format with page title and default branding @@ -91,11 +80,7 @@ describe('use default branding', () => { describe('use specific branding', () => { beforeEach(() => { vi.mocked(useIsSystemFeaturesPending).mockReturnValue(false) - act(() => { - useGlobalPublicStore.setState({ - systemFeatures: { ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: true, application_title: 'Test' } }, - }) - }) + vi.mocked(useSystemFeatures).mockReturnValue({ ...defaultSystemFeatures, branding: { ...defaultSystemFeatures.branding, enabled: true, application_title: 'Test' } }) }) /** * Test title format with page title and custom branding diff --git a/web/hooks/use-document-title.ts b/web/hooks/use-document-title.ts index 37b31a7dea..e107ac75da 100644 --- a/web/hooks/use-document-title.ts +++ b/web/hooks/use-document-title.ts @@ -1,12 +1,12 @@ 'use client' import { useFavicon, useTitle } from 'ahooks' import { useEffect } from 'react' -import { useGlobalPublicStore, useIsSystemFeaturesPending } from '@/context/global-public-context' +import { useIsSystemFeaturesPending, useSystemFeatures } from '@/hooks/use-global-public' import { basePath } from '@/utils/var' export default function useDocumentTitle(title: string) { const isPending = useIsSystemFeaturesPending() - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() const prefix = title ? `${title} - ` : '' let titleStr = '' let favicon = '' diff --git a/web/hooks/use-global-public.ts b/web/hooks/use-global-public.ts new file mode 100644 index 0000000000..86da36cd34 --- /dev/null +++ b/web/hooks/use-global-public.ts @@ -0,0 +1,30 @@ +import type { SystemFeatures } from '@/types/feature' +import { useQuery } from '@tanstack/react-query' +import { consoleClient, consoleQuery } from '@/service/client' +import { defaultSystemFeatures } from '@/types/feature' +import { fetchSetupStatusWithCache } from '@/utils/setup-status' + +export function useSystemFeaturesQuery() { + return useQuery({ + queryKey: consoleQuery.systemFeatures.queryKey(), + queryFn: () => consoleClient.systemFeatures(), + }) +} + +export function useSystemFeatures(): SystemFeatures { + const { data } = useSystemFeaturesQuery() + return { ...defaultSystemFeatures, ...data } +} + +export function useIsSystemFeaturesPending() { + const { isPending } = useSystemFeaturesQuery() + return isPending +} + +export function useSetupStatusQuery() { + return useQuery({ + queryKey: consoleQuery.setupStatus.queryKey(), + queryFn: fetchSetupStatusWithCache, + staleTime: Infinity, + }) +} diff --git a/web/models/common.ts b/web/models/common.ts index 62a543672b..00241da1ae 100644 --- a/web/models/common.ts +++ b/web/models/common.ts @@ -23,7 +23,7 @@ export type OauthResponse = { export type SetupStatusResponse = { step: 'finished' | 'not_started' - setup_at?: Date + setup_at?: string } export type InitValidateStatusResponse = { diff --git a/web/service/access-control.ts b/web/service/access-control.ts index c87e01f482..0b46a56af9 100644 --- a/web/service/access-control.ts +++ b/web/service/access-control.ts @@ -1,7 +1,7 @@ import type { AccessControlAccount, AccessControlGroup, AccessMode, Subject } from '@/models/access-control' import type { App } from '@/types/app' import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { get, post } from './base' import { getUserCanAccess } from './share' @@ -71,7 +71,7 @@ export const useUpdateAccessMode = () => { } export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string, isInstalledApp?: boolean, enabled?: boolean }) => { - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() return useQuery({ queryKey: [NAME_SPACE, 'user-can-access-app', appId, systemFeatures.webapp_auth.enabled, isInstalledApp], queryFn: () => { diff --git a/web/service/use-explore.ts b/web/service/use-explore.ts index 3e3b9ff255..7a4fafc98a 100644 --- a/web/service/use-explore.ts +++ b/web/service/use-explore.ts @@ -1,6 +1,6 @@ import type { App, AppCategory } from '@/models/explore' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { useGlobalPublicStore } from '@/context/global-public-context' +import { useSystemFeatures } from '@/hooks/use-global-public' import { AccessMode } from '@/models/access-control' import { fetchAppList, fetchBanners, fetchInstalledAppList, getAppAccessModeByAppId, uninstallApp, updatePinStatus } from './explore' import { AppSourceType, fetchAppMeta, fetchAppParams } from './share' @@ -57,7 +57,7 @@ export const useUpdateAppPinStatus = () => { } export const useGetInstalledAppAccessModeByAppId = (appId: string | null) => { - const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const systemFeatures = useSystemFeatures() return useQuery({ queryKey: [NAME_SPACE, 'appAccessMode', appId, systemFeatures.webapp_auth.enabled], queryFn: () => { diff --git a/web/utils/setup-status.spec.ts b/web/utils/setup-status.spec.ts index be96b43eba..f6dd7c1e76 100644 --- a/web/utils/setup-status.spec.ts +++ b/web/utils/setup-status.spec.ts @@ -1,14 +1,14 @@ import type { SetupStatusResponse } from '@/models/common' - -import { fetchSetupStatus } from '@/service/common' - +import { consoleClient } from '@/service/client' import { fetchSetupStatusWithCache } from './setup-status' -vi.mock('@/service/common', () => ({ - fetchSetupStatus: vi.fn(), +vi.mock('@/service/client', () => ({ + consoleClient: { + setupStatus: vi.fn(), + }, })) -const mockFetchSetupStatus = vi.mocked(fetchSetupStatus) +const mockSetupStatus = vi.mocked(consoleClient.setupStatus) describe('setup-status utilities', () => { beforeEach(() => { @@ -24,7 +24,7 @@ describe('setup-status utilities', () => { const result = await fetchSetupStatusWithCache() expect(result).toEqual({ step: 'finished' }) - expect(mockFetchSetupStatus).not.toHaveBeenCalled() + expect(mockSetupStatus).not.toHaveBeenCalled() }) it('should not modify localStorage when returning cached value', async () => { @@ -39,22 +39,22 @@ describe('setup-status utilities', () => { describe('when cache does not exist', () => { it('should call API and cache finished status', async () => { const apiResponse: SetupStatusResponse = { step: 'finished' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() - expect(mockFetchSetupStatus).toHaveBeenCalledTimes(1) + expect(mockSetupStatus).toHaveBeenCalledTimes(1) expect(result).toEqual(apiResponse) expect(localStorage.getItem('setup_status')).toBe('finished') }) it('should call API and remove cache when not finished', async () => { const apiResponse: SetupStatusResponse = { step: 'not_started' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() - expect(mockFetchSetupStatus).toHaveBeenCalledTimes(1) + expect(mockSetupStatus).toHaveBeenCalledTimes(1) expect(result).toEqual(apiResponse) expect(localStorage.getItem('setup_status')).toBeNull() }) @@ -62,7 +62,7 @@ describe('setup-status utilities', () => { it('should clear stale cache when API returns not_started', async () => { localStorage.setItem('setup_status', 'some_invalid_value') const apiResponse: SetupStatusResponse = { step: 'not_started' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() @@ -75,44 +75,44 @@ describe('setup-status utilities', () => { it('should call API when cache value is empty string', async () => { localStorage.setItem('setup_status', '') const apiResponse: SetupStatusResponse = { step: 'finished' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() - expect(mockFetchSetupStatus).toHaveBeenCalledTimes(1) + expect(mockSetupStatus).toHaveBeenCalledTimes(1) expect(result).toEqual(apiResponse) }) it('should call API when cache value is not "finished"', async () => { localStorage.setItem('setup_status', 'not_started') const apiResponse: SetupStatusResponse = { step: 'finished' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() - expect(mockFetchSetupStatus).toHaveBeenCalledTimes(1) + expect(mockSetupStatus).toHaveBeenCalledTimes(1) expect(result).toEqual(apiResponse) }) it('should call API when localStorage key does not exist', async () => { const apiResponse: SetupStatusResponse = { step: 'finished' } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() - expect(mockFetchSetupStatus).toHaveBeenCalledTimes(1) + expect(mockSetupStatus).toHaveBeenCalledTimes(1) expect(result).toEqual(apiResponse) }) }) describe('API response handling', () => { it('should preserve setup_at from API response', async () => { - const setupDate = new Date('2024-01-01') + const setupDate = '2024-01-01T00:00:00.000Z' const apiResponse: SetupStatusResponse = { step: 'finished', setup_at: setupDate, } - mockFetchSetupStatus.mockResolvedValue(apiResponse) + mockSetupStatus.mockResolvedValue(apiResponse) const result = await fetchSetupStatusWithCache() @@ -122,13 +122,13 @@ describe('setup-status utilities', () => { it('should propagate API errors', async () => { const apiError = new Error('Network error') - mockFetchSetupStatus.mockRejectedValue(apiError) + mockSetupStatus.mockRejectedValue(apiError) await expect(fetchSetupStatusWithCache()).rejects.toThrow('Network error') }) it('should not update cache when API call fails', async () => { - mockFetchSetupStatus.mockRejectedValue(new Error('API error')) + mockSetupStatus.mockRejectedValue(new Error('API error')) await expect(fetchSetupStatusWithCache()).rejects.toThrow() diff --git a/web/utils/setup-status.ts b/web/utils/setup-status.ts index 7a2810bffd..99443349fc 100644 --- a/web/utils/setup-status.ts +++ b/web/utils/setup-status.ts @@ -1,5 +1,5 @@ import type { SetupStatusResponse } from '@/models/common' -import { fetchSetupStatus } from '@/service/common' +import { consoleClient } from '@/service/client' const SETUP_STATUS_KEY = 'setup_status' @@ -10,7 +10,7 @@ export const fetchSetupStatusWithCache = async (): Promise if (isSetupStatusCached()) return { step: 'finished' } - const status = await fetchSetupStatus() + const status = await consoleClient.setupStatus() if (status.step === 'finished') localStorage.setItem(SETUP_STATUS_KEY, 'finished')