mirror of
https://github.com/langgenius/dify.git
synced 2026-02-27 04:57:08 +08:00
Extract business logic into dedicated hooks to reduce component complexity: - useFileTypeInfo: file type detection (markdown, code, image, video, etc.) - useSkillFileData: data fetching with conditional API calls - useSkillFileSave: save logic with Ctrl+S keyboard shortcut Also fix Vercel best practice: use ternary instead of && for conditional rendering.
84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
import type { TFunction } from 'i18next'
|
|
import type { StoreApi } from 'zustand'
|
|
import type { Shape } from '@/app/components/workflow/store'
|
|
import { useCallback, useEffect } from 'react'
|
|
import Toast from '@/app/components/base/toast'
|
|
import { useUpdateAppAssetFileContent } from '@/service/use-app-asset'
|
|
|
|
type UseSkillFileSaveParams = {
|
|
appId: string
|
|
activeTabId: string | null
|
|
isEditable: boolean
|
|
dirtyContents: Map<string, string>
|
|
dirtyMetadataIds: Set<string>
|
|
originalContent: string
|
|
currentMetadata: Record<string, unknown> | undefined
|
|
storeApi: StoreApi<Shape>
|
|
t: TFunction<'workflow'>
|
|
}
|
|
|
|
/**
|
|
* Hook to handle file save logic and Ctrl+S keyboard shortcut.
|
|
* Returns the save handler function.
|
|
*/
|
|
export function useSkillFileSave({
|
|
appId,
|
|
activeTabId,
|
|
isEditable,
|
|
dirtyContents,
|
|
dirtyMetadataIds,
|
|
originalContent,
|
|
currentMetadata,
|
|
storeApi,
|
|
t,
|
|
}: UseSkillFileSaveParams): () => Promise<void> {
|
|
const updateContent = useUpdateAppAssetFileContent()
|
|
|
|
const handleSave = useCallback(async () => {
|
|
if (!activeTabId || !appId || !isEditable)
|
|
return
|
|
|
|
const content = dirtyContents.get(activeTabId)
|
|
const hasDirtyMetadata = dirtyMetadataIds.has(activeTabId)
|
|
if (content === undefined && !hasDirtyMetadata)
|
|
return
|
|
|
|
try {
|
|
await updateContent.mutateAsync({
|
|
appId,
|
|
nodeId: activeTabId,
|
|
payload: {
|
|
content: content ?? originalContent,
|
|
...(currentMetadata ? { metadata: currentMetadata } : {}),
|
|
},
|
|
})
|
|
storeApi.getState().clearDraftContent(activeTabId)
|
|
storeApi.getState().clearDraftMetadata(activeTabId)
|
|
Toast.notify({
|
|
type: 'success',
|
|
message: t('api.saved', { ns: 'common' }),
|
|
})
|
|
}
|
|
catch (error) {
|
|
Toast.notify({
|
|
type: 'error',
|
|
message: String(error),
|
|
})
|
|
}
|
|
}, [activeTabId, appId, currentMetadata, dirtyContents, dirtyMetadataIds, isEditable, originalContent, storeApi, t, updateContent])
|
|
|
|
useEffect(() => {
|
|
function handleKeyDown(e: KeyboardEvent): void {
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
e.preventDefault()
|
|
handleSave()
|
|
}
|
|
}
|
|
|
|
window.addEventListener('keydown', handleKeyDown)
|
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
}, [handleSave])
|
|
|
|
return handleSave
|
|
}
|