import _ from './util' ; import store from './store' ; import { tr , noTranslate } from './translations' ; interface UndoState { stack : UndoStackEntry [ ] ; index : number ; localStorageTimer : any ; onChangeCB ? : ( ( ) => void ) | null ; } interface UndoStackEntry { state : any ; actionList ? : ActionChange [ ] ; undoText ? : string | null ; clearCacheTargets ? : ClearCacheTarget [ ] | null ; } const state = { stack : [ ] , index : - 1 , localStorageTimer : null , onChangeCB : null , } ; interface UndoRedoAction { root : any ; op : string ; path : string ; value ? : any ; } interface ActionChange { undo : UndoRedoAction [ ] ; redo : UndoRedoAction [ ] ; } interface MutationChange { mutation : string ; opts : any ; } type ClearCacheTarget = { type : 'csi' ; id ? : number | null ; } | { type : 'pliItem' ; id ? : number | null ; } | CSI | PLIItem | 'csi' | 'pliItem' | 'renderer' ; const api = { onChange ( onChangeCB ) { state . onChangeCB = onChangeCB ; } , commit ( changeList , opts , undoText , clearCacheTargets ? ) { let localChangeList ; if ( typeof changeList === 'string' ) { localChangeList = [ { mutation : changeList , opts } ] ; } else if ( isAction ( changeList ) ) { localChangeList = [ changeList ] ; } else { localChangeList = changeList ; } performClearCacheTargets ( state . index - 1 , state . index ) ; localChangeList . forEach ( change => { if ( isMutation ( change ) ) { const mutation = _ . get ( store . mutations , change . mutation ) ; if ( mutation ) { mutation ( change . opts ) ; } } else if ( isAction ( change ) ) { change . redo . forEach ( ( action ) => { jsonpatch . applyOperation ( action . root , action ) ; } ) ; } } ) ; if ( state . index < state . stack . length - 1 ) { state . stack . splice ( state . index + 1 ) ; } let newState ; if ( _ . isEmpty ( localChangeList . filter ( isMutation ) ) ) { newState = state . stack [ state . stack . length - 1 ] . state ; } else { newState = _ . cloneDeep ( store . state ) ; } state . stack . push ( { state : newState , actionList : localChangeList . filter ( isAction ) , undoText , clearCacheTargets , } ) ; setIndex ( state , state . index + 1 ) ; performClearCacheTargets ( state . index - 1 , state . index ) ; setStateTimer ( ) ; } , saveBaseState ( ) { state . stack = [ { state : _ . cloneDeep ( store . state ) , undoText : null } ] ; setIndex ( state , 0 ) ; } , undo ( ) { if ( api . isUndoAvailable ( ) ) { performUndoRedoAction ( 'undo' , state . index - 1 ) ; } } , redo ( ) { if ( api . isRedoAvailable ( ) ) { performUndoRedoAction ( 'redo' , state . index + 1 ) ; } } , clear ( ) { state . stack = [ ] ; setIndex ( state , - 1 ) ; } , getIndex ( ) { return state . index ; } , getState ( ) { return state ; } , isUndoAvailable ( ) { return state . index > 0 ; } , isRedoAvailable ( ) { return state . index < state . stack . length - 1 ; } , undoText ( ) { return noTranslate ( tr ( 'action.edit.undo.name_@c' , api . isUndoAvailable ( ) ? state . stack [ state . index ] . undoText : '' ) ) ; } , redoText ( ) { return noTranslate ( tr ( 'action.edit.redo.name_@c' , api . isRedoAvailable ( ) ? state . stack [ state . index + 1 ] . undoText : '' ) ) ; } , } ; function isMutation ( change ) { return change ?. hasOwnProperty ( 'mutation' ) || typeof change === 'string' ; } function isAction ( change ) { return change ?. hasOwnProperty ( 'undo' ) && change ?. hasOwnProperty ( 'redo' ) ; } function performUndoRedoAction ( undoOrRedo , newIndex ) { const newStack = state . stack [ newIndex ] ; const newState = _ . cloneDeep ( newStack . state ) ; store . replaceState ( newState ) ; const actionStack = ( undoOrRedo === 'undo' ) ? state . stack [ state . index ] : newStack ; ( actionStack . actionList || [ ] ) . forEach ( ( action ) => { action [ undoOrRedo ] . forEach ( ( subAction ) => { jsonpatch . applyOperation ( subAction . root , subAction ) ; } ) ; } ) ; performClearCacheTargets ( state . index , newIndex ) ; state . index = newIndex ; if ( state . onChangeCB ) { state . onChangeCB ( ) ; } } function performClearCacheTargets ( prevIndex , newIndex ) { const clearCacheTargets = [ ] , stack = state . stack ; if ( stack [ prevIndex ] ?. clearCacheTargets ) { clearCacheTargets . push ( ... stack [ prevIndex ] . clearCacheTargets ) ; } if ( stack [ newIndex ] ?. clearCacheTargets ) { clearCacheTargets . push ( ... stack [ newIndex ] . clearCacheTargets ) ; } clearCacheTargets . forEach ( item => { if ( typeof item === 'string' ) { if ( item === 'renderer' ) { store . mutations . sceneRendering . refreshAll ( ) ; } else if ( item === 'csi' || item === 'pliItem' ) { store . mutations [ item ] . markAllDirty ( ) ; } } else { if ( item . type != null && item . id != null ) { const localItemLookup = { type : item . type , id : item . id } ; const localItem = store . get . lookupToItem ( localItemLookup ) ; if ( localItem ?. type === 'csi' || localItem ?. type === 'pliItem' ) { localItem . isDirty = true ; } } } } ) ; } function setIndex ( stack , newIndex ) { stack . index = newIndex ; stack . onChangeCB ?. ( ) ; } function setStateTimer ( ) { if ( typeof setTimeout === 'function' && state . localStorageTimer == null ) { store . saveLocal ( ) ; state . localStorageTimer = setTimeout ( ( ) => { state . localStorageTimer = null ; } , 30 ) ; } } export default api ;