refactor: demo for app store

This commit is contained in:
Stephen Zhou 2026-01-19 17:45:26 +08:00
parent 93ea88d185
commit 51dd7871c5
No known key found for this signature in database
53 changed files with 151 additions and 105 deletions

View File

@ -17,13 +17,13 @@ import * as React from 'react'
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import AppSideBar from '@/app/components/app-sidebar'
import { useStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import Loading from '@/app/components/base/loading'
import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
import { useAppContext } from '@/context/app-context'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import useDocumentTitle from '@/hooks/use-document-title'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'
import s from './style.module.css'
@ -46,10 +46,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const isMobile = media === MediaType.mobile
const { isCurrentWorkspaceEditor, isLoadingCurrentWorkspace } = useAppContext()
const setAppSidebarExpand = useStore(s => s.setAppSidebarExpand)
const setAppSidebarExpand = useAppStore(s => s.setAppSidebarExpand)
const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
const { data: appDetail, isPending, error } = useAppDetail(appId)
const { data: appDetail, isPending, error } = usePrefetchAppDetail(appId)
const navigation = useMemo(() => {
if (!appDetail)

View File

@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import AppCard from '@/app/components/app/overview/app-card'
import TriggerCard from '@/app/components/app/overview/trigger-card'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Loading from '@/app/components/base/loading'
import { ToastContext } from '@/app/components/base/toast'
import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-card'
@ -21,7 +22,7 @@ import {
updateAppSiteConfig,
updateAppSiteStatus,
} from '@/service/apps'
import { useAppDetail, useInvalidateAppDetail } from '@/service/use-apps'
import { useInvalidateAppDetail } from '@/service/use-apps'
import { useAppWorkflow } from '@/service/use-workflow'
import { AppModeEnum } from '@/types/app'
import { asyncRunSafe } from '@/utils'
@ -36,7 +37,7 @@ const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
const { t } = useTranslation()
const docLink = useDocLink()
const { notify } = useContext(ToastContext)
const { data: appDetail } = useAppDetail(appId)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId))
const invalidateAppDetail = useInvalidateAppDetail()
const isWorkflowApp = appDetail?.mode === AppModeEnum.WORKFLOW

View File

@ -8,8 +8,8 @@ import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { TIME_PERIOD_MAPPING as LONG_TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter'
import { AvgResponseTime, AvgSessionInteractions, AvgUserInteractions, ConversationsChart, CostChart, EndUsersChart, MessagesChart, TokenPerSecond, UserSatisfactionRate, WorkflowCostChart, WorkflowDailyTerminalsChart, WorkflowMessagesChart } from '@/app/components/app/overview/app-chart'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { IS_CLOUD_EDITION } from '@/config'
import { useAppDetail } from '@/service/use-apps'
import LongTimeRangePicker from './long-time-range-picker'
import TimeRangePicker from './time-range-picker'
@ -34,7 +34,7 @@ export type IChartViewProps = {
export default function ChartView({ appId, headerRight }: IChartViewProps) {
const { t } = useTranslation()
const { data: appDetail } = useAppDetail(appId)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId))
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
const isWorkflow = appDetail?.mode === 'workflow'
const [period, setPeriod] = useState<PeriodParams>(IS_CLOUD_EDITION

View File

@ -14,7 +14,7 @@ import * as React from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppSideBar from '@/app/components/app-sidebar'
import { useStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vender/pipeline'
import Loading from '@/app/components/base/loading'
import ExtraInfo from '@/app/components/datasets/extra-info'
@ -107,7 +107,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
useDocumentTitle(datasetRes?.name || t('menus.datasets', { ns: 'common' }))
const setAppSidebarExpand = useStore(state => state.setAppSidebarExpand)
const setAppSidebarExpand = useAppStore(state => state.setAppSidebarExpand)
useEffect(() => {
const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'

View File

@ -18,6 +18,7 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
import ContentDialog from '@/app/components/base/content-dialog'
import { ToastContext } from '@/app/components/base/toast'
@ -25,7 +26,7 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps'
import { useAppDetail, useInvalidateAppDetail, useInvalidateAppList } from '@/service/use-apps'
import { useInvalidateAppDetail, useInvalidateAppList } from '@/service/use-apps'
import { fetchWorkflowDraft } from '@/service/workflow'
import { AppModeEnum } from '@/types/app'
import { getRedirection } from '@/utils/app-redirection'
@ -65,7 +66,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx
const { replace } = useRouter()
const { appId } = useParams()
const { onPlanInfoChanged } = useProviderContext()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const invalidateAppDetail = useInvalidateAppDetail()
const invalidateAppList = useInvalidateAppList()
const [open, setOpen] = useState(openState)

View File

@ -7,13 +7,13 @@ import { useParams } from 'next/navigation'
import * as React from 'react'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { useAppContext } from '@/context/app-context'
import { useAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'
import AppIcon from '../base/app-icon'
@ -34,7 +34,7 @@ const AppSidebarDropdown = ({ navigation }: Props) => {
const { t } = useTranslation()
const { appId } = useParams()
const { isCurrentWorkspaceEditor } = useAppContext()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const [detailExpand, setDetailExpand] = useState(false)
const [open, doSetOpen] = useState(false)

View File

@ -4,7 +4,7 @@ import { usePathname } from 'next/navigation'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { cn } from '@/utils/classnames'

View File

@ -42,7 +42,7 @@ import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/access-control'
import { fetchInstalledAppList } from '@/service/explore'
import { useAppDetail, useInvalidateAppDetail } from '@/service/use-apps'
import { useInvalidateAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import { basePath } from '@/utils/var'
import Divider from '../../base/divider'
@ -51,6 +51,7 @@ import Toast from '../../base/toast'
import Tooltip from '../../base/tooltip'
import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '../../workflow/utils'
import AccessControl from '../app-access-control'
import { appStoreSelectors, useAppStore } from '../store'
import PublishWithMultipleModel from './publish-with-multiple-model'
import SuggestedAction from './suggested-action'
@ -146,7 +147,7 @@ const AppPublisher = ({
const [showAppAccessControl, setShowAppAccessControl] = useState(false)
const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false)
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const invalidateAppDetail = useInvalidateAppDetail()
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const { formatTimeFromNow } = useFormatTimeFromNow()

View File

@ -22,9 +22,9 @@ import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { ChangeType, InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types'
import ConfigContext from '@/context/debug-configuration'
import { useAppDetail } from '@/service/use-apps'
import { AppModeEnum, TransferMethod } from '@/types/app'
import { checkKeys, getNewVarInWorkflow, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
import { appStoreSelectors, useAppStore } from '../../../store'
import ConfigSelect from '../config-select'
import ConfigString from '../config-string'
import ModalFoot from '../modal-foot'
@ -74,7 +74,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
const { modelConfig } = useContext(ConfigContext)
const { t } = useTranslation()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const [tempPayload, setTempPayload] = useState<InputVar>(() => normalizeSelectDefaultValue(payload || getNewVarInWorkflow('') as any))
const { type, label, variable, options, max_length } = tempPayload
const modalRef = useRef<HTMLDivElement>(null)

View File

@ -8,7 +8,7 @@ import * as React from 'react'
import { vi } from 'vitest'
import Toast from '@/app/components/base/toast'
import DebugConfigurationContext from '@/context/debug-configuration'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import ConfigVar, { ADD_EXTERNAL_DATA_TOOL } from './index'
@ -47,7 +47,7 @@ vi.mock('next/navigation', () => ({
}))
vi.mock('@/service/use-apps')
const mockUseAppDetail = vi.mocked(useAppDetail)
const mockUseAppDetail = vi.mocked(usePrefetchAppDetail)
type SortableItem = {
id: string
@ -105,7 +105,7 @@ function setupUseAppDetailMock() {
isLoading: false,
isPending: false,
error: null,
} as ReturnType<typeof useAppDetail>)
} as ReturnType<typeof usePrefetchAppDetail>)
}
const renderConfigVar = (props: Partial<IConfigVarProps> = {}, debugOverrides: Partial<DebugConfigurationState> = {}) => {

View File

@ -7,7 +7,7 @@ import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { Inputs, ModelConfig } from '@/models/debug'
import type { PromptVariable } from '@/types/app'
import { fireEvent, render, screen } from '@testing-library/react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import { DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
import { AppModeEnum, ModelModeType, Resolution, TransferMethod } from '@/types/app'
import { APP_CHAT_WITH_MULTIPLE_MODEL } from '../types'

View File

@ -7,7 +7,7 @@ import {
useCallback,
useMemo,
} from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import ChatInputArea from '@/app/components/base/chat/chat/chat-input-area'
import { useFeatures } from '@/app/components/base/features/hooks'
import { useDebugConfigurationContext } from '@/context/debug-configuration'

View File

@ -7,7 +7,7 @@ import type { ProviderContextState } from '@/context/provider-context'
import type { DatasetConfigs, ModelConfig } from '@/models/debug'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { createRef } from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import { ConfigurationMethodEnum, ModelFeatureEnum, ModelStatusEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { CollectionType } from '@/app/components/tools/types'
import { PromptMode } from '@/models/debug'

View File

@ -2,7 +2,7 @@ import type { InputForm } from '@/app/components/base/chat/chat/type'
import type { ChatConfig, ChatItem, OnSend } from '@/app/components/base/chat/types'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { memo, useCallback, useImperativeHandle, useMemo } from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import Avatar from '@/app/components/base/avatar'
import Chat from '@/app/components/base/chat/chat'
import { useChat } from '@/app/components/base/chat/chat/hooks'

View File

@ -21,7 +21,7 @@ import { useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import ChatUserInput from '@/app/components/app/configuration/debug/chat-user-input'
import PromptValuePanel from '@/app/components/app/configuration/prompt-value-panel'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import TextGeneration from '@/app/components/app/text-generate/item'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import AgentLogModal from '@/app/components/base/agent-log-modal'

View File

@ -40,7 +40,7 @@ import {
useFormattingChangedDispatcher,
} from '@/app/components/app/configuration/debug/hooks'
import useAdvancedPromptConfig from '@/app/components/app/configuration/hooks/use-advanced-prompt-config'
import { useStore as useAppStore } from '@/app/components/app/store'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
import Confirm from '@/app/components/base/confirm'
import Divider from '@/app/components/base/divider'
@ -74,7 +74,6 @@ import { PromptMode } from '@/models/debug'
import { updateAppModelConfig } from '@/service/apps'
import { fetchDatasets } from '@/service/datasets'
import { fetchCollectionList } from '@/service/tools'
import { useAppDetail } from '@/service/use-apps'
import { useFileUploadConfig } from '@/service/use-common'
import { AgentStrategy, AppModeEnum, ModelModeType, Resolution, RETRIEVE_TYPE, TransferMethod } from '@/types/app'
import {
@ -105,7 +104,7 @@ const Configuration: FC = () => {
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const { data: appDetail } = useAppDetail(appId)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId))
const latestPublishedAt = useMemo(() => appDetail?.model_config?.updated_at, [appDetail])
const [formattingChanged, setFormattingChanged] = useState(false)

View File

@ -11,7 +11,7 @@ import * as React from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
import FeatureBar from '@/app/components/base/features/new-feature-panel/feature-bar'
import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader'

View File

@ -2,12 +2,12 @@ import type { App, AppIconType } from '@/types/app'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import LogAnnotation from './index'
vi.mock('@/service/use-apps')
const mockUseAppDetail = vi.mocked(useAppDetail)
const mockUseAppDetail = vi.mocked(usePrefetchAppDetail)
const mockRouterPush = vi.fn()
vi.mock('next/navigation', () => ({
@ -72,7 +72,7 @@ function mockAppDetailReturn(app: App | undefined) {
data: app,
isLoading: false,
error: null,
} as ReturnType<typeof useAppDetail>)
} as ReturnType<typeof usePrefetchAppDetail>)
}
describe('LogAnnotation', () => {

View File

@ -10,9 +10,9 @@ import WorkflowLog from '@/app/components/app/workflow-log'
import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type'
import Loading from '@/app/components/base/loading'
import TabSlider from '@/app/components/base/tab-slider-plain'
import { useAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'
import { appStoreSelectors, useAppStore } from '../store'
type Props = {
pageType: PageType
@ -24,7 +24,7 @@ const LogAnnotation: FC<Props> = ({
const { t } = useTranslation()
const router = useRouter()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const options = useMemo(() => {
if (appDetail?.mode === AppModeEnum.COMPLETION)

View File

@ -21,7 +21,7 @@ import { useTranslation } from 'react-i18next'
import { createContext, useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import ModelInfo from '@/app/components/app/log/model-info'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import TextGeneration from '@/app/components/app/text-generate/item'
import ActionButton from '@/app/components/base/action-button'
import Chat from '@/app/components/base/chat/chat'

View File

@ -34,12 +34,13 @@ import { useGlobalPublicStore } from '@/context/global-public-context'
import { useDocLink } from '@/context/i18n'
import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects } from '@/service/access-control'
import { useAppDetail, useInvalidateAppDetail } from '@/service/use-apps'
import { useInvalidateAppDetail } from '@/service/use-apps'
import { useAppWorkflow } from '@/service/use-workflow'
import { AppModeEnum } from '@/types/app'
import { asyncRunSafe } from '@/utils'
import { basePath } from '@/utils/var'
import AccessControl from '../app-access-control'
import { appStoreSelectors, useAppStore } from '../store'
import CustomizeModal from './customize'
import EmbeddedModal from './embedded'
import SettingsModal from './settings'
@ -76,7 +77,7 @@ function AppCard({
const { isCurrentWorkspaceManager, isCurrentWorkspaceEditor } = useAppContext()
const { data: currentWorkflow } = useAppWorkflow(appInfo.mode === AppModeEnum.WORKFLOW ? appInfo.id : '')
const docLink = useDocLink()
const { data: appDetail } = useAppDetail(appInfo.id)
const appDetail = useAppStore(appStoreSelectors.appDetails(appInfo.id))
const [showSettingsModal, setShowSettingsModal] = useState(false)
const [showEmbedded, setShowEmbedded] = useState(false)
const [showCustomizeModal, setShowCustomizeModal] = useState(false)

View File

@ -1,7 +1,13 @@
import type { IChatItem } from '@/app/components/base/chat/chat/type'
import { create } from 'zustand'
import type { App, AppSSO } from '@/types/app'
import { shallow } from 'zustand/shallow'
import { createWithEqualityFn } from 'zustand/traditional'
import { get as serviceGet } from '@/service/base'
type AppDetail = App & Partial<AppSSO>
type State = {
appDetails?: Record<string, AppDetail>
appSidebarExpand: string
currentLogItem?: IChatItem
currentLogModalActiveTab: string
@ -21,7 +27,8 @@ type Action = {
setShowAppConfigureFeaturesModal: (showAppConfigureFeaturesModal: boolean) => void
}
export const useStore = create<State & Action>(set => ({
export const useAppStore = createWithEqualityFn<State & Action>(set => ({
appDetails: undefined,
appSidebarExpand: '',
setAppSidebarExpand: appSidebarExpand => set(() => ({ appSidebarExpand })),
currentLogItem: undefined,
@ -46,4 +53,39 @@ export const useStore = create<State & Action>(set => ({
}),
showAppConfigureFeaturesModal: false,
setShowAppConfigureFeaturesModal: showAppConfigureFeaturesModal => set(() => ({ showAppConfigureFeaturesModal })),
}))
}), shallow)
const appDetails = (appID: string | undefined) => (state: State) => {
if (!appID)
return undefined
return state.appDetails ? state.appDetails[appID] : undefined
}
export const appStoreSelectors = {
appDetails,
}
const get = useAppStore.getState
const set = useAppStore.setState
async function fetchAppDetail(appID: string | undefined) {
if (!appID)
return null
const cur = get().appDetails?.[appID]
if (cur)
return cur
const appDetail = await serviceGet<App>(`/apps/${appID}`)
set(state => ({
appDetails: {
...state.appDetails,
[appID]: appDetail,
},
}))
return appDetail
}
export const appStoreActions = {
fetchAppDetail,
}

View File

@ -20,7 +20,7 @@ import { useParams } from 'next/navigation'
import * as React from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process'
import { useChatContext } from '@/app/components/base/chat/chat/context'

View File

@ -11,7 +11,7 @@
import type { App, AppIconType, AppModeEnum } from '@/types/app'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
import DetailPanel from './detail'
// ============================================================================
@ -19,7 +19,7 @@ import DetailPanel from './detail'
// ============================================================================
vi.mock('@/service/use-apps')
const mockUseAppDetail = vi.mocked(useAppDetail)
const mockUseAppDetail = vi.mocked(usePrefetchAppDetail)
const mockRouterPush = vi.fn()
vi.mock('next/navigation', () => ({
@ -103,7 +103,7 @@ function mockAppDetailReturn(app: App | undefined) {
data: app,
isLoading: false,
error: null,
} as ReturnType<typeof useAppDetail>)
} as ReturnType<typeof usePrefetchAppDetail>)
}
// ============================================================================

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import TooltipPlus from '@/app/components/base/tooltip'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
import Run from '@/app/components/workflow/run'
import { useAppDetail } from '@/service/use-apps'
import { appStoreSelectors, useAppStore } from '../store'
type ILogDetail = {
runID: string
@ -17,7 +17,7 @@ type ILogDetail = {
const DetailPanel: FC<ILogDetail> = ({ runID, onClose, canReplay = false }) => {
const { t } = useTranslation()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const router = useRouter()
const handleReplay = () => {

View File

@ -9,10 +9,10 @@ import * as React from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Loading from '@/app/components/base/loading'
import { ToastContext } from '@/app/components/base/toast'
import { fetchAgentLogDetail } from '@/service/log'
import { useAppDetail } from '@/service/use-apps'
import { cn } from '@/utils/classnames'
import ResultPanel from './result'
import TracingPanel from './tracing'
@ -34,7 +34,7 @@ const AgentLogDetail: FC<AgentLogDetailProps> = ({
const { notify } = useContext(ToastContext)
const [currentTab, setCurrentTab] = useState<string>(activeTab)
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const [loading, setLoading] = useState<boolean>(true)
const [runDetail, setRunDetail] = useState<AgentLogDetailResponse>()
const [list, setList] = useState<AgentIteration[]>([])

View File

@ -23,7 +23,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import AgentLogModal from '@/app/components/base/agent-log-modal'
import Button from '@/app/components/base/button'
import { StopCircle } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'

View File

@ -1,7 +1,7 @@
import type { FC } from 'react'
import type { IChatItem } from '@/app/components/base/chat/chat/type'
import { RiFileList3Line } from '@remixicon/react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import ActionButton from '@/app/components/base/action-button'
type LogProps = {

View File

@ -5,8 +5,8 @@ import { useClickAway } from 'ahooks'
import { useParams } from 'next/navigation'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Run from '@/app/components/workflow/run'
import { useAppDetail } from '@/service/use-apps'
import { cn } from '@/utils/classnames'
type MessageLogModalProps = {
@ -27,7 +27,7 @@ const MessageLogModal: FC<MessageLogModalProps> = ({
const ref = useRef(null)
const [mounted, setMounted] = useState(false)
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
useClickAway(() => {
if (mounted)

View File

@ -1,7 +1,7 @@
import type { Meta, StoryObj } from '@storybook/nextjs'
import type { IChatItem } from '@/app/components/base/chat/chat/type'
import { useEffect } from 'react'
import { useStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import PromptLogModal from '.'
type PromptLogModalProps = React.ComponentProps<typeof PromptLogModal>
@ -28,9 +28,9 @@ const mockLogItem: IChatItem = {
const usePromptLogMocks = () => {
useEffect(() => {
useStore.getState().setCurrentLogItem(mockLogItem)
useAppStore.getState().setCurrentLogItem(mockLogItem)
return () => {
useStore.getState().setCurrentLogItem(undefined)
useAppStore.getState().setCurrentLogItem(undefined)
}
}, [])
}

View File

@ -6,7 +6,7 @@ import { memo, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import Divider from '@/app/components/base/divider'
import { ToastContext } from '@/app/components/base/toast'
import { ChunkingMode } from '@/models/datasets'

View File

@ -7,7 +7,7 @@ import { memo, useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import Divider from '@/app/components/base/divider'
import { ToastContext } from '@/app/components/base/toast'
import ImageUploaderInChunk from '@/app/components/datasets/common/image-uploader/image-uploader-in-chunk'

View File

@ -2,14 +2,14 @@
import Loading from '@/app/components/base/loading'
import ApiServer from '@/app/components/develop/ApiServer'
import Doc from '@/app/components/develop/doc'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
type IDevelopMainProps = {
appId: string
}
const DevelopMain = ({ appId }: IDevelopMainProps) => {
const { data: appDetail, isPending } = useAppDetail(appId)
const { data: appDetail, isPending } = usePrefetchAppDetail(appId)
if (isPending || !appDetail) {
return (

View File

@ -12,8 +12,9 @@ import { useTranslation } from 'react-i18next'
import CreateAppTemplateDialog from '@/app/components/app/create-app-dialog'
import CreateAppModal from '@/app/components/app/create-app-modal'
import CreateFromDSLModal from '@/app/components/app/create-from-dsl-modal'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { useAppContext } from '@/context/app-context'
import { useAppDetail, useInfiniteAppList } from '@/service/use-apps'
import { useInfiniteAppList } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import Nav from '../nav'
@ -21,7 +22,7 @@ const AppNav = () => {
const { t } = useTranslation()
const { appId } = useParams()
const { isCurrentWorkspaceEditor } = useAppContext()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
const [showNewAppTemplateDialog, setShowNewAppTemplateDialog] = useState(false)
const [showCreateFromDSLModal, setShowCreateFromDSLModal] = useState(false)

View File

@ -8,7 +8,7 @@ import Loading from '@/app/components/base/loading'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import AppInputsForm from '@/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-form'
import { BlockEnum, InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types'
import { useAppDetail } from '@/service/use-apps'
import { usePrefetchAppDetail } from '@/service/use-apps'
import { useFileUploadConfig } from '@/service/use-common'
import { useAppWorkflow } from '@/service/use-workflow'
import { AppModeEnum, Resolution } from '@/types/app'
@ -33,7 +33,7 @@ const AppInputsPanel = ({
const inputsRef = useRef<any>(value?.inputs || {})
const isBasicApp = appDetail.mode !== AppModeEnum.ADVANCED_CHAT && appDetail.mode !== AppModeEnum.WORKFLOW
const { data: fileUploadConfig } = useFileUploadConfig()
const { data: currentApp, isFetching: isAppLoading } = useAppDetail(appDetail.id)
const { data: currentApp, isFetching: isAppLoading } = usePrefetchAppDetail(appDetail.id)
const { data: currentWorkflow, isFetching: isWorkflowLoading } = useAppWorkflow(isBasicApp ? '' : appDetail.id)
const isLoading = isAppLoading || isWorkflowLoading

View File

@ -8,6 +8,7 @@ import type { App } from '@/types/app'
import * as React from 'react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import {
PortalToFollowElem,
PortalToFollowElemContent,
@ -16,7 +17,7 @@ import {
import AppInputsPanel from '@/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-panel'
import AppPicker from '@/app/components/plugins/plugin-detail-panel/app-selector/app-picker'
import AppTrigger from '@/app/components/plugins/plugin-detail-panel/app-selector/app-trigger'
import { useAppDetail, useInfiniteAppList } from '@/service/use-apps'
import { useInfiniteAppList } from '@/service/use-apps'
const PAGE_SIZE = 20
@ -71,7 +72,7 @@ const AppSelector: FC<Props> = ({
}, [pages])
// fetch selected app by id to avoid pagination gaps
const { data: selectedAppDetail } = useAppDetail(value?.app_id || '')
const selectedAppDetail = useAppStore(appStoreSelectors.appDetails(value?.app_id || ''))
// Ensure the currently selected app is available for display and in the picker options
const currentAppInfo = useMemo(() => {

View File

@ -2,7 +2,7 @@ import type { IChatItem } from '@/app/components/base/chat/chat/type'
import type { HeaderProps } from '@/app/components/workflow/header'
import type { App } from '@/types/app'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import { AppModeEnum } from '@/types/app'
import WorkflowHeader from './index'

View File

@ -5,9 +5,8 @@ import {
useCallback,
useMemo,
} from 'react'
import { useStore as useAppStore } from '@/app/components/app/store'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Header from '@/app/components/workflow/header'
import { useAppDetail } from '@/service/use-apps'
import { useResetWorkflowVersionHistory } from '@/service/use-workflow'
import { useIsChatMode } from '../../hooks'
import ChatVariableTrigger from './chat-variable-trigger'
@ -15,7 +14,7 @@ import FeaturesTrigger from './features-trigger'
const WorkflowHeader = () => {
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const setCurrentLogItem = useAppStore(state => state.setCurrentLogItem)
const setShowMessageLogModal = useAppStore(state => state.setShowMessageLogModal)
const resetWorkflowVersionHistory = useResetWorkflowVersionHistory()

View File

@ -6,10 +6,9 @@ import {
useMemo,
} from 'react'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Panel from '@/app/components/workflow/panel'
import { useStore } from '@/app/components/workflow/store'
import { useAppDetail } from '@/service/use-apps'
import {
useIsChatMode,
} from '../hooks'
@ -107,7 +106,7 @@ const WorkflowPanelOnRight = () => {
}
const WorkflowPanel = () => {
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const versionHistoryPanelProps = useMemo(() => {
return {
getVersionListUrl: `/apps/${appId}/workflows`,

View File

@ -4,13 +4,13 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { useToastContext } from '@/app/components/base/toast'
import {
DSL_EXPORT_CHECK,
} from '@/app/components/workflow/constants'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { exportAppConfig } from '@/service/apps'
import { useAppDetail } from '@/service/use-apps'
import { fetchWorkflowDraft } from '@/service/workflow'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
@ -22,7 +22,7 @@ export const useDSL = () => {
const { doSyncWorkflowDraft } = useNodesSyncDraft()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const handleExportDSL = useCallback(async (include = false, workflowId?: string) => {
if (!appDetail)

View File

@ -1,10 +1,10 @@
import { useParams } from 'next/navigation'
import { useAppDetail } from '@/service/use-apps'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { AppModeEnum } from '@/types/app'
export const useIsChatMode = () => {
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
return appDetail?.mode === AppModeEnum.ADVANCED_CHAT
}

View File

@ -1,18 +1,19 @@
import type { Edge, Node } from '@/app/components/workflow/types'
import type { FileUploadConfigResponse } from '@/models/common'
import type { FetchWorkflowDraftResponse } from '@/types/workflow'
import { isEqual } from 'es-toolkit/compat'
import { useParams } from 'next/navigation'
import {
useCallback,
useEffect,
useState,
} from 'react'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import {
useStore,
useWorkflowStore,
} from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import { useAppDetail } from '@/service/use-apps'
import { useWorkflowConfig } from '@/service/use-workflow'
import {
fetchNodesDefaultConfigs,
@ -40,7 +41,7 @@ export const useWorkflowInit = () => {
edges: edgesTemplate,
} = useWorkflowTemplate()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string), isEqual)
const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash)
const [data, setData] = useState<FetchWorkflowDraftResponse>()
const [isLoading, setIsLoading] = useState(true)
@ -115,9 +116,9 @@ export const useWorkflowInit = () => {
}, [appDetail, nodesTemplate, edgesTemplate, workflowStore, setSyncWorkflowDraftHash])
useEffect(() => {
if (appDetail)
if (appDetail?.id)
handleGetInitialWorkflowData()
}, [appDetail, handleGetInitialWorkflowData])
}, [appDetail?.id])
const handleFetchPreloadData = useCallback(async () => {
try {

View File

@ -11,6 +11,7 @@ import {
useStoreApi,
} from 'reactflow'
import { v4 as uuidV4 } from 'uuid'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { trackEvent } from '@/app/components/base/amplitude'
import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
@ -22,7 +23,6 @@ import { useWorkflowStore } from '@/app/components/workflow/store'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { handleStream, post, ssePost } from '@/service/base'
import { ContentType } from '@/service/fetch'
import { useAppDetail } from '@/service/use-apps'
import { useInvalidAllLastRun } from '@/service/use-workflow'
import { stopWorkflowRun } from '@/service/workflow'
import { AppModeEnum } from '@/types/app'
@ -64,7 +64,7 @@ export const useWorkflowRun = () => {
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
const pathname = usePathname()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const configsMap = useConfigsMap()
const { flowId, flowType } = configsMap
const invalidAllLastRun = useInvalidAllLastRun(flowType, flowId)

View File

@ -25,11 +25,11 @@ import {
} from '@/app/components/workflow/utils'
import { useAppContext } from '@/context/app-context'
import { fetchRunDetail } from '@/service/log'
import { useAppDetail } from '@/service/use-apps'
import { useAppTriggers } from '@/service/use-tools'
import { AppModeEnum } from '@/types/app'
import WorkflowAppMain from './components/workflow-main'
import { appStoreSelectors, useAppStore } from '../app/store'
import WorkflowAppMain from './components/workflow-main'
import { useGetRunAndTraceUrl } from './hooks/use-get-run-and-trace-url'
import {
useWorkflowInit,
@ -48,7 +48,7 @@ const WorkflowAppWithAdditionalContext = () => {
// Initialize trigger status at application level
const { setTriggerStatuses } = useTriggerStatusStore()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const isWorkflowMode = appDetail?.mode === AppModeEnum.WORKFLOW
const { data: triggersResponse } = useAppTriggers(isWorkflowMode ? appId as string : undefined, {
staleTime: 5 * 60 * 1000, // 5 minutes cache

View File

@ -12,7 +12,7 @@ import {
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import {
PortalToFollowElem,
PortalToFollowElemContent,

View File

@ -22,6 +22,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useEdges, useStoreApi } from 'reactflow'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { useToastContext } from '@/app/components/base/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
@ -29,7 +30,6 @@ import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { MAX_TREE_DEPTH } from '@/config'
import { useGetLanguage } from '@/context/i18n'
import { fetchDatasets } from '@/service/datasets'
import { useAppDetail } from '@/service/use-apps'
import { useStrategyProviders } from '@/service/use-strategy'
import {
useAllBuiltInTools,
@ -98,7 +98,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
const getToolIcon = useGetToolIcon()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const appMode = appDetail?.mode
const shouldCheckStartNode = appMode === AppModeEnum.WORKFLOW || appMode === AppModeEnum.ADVANCED_CHAT
@ -274,7 +274,7 @@ export const useChecklistBeforePublish = () => {
const { data: customTools } = useAllCustomTools()
const { data: workflowTools } = useAllWorkflowTools()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const appMode = appDetail?.mode
const shouldCheckStartNode = appMode === AppModeEnum.WORKFLOW || appMode === AppModeEnum.ADVANCED_CHAT

View File

@ -19,9 +19,9 @@ import {
getOutgoers,
useStoreApi,
} from 'reactflow'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
import { useAppDetail } from '@/service/use-apps'
import { AppModeEnum } from '@/types/app'
import { useNodesMetaData } from '.'
import {
@ -45,7 +45,7 @@ import { useAvailableBlocks } from './use-available-blocks'
export const useIsChatMode = () => {
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
return appDetail?.mode === AppModeEnum.ADVANCED_CHAT
}

View File

@ -20,7 +20,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import Tooltip from '@/app/components/base/tooltip'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
@ -61,7 +61,6 @@ import {
isSupportCustomRunForm,
} from '@/app/components/workflow/utils'
import { useModalContext } from '@/context/modal-context'
import { useAppDetail } from '@/service/use-apps'
import { useAllBuiltInTools } from '@/service/use-tools'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import { FlowType } from '@/types/common'
@ -112,7 +111,7 @@ const BasePanel: FC<BasePanelProps> = ({
const { t } = useTranslation()
const language = useLanguage()
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const { showMessageLogModal } = useAppStore(useShallow(state => ({
showMessageLogModal: state.showMessageLogModal,
})))

View File

@ -10,7 +10,7 @@ import {
import { useTranslation } from 'react-i18next'
import { getNodesBounds, useReactFlow } from 'reactflow'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useAppStore } from '@/app/components/app/store'
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
import {
PortalToFollowElem,

View File

@ -8,12 +8,12 @@ import {
useEffect,
useState,
} from 'react'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Chat from '@/app/components/base/chat/chat'
import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
import Loading from '@/app/components/base/loading'
import { fetchConversationMessages } from '@/service/debug'
import { useAppDetail } from '@/service/use-apps'
import { useWorkflowRun } from '../../hooks'
import {
useStore,
@ -53,7 +53,7 @@ const ChatRecord = () => {
const [chatItemTree, setChatItemTree] = useState<ChatItemInTree[]>([])
const [threadChatItems, setThreadChatItems] = useState<IChatItem[]>([])
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const workflowStore = useWorkflowStore()
const { handleLoadBackupDraft } = useWorkflowRun()
const historyWorkflowData = useStore(s => s.historyWorkflowData)

View File

@ -5,6 +5,7 @@ import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { useParams } from 'next/navigation'
import { memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react'
import { useNodes } from 'reactflow'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Chat from '@/app/components/base/chat/chat'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import { useFeatures } from '@/app/components/base/features/hooks'
@ -14,7 +15,6 @@ import {
fetchSuggestedQuestions,
stopChatMessageResponding,
} from '@/service/debug'
import { useAppDetail } from '@/service/use-apps'
import {
useStore,
useWorkflowStore,
@ -47,7 +47,7 @@ const ChatWrapper = (
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
const startVariables = startNode?.data.variables
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const workflowStore = useWorkflowStore()
const inputs = useStore(s => s.inputs)
const setInputs = useStore(s => s.setInputs)

View File

@ -21,6 +21,7 @@ import {
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
import { appStoreSelectors, useAppStore } from '@/app/components/app/store'
import Button from '@/app/components/base/button'
import Modal from '@/app/components/base/modal'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
@ -35,7 +36,6 @@ import {
importDSL,
importDSLConfirm,
} from '@/service/apps'
import { useAppDetail } from '@/service/use-apps'
import { fetchWorkflowDraft } from '@/service/workflow'
import { AppModeEnum } from '@/types/app'
import { WORKFLOW_DATA_UPDATE } from './constants'
@ -62,7 +62,7 @@ const UpdateDSLModal = ({
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { appId } = useParams()
const { data: appDetail } = useAppDetail(appId as string)
const appDetail = useAppStore(appStoreSelectors.appDetails(appId as string))
const [currentFile, setDSLFile] = useState<File>()
const [fileContent, setFileContent] = useState<string>()
const [loading, setLoading] = useState(false)

View File

@ -17,6 +17,7 @@ import {
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { appStoreActions } from '@/app/components/app/store'
import { AppModeEnum } from '@/types/app'
import { get, post } from './base'
import { useInvalid } from './use-base'
@ -86,10 +87,10 @@ export const useGenerateRuleTemplate = (type: GeneratorType, disabled?: boolean)
})
}
export const useAppDetail = (appID: string) => {
export const usePrefetchAppDetail = (appID: string) => {
return useQuery<App>({
queryKey: [NAME_SPACE, 'detail', appID],
queryFn: () => get<App>(`/apps/${appID}`),
queryFn: () => appStoreActions.fetchAppDetail(appID) as Promise<App>,
enabled: !!appID,
})
}