mirror of
https://github.com/langgenius/dify.git
synced 2026-05-01 16:08:04 +08:00
refactor: extract skill save context, stabilize mutation dependency, and deduplicate cache updates
Split SkillSaveContext and useSkillSaveManager into a separate file to fix react-refresh/only-export-components lint error. Destructure mutateAsync from useUpdateAppAssetFileContent for a stable callback reference, preventing unnecessary useCallback cascade rebuilds. Extract shared patchFileContentCache helper to unify setQueryData logic between updateCachedContent and the collaboration event handler.
This commit is contained in:
@ -17,10 +17,10 @@ import { useSkillMarkdownCollaboration } from '../collaboration/skills/use-skill
|
||||
import { START_TAB_ID } from './constants'
|
||||
import CodeFileEditor from './editor/code-file-editor'
|
||||
import MarkdownFileEditor from './editor/markdown-file-editor'
|
||||
import { useSkillSaveManager } from './hooks/skill-save-context'
|
||||
import { useFileTypeInfo } from './hooks/use-file-type-info'
|
||||
import { useSkillAssetNodeMap } from './hooks/use-skill-asset-tree'
|
||||
import { useSkillFileData } from './hooks/use-skill-file-data'
|
||||
import { useSkillSaveManager } from './hooks/use-skill-save-manager'
|
||||
import StartTabContent from './start-tab'
|
||||
import { getFileLanguage } from './utils/file-utils'
|
||||
import MediaFilePreview from './viewer/media-file-preview'
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import type { FallbackEntry, SaveFileOptions, SaveResult } from './use-skill-save-manager'
|
||||
import * as React from 'react'
|
||||
|
||||
type SkillSaveContextValue = {
|
||||
saveFile: (fileId: string, options?: SaveFileOptions) => Promise<SaveResult>
|
||||
saveAllDirty: () => void
|
||||
registerFallback: (fileId: string, entry: FallbackEntry) => void
|
||||
unregisterFallback: (fileId: string) => void
|
||||
}
|
||||
|
||||
export const SkillSaveContext = React.createContext<SkillSaveContextValue | null>(null)
|
||||
|
||||
export const useSkillSaveManager = () => {
|
||||
const context = React.useContext(SkillSaveContext)
|
||||
if (!context)
|
||||
throw new Error('Missing SkillSaveProvider in the tree')
|
||||
|
||||
return context
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEventListener, useUnmount } from 'ahooks'
|
||||
import { useSkillSaveManager } from './use-skill-save-manager'
|
||||
import { useSkillSaveManager } from './skill-save-context'
|
||||
|
||||
export function useSkillAutoSave(): void {
|
||||
const { saveAllDirty } = useSkillSaveManager()
|
||||
|
||||
@ -5,7 +5,8 @@ import { WorkflowContext } from '@/app/components/workflow/context'
|
||||
import { createWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { START_TAB_ID } from '../constants'
|
||||
import { SkillSaveProvider, useSkillSaveManager } from './use-skill-save-manager'
|
||||
import { useSkillSaveManager } from './skill-save-context'
|
||||
import { SkillSaveProvider } from './use-skill-save-manager'
|
||||
|
||||
const { mockMutateAsync, mockToastNotify } = vi.hoisted(() => ({
|
||||
mockMutateAsync: vi.fn(),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useEventListener } from 'ahooks'
|
||||
import isDeepEqual from 'fast-deep-equal'
|
||||
@ -12,6 +13,7 @@ import { consoleQuery } from '@/service/client'
|
||||
import { useUpdateAppAssetFileContent } from '@/service/use-app-asset'
|
||||
import { skillCollaborationManager } from '../../collaboration/skills/skill-collaboration-manager'
|
||||
import { START_TAB_ID } from '../constants'
|
||||
import { SkillSaveContext } from './skill-save-context'
|
||||
|
||||
type SaveSnapshot = {
|
||||
content: string
|
||||
@ -41,13 +43,6 @@ export type FallbackEntry = {
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
type SkillSaveContextValue = {
|
||||
saveFile: (fileId: string, options?: SaveFileOptions) => Promise<SaveResult>
|
||||
saveAllDirty: () => void
|
||||
registerFallback: (fileId: string, entry: FallbackEntry) => void
|
||||
unregisterFallback: (fileId: string) => void
|
||||
}
|
||||
|
||||
type SkillSaveProviderProps = {
|
||||
appId: string
|
||||
children: React.ReactNode
|
||||
@ -79,7 +74,17 @@ const normalizeMetadata = (
|
||||
return nextMetadata
|
||||
}
|
||||
|
||||
const SkillSaveContext = React.createContext<SkillSaveContextValue | null>(null)
|
||||
const patchFileContentCache = (
|
||||
qc: QueryClient,
|
||||
queryKey: readonly unknown[],
|
||||
serialized: string,
|
||||
) => {
|
||||
qc.setQueryData<CachedFileContent>(queryKey, (existing) => {
|
||||
if (!existing || typeof existing !== 'object')
|
||||
return { content: serialized }
|
||||
return { ...existing, content: serialized }
|
||||
})
|
||||
}
|
||||
|
||||
export const SkillSaveProvider = ({
|
||||
appId,
|
||||
@ -88,7 +93,7 @@ export const SkillSaveProvider = ({
|
||||
const { t } = useTranslation()
|
||||
const storeApi = useWorkflowStore()
|
||||
const queryClient = useQueryClient()
|
||||
const updateContent = useUpdateAppAssetFileContent()
|
||||
const { mutateAsync: updateFileContent } = useUpdateAppAssetFileContent()
|
||||
const isCollaborationEnabled = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
|
||||
const queueRef = useRef<Map<string, Promise<SaveResult>>>(new Map())
|
||||
const fallbackRegistryRef = useRef<Map<string, FallbackEntry>>(new Map())
|
||||
@ -155,17 +160,11 @@ export const SkillSaveProvider = ({
|
||||
const queryKey = consoleQuery.appAsset.getFileContent.queryKey({
|
||||
input: { params: { appId, nodeId: fileId } },
|
||||
})
|
||||
const existing = queryClient.getQueryData<CachedFileContent>(queryKey)
|
||||
const serialized = JSON.stringify({
|
||||
content: snapshot.content,
|
||||
...(snapshot.metadata ? { metadata: snapshot.metadata } : {}),
|
||||
})
|
||||
const nextData: CachedFileContent & { content: string } = {
|
||||
...(existing && typeof existing === 'object' ? existing : {}),
|
||||
content: serialized,
|
||||
}
|
||||
|
||||
queryClient.setQueryData(queryKey, nextData)
|
||||
patchFileContentCache(queryClient, queryKey, serialized)
|
||||
}, [appId, queryClient])
|
||||
|
||||
const performSave = useCallback(async (
|
||||
@ -186,7 +185,7 @@ export const SkillSaveProvider = ({
|
||||
}
|
||||
|
||||
try {
|
||||
await updateContent.mutateAsync({
|
||||
await updateFileContent({
|
||||
appId,
|
||||
nodeId: fileId,
|
||||
payload: {
|
||||
@ -220,7 +219,7 @@ export const SkillSaveProvider = ({
|
||||
catch (error) {
|
||||
return { saved: false, error }
|
||||
}
|
||||
}, [appId, buildSnapshot, isCollaborationEnabled, storeApi, updateCachedContent, updateContent])
|
||||
}, [appId, buildSnapshot, isCollaborationEnabled, storeApi, updateCachedContent, updateFileContent])
|
||||
|
||||
const saveFile = useCallback(async (
|
||||
fileId: string,
|
||||
@ -297,7 +296,7 @@ export const SkillSaveProvider = ({
|
||||
}
|
||||
}, { target: typeof window !== 'undefined' ? window : undefined })
|
||||
|
||||
const value = useMemo<SkillSaveContextValue>(() => ({
|
||||
const value = useMemo(() => ({
|
||||
saveFile,
|
||||
saveAllDirty,
|
||||
registerFallback,
|
||||
@ -320,11 +319,7 @@ export const SkillSaveProvider = ({
|
||||
content: payload.content,
|
||||
...(payload.metadata ? { metadata: payload.metadata } : {}),
|
||||
})
|
||||
const existing = queryClient.getQueryData<CachedFileContent>(queryKey)
|
||||
queryClient.setQueryData(queryKey, {
|
||||
...(existing && typeof existing === 'object' ? existing : {}),
|
||||
content: serialized,
|
||||
})
|
||||
patchFileContentCache(queryClient, queryKey, serialized)
|
||||
|
||||
const state = storeApi.getState()
|
||||
state.clearDraftContent(fileId)
|
||||
@ -342,11 +337,3 @@ export const SkillSaveProvider = ({
|
||||
</SkillSaveContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSkillSaveManager = () => {
|
||||
const context = React.useContext(SkillSaveContext)
|
||||
if (!context)
|
||||
throw new Error('Missing SkillSaveProvider in the tree')
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
@ -4123,11 +4123,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/workflow/skill/hooks/use-skill-save-manager.tsx": {
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/workflow/store/workflow/debug/inspect-vars-slice.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 2
|
||||
|
||||
Reference in New Issue
Block a user