use-nodes-sync-draft.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 { useNodesReadOnly } from './use-workflow'
  12. import { syncWorkflowDraft } from '@/service/workflow'
  13. import { useFeaturesStore } from '@/app/components/base/features/hooks'
  14. import { API_PREFIX } from '@/config'
  15. export const useNodesSyncDraft = () => {
  16. const store = useStoreApi()
  17. const workflowStore = useWorkflowStore()
  18. const featuresStore = useFeaturesStore()
  19. const { getNodesReadOnly } = useNodesReadOnly()
  20. const { handleRefreshWorkflowDraft } = useWorkflowUpdate()
  21. const debouncedSyncWorkflowDraft = useStore(s => s.debouncedSyncWorkflowDraft)
  22. const params = useParams()
  23. const getPostParams = useCallback(() => {
  24. const {
  25. getNodes,
  26. edges,
  27. transform,
  28. } = store.getState()
  29. const [x, y, zoom] = transform
  30. const {
  31. appId,
  32. syncWorkflowDraftHash,
  33. } = workflowStore.getState()
  34. if (appId) {
  35. const nodes = getNodes()
  36. const hasStartNode = nodes.find(node => node.data.type === BlockEnum.Start)
  37. if (!hasStartNode)
  38. return
  39. const features = featuresStore!.getState().features
  40. const producedNodes = produce(nodes, (draft) => {
  41. draft.forEach((node) => {
  42. Object.keys(node.data).forEach((key) => {
  43. if (key.startsWith('_'))
  44. delete node.data[key]
  45. })
  46. })
  47. })
  48. const producedEdges = produce(edges, (draft) => {
  49. draft.forEach((edge) => {
  50. Object.keys(edge.data).forEach((key) => {
  51. if (key.startsWith('_'))
  52. delete edge.data[key]
  53. })
  54. })
  55. })
  56. return {
  57. url: `/apps/${appId}/workflows/draft`,
  58. params: {
  59. graph: {
  60. nodes: producedNodes,
  61. edges: producedEdges,
  62. viewport: {
  63. x,
  64. y,
  65. zoom,
  66. },
  67. },
  68. features: {
  69. opening_statement: features.opening?.opening_statement || '',
  70. suggested_questions: features.opening?.suggested_questions || [],
  71. suggested_questions_after_answer: features.suggested,
  72. text_to_speech: features.text2speech,
  73. speech_to_text: features.speech2text,
  74. retriever_resource: features.citation,
  75. sensitive_word_avoidance: features.moderation,
  76. file_upload: features.file,
  77. },
  78. hash: syncWorkflowDraftHash,
  79. },
  80. }
  81. }
  82. }, [store, featuresStore, workflowStore])
  83. const syncWorkflowDraftWhenPageClose = useCallback(() => {
  84. if (getNodesReadOnly())
  85. return
  86. const postParams = getPostParams()
  87. if (postParams) {
  88. navigator.sendBeacon(
  89. `${API_PREFIX}/apps/${params.appId}/workflows/draft?_token=${localStorage.getItem('console_token')}`,
  90. JSON.stringify(postParams.params),
  91. )
  92. }
  93. }, [getPostParams, params.appId, getNodesReadOnly])
  94. const doSyncWorkflowDraft = useCallback(async (notRefreshWhenSyncError?: boolean) => {
  95. if (getNodesReadOnly())
  96. return
  97. const postParams = getPostParams()
  98. if (postParams) {
  99. const {
  100. setSyncWorkflowDraftHash,
  101. setDraftUpdatedAt,
  102. } = workflowStore.getState()
  103. try {
  104. const res = await syncWorkflowDraft(postParams)
  105. setSyncWorkflowDraftHash(res.hash)
  106. setDraftUpdatedAt(res.updated_at)
  107. }
  108. catch (error: any) {
  109. if (error && error.json && !error.bodyUsed) {
  110. error.json().then((err: any) => {
  111. if (err.code === 'draft_workflow_not_sync' && !notRefreshWhenSyncError)
  112. handleRefreshWorkflowDraft()
  113. })
  114. }
  115. }
  116. }
  117. }, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft])
  118. const handleSyncWorkflowDraft = useCallback((sync?: boolean, notRefreshWhenSyncError?: boolean) => {
  119. if (getNodesReadOnly())
  120. return
  121. if (sync)
  122. doSyncWorkflowDraft(notRefreshWhenSyncError)
  123. else
  124. debouncedSyncWorkflowDraft(doSyncWorkflowDraft)
  125. }, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, getNodesReadOnly])
  126. return {
  127. doSyncWorkflowDraft,
  128. handleSyncWorkflowDraft,
  129. syncWorkflowDraftWhenPageClose,
  130. }
  131. }