use-nodes-sync-draft.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { useCallback } from 'react'
  2. import produce from 'immer'
  3. import { useStoreApi } from 'reactflow'
  4. import { useParams } from 'next/navigation'
  5. import {
  6. useStore,
  7. useWorkflowStore,
  8. } from '../store'
  9. import { BlockEnum } from '../types'
  10. import { useWorkflowUpdate } from '../hooks'
  11. import {
  12. useNodesReadOnly,
  13. } from './use-workflow'
  14. import { syncWorkflowDraft } from '@/service/workflow'
  15. import { useFeaturesStore } from '@/app/components/base/features/hooks'
  16. import { API_PREFIX } from '@/config'
  17. import { useAppDetailContext } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main'
  18. export const useNodesSyncDraft = () => {
  19. const { detail, isCreate, isEdit, isOperation } = useAppDetailContext()
  20. const store = useStoreApi()
  21. const workflowStore = useWorkflowStore()
  22. const featuresStore = useFeaturesStore()
  23. const { getNodesReadOnly } = useNodesReadOnly()
  24. const { handleRefreshWorkflowDraft } = useWorkflowUpdate()
  25. const debouncedSyncWorkflowDraft = useStore(s => s.debouncedSyncWorkflowDraft)
  26. const params = useParams()
  27. const getPostParams = useCallback(() => {
  28. const {
  29. getNodes,
  30. edges,
  31. transform,
  32. } = store.getState()
  33. const [x, y, zoom] = transform
  34. const {
  35. appId,
  36. conversationVariables,
  37. environmentVariables,
  38. syncWorkflowDraftHash,
  39. } = workflowStore.getState()
  40. if (appId && isEdit) {
  41. const nodes = getNodes()
  42. const hasStartNode = nodes.find(node => node.data.type === BlockEnum.Start)
  43. if (!hasStartNode)
  44. return
  45. const features = featuresStore!.getState().features
  46. const producedNodes = produce(nodes, (draft) => {
  47. draft.forEach((node) => {
  48. Object.keys(node.data).forEach((key) => {
  49. if (key.startsWith('_'))
  50. delete node.data[key]
  51. })
  52. })
  53. })
  54. const producedEdges = produce(edges, (draft) => {
  55. draft.forEach((edge) => {
  56. Object.keys(edge.data).forEach((key) => {
  57. if (key.startsWith('_'))
  58. delete edge.data[key]
  59. })
  60. })
  61. })
  62. return {
  63. url: `/apps/${appId}/workflows/draft`,
  64. params: {
  65. graph: {
  66. nodes: producedNodes,
  67. edges: producedEdges,
  68. viewport: {
  69. x,
  70. y,
  71. zoom,
  72. },
  73. },
  74. features: {
  75. opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '',
  76. suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [],
  77. suggested_questions_after_answer: features.suggested,
  78. text_to_speech: features.text2speech,
  79. speech_to_text: features.speech2text,
  80. retriever_resource: features.citation,
  81. sensitive_word_avoidance: features.moderation,
  82. file_upload: features.file,
  83. },
  84. environment_variables: environmentVariables,
  85. conversation_variables: conversationVariables,
  86. hash: syncWorkflowDraftHash,
  87. },
  88. }
  89. }
  90. }, [store, featuresStore, workflowStore])
  91. const syncWorkflowDraftWhenPageClose = useCallback(() => {
  92. if (getNodesReadOnly())
  93. return
  94. const postParams = getPostParams()
  95. if (postParams) {
  96. navigator.sendBeacon(
  97. `${API_PREFIX}/apps/${params.appId}/workflows/draft?_token=${localStorage.getItem('console_token')}`,
  98. JSON.stringify(postParams.params),
  99. )
  100. }
  101. }, [getPostParams, params.appId, getNodesReadOnly])
  102. const doSyncWorkflowDraft = useCallback(async (
  103. notRefreshWhenSyncError?: boolean,
  104. callback?: {
  105. onSuccess?: () => void
  106. onError?: () => void
  107. onSettled?: () => void
  108. },
  109. ) => {
  110. if (getNodesReadOnly())
  111. return
  112. const postParams = getPostParams()
  113. if (postParams) {
  114. const {
  115. setSyncWorkflowDraftHash,
  116. setDraftUpdatedAt,
  117. } = workflowStore.getState()
  118. try {
  119. const res = await syncWorkflowDraft(postParams)
  120. setSyncWorkflowDraftHash(res.hash)
  121. setDraftUpdatedAt(res.updated_at)
  122. callback?.onSuccess && callback.onSuccess()
  123. }
  124. catch (error: any) {
  125. if (error && error.json && !error.bodyUsed) {
  126. error.json().then((err: any) => {
  127. if (err.code === 'draft_workflow_not_sync' && !notRefreshWhenSyncError)
  128. handleRefreshWorkflowDraft()
  129. })
  130. }
  131. callback?.onError && callback.onError()
  132. }
  133. finally {
  134. callback?.onSettled && callback.onSettled()
  135. }
  136. }
  137. }, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft])
  138. const handleSyncWorkflowDraft = useCallback((
  139. sync?: boolean,
  140. notRefreshWhenSyncError?: boolean,
  141. callback?: {
  142. onSuccess?: () => void
  143. onError?: () => void
  144. onSettled?: () => void
  145. },
  146. ) => {
  147. if (getNodesReadOnly())
  148. return
  149. if (sync)
  150. doSyncWorkflowDraft(notRefreshWhenSyncError, callback)
  151. else
  152. debouncedSyncWorkflowDraft(doSyncWorkflowDraft)
  153. }, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, getNodesReadOnly])
  154. return {
  155. doSyncWorkflowDraft,
  156. handleSyncWorkflowDraft,
  157. syncWorkflowDraftWhenPageClose,
  158. }
  159. }