Spaces:
Building
Building
File size: 5,530 Bytes
a8b3f00 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import {
useCallback,
useRef, useState,
} from 'react'
import { debounce } from 'lodash-es'
import {
useStoreApi,
} from 'reactflow'
import { useTranslation } from 'react-i18next'
import { useWorkflowHistoryStore } from '../workflow-history-store'
/**
* All supported Events that create a new history state.
* Current limitations:
* - InputChange events in Node Panels do not trigger state changes.
* - Resizing UI elements does not trigger state changes.
*/
export enum WorkflowHistoryEvent {
NodeTitleChange = 'NodeTitleChange',
NodeDescriptionChange = 'NodeDescriptionChange',
NodeDragStop = 'NodeDragStop',
NodeChange = 'NodeChange',
NodeConnect = 'NodeConnect',
NodePaste = 'NodePaste',
NodeDelete = 'NodeDelete',
EdgeDelete = 'EdgeDelete',
EdgeDeleteByDeleteBranch = 'EdgeDeleteByDeleteBranch',
NodeAdd = 'NodeAdd',
NodeResize = 'NodeResize',
NoteAdd = 'NoteAdd',
NoteChange = 'NoteChange',
NoteDelete = 'NoteDelete',
LayoutOrganize = 'LayoutOrganize',
}
export const useWorkflowHistory = () => {
const store = useStoreApi()
const { store: workflowHistoryStore } = useWorkflowHistoryStore()
const { t } = useTranslation()
const [undoCallbacks, setUndoCallbacks] = useState<any[]>([])
const [redoCallbacks, setRedoCallbacks] = useState<any[]>([])
const onUndo = useCallback((callback: unknown) => {
setUndoCallbacks((prev: any) => [...prev, callback])
return () => setUndoCallbacks(prev => prev.filter(cb => cb !== callback))
}, [])
const onRedo = useCallback((callback: unknown) => {
setRedoCallbacks((prev: any) => [...prev, callback])
return () => setRedoCallbacks(prev => prev.filter(cb => cb !== callback))
}, [])
const undo = useCallback(() => {
workflowHistoryStore.temporal.getState().undo()
undoCallbacks.forEach(callback => callback())
}, [undoCallbacks, workflowHistoryStore.temporal])
const redo = useCallback(() => {
workflowHistoryStore.temporal.getState().redo()
redoCallbacks.forEach(callback => callback())
}, [redoCallbacks, workflowHistoryStore.temporal])
// Some events may be triggered multiple times in a short period of time.
// We debounce the history state update to avoid creating multiple history states
// with minimal changes.
const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEvent) => {
workflowHistoryStore.setState({
workflowHistoryEvent: event,
nodes: store.getState().getNodes(),
edges: store.getState().edges,
})
}, 500))
const saveStateToHistory = useCallback((event: WorkflowHistoryEvent) => {
switch (event) {
case WorkflowHistoryEvent.NoteChange:
// Hint: Note change does not trigger when note text changes,
// because the note editors have their own history states.
saveStateToHistoryRef.current(event)
break
case WorkflowHistoryEvent.NodeTitleChange:
case WorkflowHistoryEvent.NodeDescriptionChange:
case WorkflowHistoryEvent.NodeDragStop:
case WorkflowHistoryEvent.NodeChange:
case WorkflowHistoryEvent.NodeConnect:
case WorkflowHistoryEvent.NodePaste:
case WorkflowHistoryEvent.NodeDelete:
case WorkflowHistoryEvent.EdgeDelete:
case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
case WorkflowHistoryEvent.NodeAdd:
case WorkflowHistoryEvent.NodeResize:
case WorkflowHistoryEvent.NoteAdd:
case WorkflowHistoryEvent.LayoutOrganize:
case WorkflowHistoryEvent.NoteDelete:
saveStateToHistoryRef.current(event)
break
default:
// We do not create a history state for every event.
// Some events of reactflow may change things the user would not want to undo/redo.
// For example: UI state changes like selecting a node.
break
}
}, [])
const getHistoryLabel = useCallback((event: WorkflowHistoryEvent) => {
switch (event) {
case WorkflowHistoryEvent.NodeTitleChange:
return t('workflow.changeHistory.nodeTitleChange')
case WorkflowHistoryEvent.NodeDescriptionChange:
return t('workflow.changeHistory.nodeDescriptionChange')
case WorkflowHistoryEvent.LayoutOrganize:
case WorkflowHistoryEvent.NodeDragStop:
return t('workflow.changeHistory.nodeDragStop')
case WorkflowHistoryEvent.NodeChange:
return t('workflow.changeHistory.nodeChange')
case WorkflowHistoryEvent.NodeConnect:
return t('workflow.changeHistory.nodeConnect')
case WorkflowHistoryEvent.NodePaste:
return t('workflow.changeHistory.nodePaste')
case WorkflowHistoryEvent.NodeDelete:
return t('workflow.changeHistory.nodeDelete')
case WorkflowHistoryEvent.NodeAdd:
return t('workflow.changeHistory.nodeAdd')
case WorkflowHistoryEvent.EdgeDelete:
case WorkflowHistoryEvent.EdgeDeleteByDeleteBranch:
return t('workflow.changeHistory.edgeDelete')
case WorkflowHistoryEvent.NodeResize:
return t('workflow.changeHistory.nodeResize')
case WorkflowHistoryEvent.NoteAdd:
return t('workflow.changeHistory.noteAdd')
case WorkflowHistoryEvent.NoteChange:
return t('workflow.changeHistory.noteChange')
case WorkflowHistoryEvent.NoteDelete:
return t('workflow.changeHistory.noteDelete')
default:
return 'Unknown Event'
}
}, [t])
return {
store: workflowHistoryStore,
saveStateToHistory,
getHistoryLabel,
undo,
redo,
onUndo,
onRedo,
}
}
|