mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-31 10:53:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			183 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 'use client'
 | |
| 
 | |
| import type { MouseEventHandler } from 'react'
 | |
| import {
 | |
|   memo,
 | |
|   useCallback,
 | |
|   useRef,
 | |
|   useState,
 | |
| } from 'react'
 | |
| import { useContext } from 'use-context-selector'
 | |
| import { useTranslation } from 'react-i18next'
 | |
| import {
 | |
|   RiAlertLine,
 | |
|   RiCloseLine,
 | |
| } from '@remixicon/react'
 | |
| import { WORKFLOW_DATA_UPDATE } from './constants'
 | |
| import {
 | |
|   SupportUploadFileTypes,
 | |
| } from './types'
 | |
| import {
 | |
|   initialEdges,
 | |
|   initialNodes,
 | |
| } from './utils'
 | |
| import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
 | |
| import Button from '@/app/components/base/button'
 | |
| import Modal from '@/app/components/base/modal'
 | |
| import { ToastContext } from '@/app/components/base/toast'
 | |
| import { updateWorkflowDraftFromDSL } from '@/service/workflow'
 | |
| import { useEventEmitterContextContext } from '@/context/event-emitter'
 | |
| import { useStore as useAppStore } from '@/app/components/app/store'
 | |
| import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
 | |
| 
 | |
| type UpdateDSLModalProps = {
 | |
|   onCancel: () => void
 | |
|   onBackup: () => void
 | |
|   onImport?: () => void
 | |
| }
 | |
| 
 | |
| const UpdateDSLModal = ({
 | |
|   onCancel,
 | |
|   onBackup,
 | |
|   onImport,
 | |
| }: UpdateDSLModalProps) => {
 | |
|   const { t } = useTranslation()
 | |
|   const { notify } = useContext(ToastContext)
 | |
|   const appDetail = useAppStore(s => s.appDetail)
 | |
|   const [currentFile, setDSLFile] = useState<File>()
 | |
|   const [fileContent, setFileContent] = useState<string>()
 | |
|   const [loading, setLoading] = useState(false)
 | |
|   const { eventEmitter } = useEventEmitterContextContext()
 | |
| 
 | |
|   const readFile = (file: File) => {
 | |
|     const reader = new FileReader()
 | |
|     reader.onload = function (event) {
 | |
|       const content = event.target?.result
 | |
|       setFileContent(content as string)
 | |
|     }
 | |
|     reader.readAsText(file)
 | |
|   }
 | |
| 
 | |
|   const handleFile = (file?: File) => {
 | |
|     setDSLFile(file)
 | |
|     if (file)
 | |
|       readFile(file)
 | |
|     if (!file)
 | |
|       setFileContent('')
 | |
|   }
 | |
| 
 | |
|   const isCreatingRef = useRef(false)
 | |
|   const handleImport: MouseEventHandler = useCallback(async () => {
 | |
|     if (isCreatingRef.current)
 | |
|       return
 | |
|     isCreatingRef.current = true
 | |
|     if (!currentFile)
 | |
|       return
 | |
|     try {
 | |
|       if (appDetail && fileContent) {
 | |
|         setLoading(true)
 | |
|         const {
 | |
|           graph,
 | |
|           features,
 | |
|           hash,
 | |
|         } = await updateWorkflowDraftFromDSL(appDetail.id, fileContent)
 | |
|         const { nodes, edges, viewport } = graph
 | |
|         const newFeatures = {
 | |
|           file: {
 | |
|             image: {
 | |
|               enabled: !!features.file_upload?.image?.enabled,
 | |
|               number_limits: features.file_upload?.image?.number_limits || 3,
 | |
|               transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
 | |
|             },
 | |
|             enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
 | |
|             allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
 | |
|             allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
 | |
|             allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
 | |
|             number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
 | |
|           },
 | |
|           opening: {
 | |
|             enabled: !!features.opening_statement,
 | |
|             opening_statement: features.opening_statement,
 | |
|             suggested_questions: features.suggested_questions,
 | |
|           },
 | |
|           suggested: features.suggested_questions_after_answer || { enabled: false },
 | |
|           speech2text: features.speech_to_text || { enabled: false },
 | |
|           text2speech: features.text_to_speech || { enabled: false },
 | |
|           citation: features.retriever_resource || { enabled: false },
 | |
|           moderation: features.sensitive_word_avoidance || { enabled: false },
 | |
|         }
 | |
|         eventEmitter?.emit({
 | |
|           type: WORKFLOW_DATA_UPDATE,
 | |
|           payload: {
 | |
|             nodes: initialNodes(nodes, edges),
 | |
|             edges: initialEdges(edges, nodes),
 | |
|             viewport,
 | |
|             features: newFeatures,
 | |
|             hash,
 | |
|           },
 | |
|         } as any)
 | |
|         if (onImport)
 | |
|           onImport()
 | |
|         notify({ type: 'success', message: t('workflow.common.importSuccess') })
 | |
|         setLoading(false)
 | |
|         onCancel()
 | |
|       }
 | |
|     }
 | |
|     catch (e) {
 | |
|       setLoading(false)
 | |
|       notify({ type: 'error', message: t('workflow.common.importFailure') })
 | |
|     }
 | |
|     isCreatingRef.current = false
 | |
|   }, [currentFile, fileContent, onCancel, notify, t, eventEmitter, appDetail, onImport])
 | |
| 
 | |
|   return (
 | |
|     <Modal
 | |
|       className='p-6 w-[520px] rounded-2xl'
 | |
|       isShow={true}
 | |
|       onClose={() => {}}
 | |
|     >
 | |
|       <div className='flex items-center justify-between mb-6'>
 | |
|         <div className='text-2xl font-semibold text-[#101828]'>{t('workflow.common.importDSL')}</div>
 | |
|         <div className='flex items-center justify-center w-[22px] h-[22px] cursor-pointer' onClick={onCancel}>
 | |
|           <RiCloseLine className='w-5 h-5 text-gray-500' />
 | |
|         </div>
 | |
|       </div>
 | |
|       <div className='flex mb-4 px-4 py-3 bg-[#FFFAEB] rounded-xl border border-[#FEDF89]'>
 | |
|         <RiAlertLine className='shrink-0 mt-0.5 mr-2 w-4 h-4 text-[#F79009]' />
 | |
|         <div>
 | |
|           <div className='mb-2 text-sm font-medium text-[#354052]'>{t('workflow.common.importDSLTip')}</div>
 | |
|           <Button
 | |
|             variant='secondary-accent'
 | |
|             onClick={onBackup}
 | |
|           >
 | |
|             {t('workflow.common.backupCurrentDraft')}
 | |
|           </Button>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div className='mb-8'>
 | |
|         <div className='mb-1 text-[13px] font-semibold text-[#354052]'>
 | |
|           {t('workflow.common.chooseDSL')}
 | |
|         </div>
 | |
|         <Uploader
 | |
|           file={currentFile}
 | |
|           updateFile={handleFile}
 | |
|           className='!mt-0'
 | |
|         />
 | |
|       </div>
 | |
|       <div className='flex justify-end'>
 | |
|         <Button className='mr-2' onClick={onCancel}>{t('app.newApp.Cancel')}</Button>
 | |
|         <Button
 | |
|           disabled={!currentFile || loading}
 | |
|           variant='warning'
 | |
|           onClick={handleImport}
 | |
|           loading={loading}
 | |
|         >
 | |
|           {t('workflow.common.overwriteAndImport')}
 | |
|         </Button>
 | |
|       </div>
 | |
|     </Modal>
 | |
|   )
 | |
| }
 | |
| 
 | |
| export default memo(UpdateDSLModal)
 | 
