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