use-workflow-interactions.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import {
  2. useCallback,
  3. useState,
  4. } from 'react'
  5. import { useTranslation } from 'react-i18next'
  6. import { useReactFlow } from 'reactflow'
  7. import { useWorkflowStore } from '../store'
  8. import { DSL_EXPORT_CHECK, WORKFLOW_DATA_UPDATE } from '../constants'
  9. import type { WorkflowDataUpdator } from '../types'
  10. import {
  11. initialEdges,
  12. initialNodes,
  13. } from '../utils'
  14. import { useEdgesInteractions } from './use-edges-interactions'
  15. import { useNodesInteractions } from './use-nodes-interactions'
  16. import { useNodesSyncDraft } from './use-nodes-sync-draft'
  17. import { useEventEmitterContextContext } from '@/context/event-emitter'
  18. import { fetchWorkflowDraft } from '@/service/workflow'
  19. import { exportAppConfig } from '@/service/apps'
  20. import { useToastContext } from '@/app/components/base/toast'
  21. import { useStore as useAppStore } from '@/app/components/app/store'
  22. export const useWorkflowInteractions = () => {
  23. const workflowStore = useWorkflowStore()
  24. const { handleNodeCancelRunningStatus } = useNodesInteractions()
  25. const { handleEdgeCancelRunningStatus } = useEdgesInteractions()
  26. const handleCancelDebugAndPreviewPanel = useCallback(() => {
  27. workflowStore.setState({
  28. showDebugAndPreviewPanel: false,
  29. workflowRunningData: undefined,
  30. })
  31. handleNodeCancelRunningStatus()
  32. handleEdgeCancelRunningStatus()
  33. }, [workflowStore, handleNodeCancelRunningStatus, handleEdgeCancelRunningStatus])
  34. return {
  35. handleCancelDebugAndPreviewPanel,
  36. }
  37. }
  38. export const useWorkflowUpdate = () => {
  39. const reactflow = useReactFlow()
  40. const workflowStore = useWorkflowStore()
  41. const { eventEmitter } = useEventEmitterContextContext()
  42. const handleUpdateWorkflowCanvas = useCallback((payload: WorkflowDataUpdator) => {
  43. const {
  44. nodes,
  45. edges,
  46. viewport,
  47. } = payload
  48. const { setViewport } = reactflow
  49. eventEmitter?.emit({
  50. type: WORKFLOW_DATA_UPDATE,
  51. payload: {
  52. nodes: initialNodes(nodes, edges),
  53. edges: initialEdges(edges, nodes),
  54. },
  55. } as any)
  56. setViewport(viewport)
  57. }, [eventEmitter, reactflow])
  58. const handleRefreshWorkflowDraft = useCallback(() => {
  59. const {
  60. appId,
  61. setSyncWorkflowDraftHash,
  62. setIsSyncingWorkflowDraft,
  63. setEnvironmentVariables,
  64. setEnvSecrets,
  65. setConversationVariables,
  66. } = workflowStore.getState()
  67. setIsSyncingWorkflowDraft(true)
  68. fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => {
  69. handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdator)
  70. setSyncWorkflowDraftHash(response.hash)
  71. setEnvSecrets((response.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
  72. acc[env.id] = env.value
  73. return acc
  74. }, {} as Record<string, string>))
  75. setEnvironmentVariables(response.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [])
  76. // #TODO chatVar sync#
  77. setConversationVariables(response.conversation_variables || [])
  78. }).finally(() => setIsSyncingWorkflowDraft(false))
  79. }, [handleUpdateWorkflowCanvas, workflowStore])
  80. return {
  81. handleUpdateWorkflowCanvas,
  82. handleRefreshWorkflowDraft,
  83. }
  84. }
  85. export const useDSL = () => {
  86. const { t } = useTranslation()
  87. const { notify } = useToastContext()
  88. const { eventEmitter } = useEventEmitterContextContext()
  89. const [exporting, setExporting] = useState(false)
  90. const { doSyncWorkflowDraft } = useNodesSyncDraft()
  91. const appDetail = useAppStore(s => s.appDetail)
  92. const handleExportDSL = useCallback(async (include = false) => {
  93. if (!appDetail)
  94. return
  95. if (exporting)
  96. return
  97. try {
  98. setExporting(true)
  99. await doSyncWorkflowDraft()
  100. const { data } = await exportAppConfig({
  101. appID: appDetail.id,
  102. include,
  103. })
  104. const a = document.createElement('a')
  105. const file = new Blob([data], { type: 'application/yaml' })
  106. a.href = URL.createObjectURL(file)
  107. a.download = `${appDetail.name}.yml`
  108. a.click()
  109. }
  110. catch (e) {
  111. notify({ type: 'error', message: t('app.exportFailed') })
  112. }
  113. finally {
  114. setExporting(false)
  115. }
  116. }, [appDetail, notify, t, doSyncWorkflowDraft, exporting])
  117. const exportCheck = useCallback(async () => {
  118. if (!appDetail)
  119. return
  120. try {
  121. const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail?.id}/workflows/draft`)
  122. const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret')
  123. if (list.length === 0) {
  124. handleExportDSL()
  125. return
  126. }
  127. eventEmitter?.emit({
  128. type: DSL_EXPORT_CHECK,
  129. payload: {
  130. data: list,
  131. },
  132. } as any)
  133. }
  134. catch (e) {
  135. notify({ type: 'error', message: t('app.exportFailed') })
  136. }
  137. }, [appDetail, eventEmitter, handleExportDSL, notify, t])
  138. return {
  139. exportCheck,
  140. handleExportDSL,
  141. }
  142. }