Ver código fonte

chore: workflow syncing modal (#5108)

zxhlyh 10 meses atrás
pai
commit
95443bd551

+ 9 - 0
web/app/components/workflow/header/editing-title.tsx

@@ -10,6 +10,7 @@ const EditingTitle = () => {
   const { formatTimeFromNow } = useWorkflow()
   const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
   const publishedAt = useStore(state => state.publishedAt)
+  const isSyncingWorkflowDraft = useStore(s => s.isSyncingWorkflowDraft)
 
   return (
     <div className='flex items-center h-[18px] text-xs text-gray-500'>
@@ -26,6 +27,14 @@ const EditingTitle = () => {
           ? `${t('workflow.common.published')} ${formatTimeFromNow(publishedAt)}`
           : t('workflow.common.unpublished')
       }
+      {
+        isSyncingWorkflowDraft && (
+          <>
+            <span className='flex items-center mx-1'>·</span>
+            {t('workflow.common.syncingData')}
+          </>
+        )
+      }
     </div>
   )
 }

+ 3 - 1
web/app/components/workflow/hooks/use-workflow-interactions.ts

@@ -57,11 +57,13 @@ export const useWorkflowUpdate = () => {
     const {
       appId,
       setSyncWorkflowDraftHash,
+      setIsSyncingWorkflowDraft,
     } = workflowStore.getState()
+    setIsSyncingWorkflowDraft(true)
     fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => {
       handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdator)
       setSyncWorkflowDraftHash(response.hash)
-    })
+    }).finally(() => setIsSyncingWorkflowDraft(false))
   }, [handleUpdateWorkflowCanvas, workflowStore])
 
   return {

+ 8 - 2
web/app/components/workflow/index.tsx

@@ -55,6 +55,7 @@ import HelpLine from './help-line'
 import CandidateNode from './candidate-node'
 import PanelContextmenu from './panel-contextmenu'
 import NodeContextmenu from './node-contextmenu'
+import SyncingDataModal from './syncing-data-modal'
 import {
   useStore,
   useWorkflowStore,
@@ -99,7 +100,10 @@ const Workflow: FC<WorkflowProps> = memo(({
   const controlMode = useStore(s => s.controlMode)
   const nodeAnimation = useStore(s => s.nodeAnimation)
   const showConfirm = useStore(s => s.showConfirm)
-  const { setShowConfirm } = workflowStore.getState()
+  const {
+    setShowConfirm,
+    setControlPromptEditorRerenderKey,
+  } = workflowStore.getState()
   const {
     handleSyncWorkflowDraft,
     syncWorkflowDraftWhenPageClose,
@@ -113,6 +117,7 @@ const Workflow: FC<WorkflowProps> = memo(({
     if (v.type === WORKFLOW_DATA_UPDATE) {
       setNodes(v.payload.nodes)
       setEdges(v.payload.edges)
+      setTimeout(() => setControlPromptEditorRerenderKey(Date.now()))
     }
   })
 
@@ -135,7 +140,7 @@ const Workflow: FC<WorkflowProps> = memo(({
     if (document.visibilityState === 'hidden')
       syncWorkflowDraftWhenPageClose()
     else if (document.visibilityState === 'visible')
-      handleRefreshWorkflowDraft()
+      setTimeout(() => handleRefreshWorkflowDraft(), 500)
   }, [syncWorkflowDraftWhenPageClose, handleRefreshWorkflowDraft])
 
   useEffect(() => {
@@ -223,6 +228,7 @@ const Workflow: FC<WorkflowProps> = memo(({
       `}
       ref={workflowContainerRef}
     >
+      <SyncingDataModal />
       <CandidateNode />
       <Header />
       <Panel />

+ 3 - 0
web/app/components/workflow/nodes/_base/components/prompt/editor.tsx

@@ -27,6 +27,7 @@ import TooltipPlus from '@/app/components/base/tooltip-plus'
 import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars'
 import Switch from '@/app/components/base/switch'
 import { Jinja } from '@/app/components/base/icons/src/vender/workflow'
+import { useStore } from '@/app/components/workflow/store'
 
 type Props = {
   className?: string
@@ -82,6 +83,7 @@ const Editor: FC<Props> = ({
 }) => {
   const { t } = useTranslation()
   const { eventEmitter } = useEventEmitterContextContext()
+  const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
 
   const isShowHistory = !isChatModel && isChatApp
 
@@ -173,6 +175,7 @@ const Editor: FC<Props> = ({
               ? (
                 <div className={cn(isExpand ? 'grow' : 'max-h-[536px]', 'relative px-3 min-h-[56px]  overflow-y-auto')}>
                   <PromptEditor
+                    key={controlPromptEditorRerenderKey}
                     instanceId={instanceId}
                     compact
                     className='min-h-[56px]'

+ 8 - 0
web/app/components/workflow/store.ts

@@ -125,6 +125,10 @@ type Shape = {
     nodeData: VariableAssignerNodeType
   }
   setEnteringNodePayload: (enteringNodePayload?: Shape['enteringNodePayload']) => void
+  isSyncingWorkflowDraft: boolean
+  setIsSyncingWorkflowDraft: (isSyncingWorkflowDraft: boolean) => void
+  controlPromptEditorRerenderKey: number
+  setControlPromptEditorRerenderKey: (controlPromptEditorRerenderKey: number) => void
 }
 
 export const createWorkflowStore = () => {
@@ -209,6 +213,10 @@ export const createWorkflowStore = () => {
     setConnectingNodePayload: connectingNodePayload => set(() => ({ connectingNodePayload })),
     enteringNodePayload: undefined,
     setEnteringNodePayload: enteringNodePayload => set(() => ({ enteringNodePayload })),
+    isSyncingWorkflowDraft: false,
+    setIsSyncingWorkflowDraft: isSyncingWorkflowDraft => set(() => ({ isSyncingWorkflowDraft })),
+    controlPromptEditorRerenderKey: 0,
+    setControlPromptEditorRerenderKey: controlPromptEditorRerenderKey => set(() => ({ controlPromptEditorRerenderKey })),
   }))
 }
 

+ 15 - 0
web/app/components/workflow/syncing-data-modal.tsx

@@ -0,0 +1,15 @@
+import { useStore } from './store'
+
+const SyncingDataModal = () => {
+  const isSyncingWorkflowDraft = useStore(s => s.isSyncingWorkflowDraft)
+
+  if (!isSyncingWorkflowDraft)
+    return null
+
+  return (
+    <div className='absolute inset-0 z-[9999]'>
+    </div>
+  )
+}
+
+export default SyncingDataModal

+ 1 - 0
web/i18n/en-US/workflow.ts

@@ -67,6 +67,7 @@ const translation = {
     manageInTools: 'Manage in Tools',
     workflowAsToolTip: 'Tool reconfiguration is required after the workflow update.',
     viewDetailInTracingPanel: 'View details',
+    syncingData: 'Syncing data, just a few seconds.',
   },
   errorMsg: {
     fieldRequired: '{{field}} is required',

+ 1 - 0
web/i18n/zh-Hans/workflow.ts

@@ -67,6 +67,7 @@ const translation = {
     manageInTools: '访问工具页',
     workflowAsToolTip: '工作流更新后需要重新配置工具参数',
     viewDetailInTracingPanel: '查看详细信息',
+    syncingData: '同步数据中,只需几秒钟。',
   },
   errorMsg: {
     fieldRequired: '{{field}} 不能为空',