w-funnel/src/components/admin/builder/providers/BuilderUndoRedoProvider.tsx
dev.daminik00 e98b1bfc05 fix
2025-09-28 15:59:45 +02:00

119 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Provider that wraps the builder and adds undo/redo functionality
* Automatically stores state snapshots when significant changes occur
*/
"use client";
import { createContext, useContext, useEffect, useRef, type ReactNode } from 'react';
import { useBuilderState, useBuilderDispatch } from '@/lib/admin/builder/context';
import { useSimpleUndoRedo } from '@/lib/admin/builder/useSimpleUndoRedo';
import type { BuilderState } from '@/lib/admin/builder/context';
interface UndoRedoContextValue {
canUndo: boolean;
canRedo: boolean;
undo: () => void;
redo: () => void;
store: () => void;
clear: () => void;
resetDirty: () => void; // Сброс isDirty флага
}
const UndoRedoContext = createContext<UndoRedoContextValue | undefined>(undefined);
interface BuilderUndoRedoProviderProps {
children: ReactNode;
}
export function BuilderUndoRedoProvider({ children }: BuilderUndoRedoProviderProps) {
const state = useBuilderState();
const dispatch = useBuilderDispatch();
const previousStateRef = useRef<BuilderState>(state);
const isRestoringRef = useRef(false);
// Функция для сброса isDirty
const resetDirty = () => {
dispatch({ type: 'reset', payload: { ...state, isDirty: false } });
};
const undoRedo = useSimpleUndoRedo(
state,
(newState) => {
isRestoringRef.current = true;
dispatch({ type: 'reset', payload: newState });
}
);
// Auto-store state when significant changes occur
useEffect(() => {
// Don't store if we're in the middle of restoring from undo/redo
if (isRestoringRef.current) {
isRestoringRef.current = false;
previousStateRef.current = state;
return;
}
const prev = previousStateRef.current;
// Check for significant changes that should trigger a store
const shouldStore = (
// Screen count changed
prev.screens.length !== state.screens.length ||
// Selected screen changed
prev.selectedScreenId !== state.selectedScreenId ||
// Meta data changed
JSON.stringify(prev.meta) !== JSON.stringify(state.meta) ||
// Screen structure changed (templates, navigation, etc.)
prev.screens.some((prevScreen, index) => {
const currentScreen = state.screens[index];
if (!currentScreen || prevScreen.id !== currentScreen.id) return true;
return (
prevScreen.template !== currentScreen.template ||
JSON.stringify(prevScreen.navigation) !== JSON.stringify(currentScreen.navigation) ||
JSON.stringify(prevScreen.title) !== JSON.stringify(currentScreen.title)
);
})
);
if (shouldStore) {
// Store the previous state (not current) so we can undo to it
undoRedo.store();
previousStateRef.current = state;
}
}, [state, undoRedo]);
// Store initial state
useEffect(() => {
undoRedo.store();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const contextValue: UndoRedoContextValue = {
canUndo: undoRedo.canUndo,
canRedo: undoRedo.canRedo,
undo: undoRedo.undo,
redo: undoRedo.redo,
store: undoRedo.store,
clear: undoRedo.clear,
resetDirty,
};
return (
<UndoRedoContext.Provider value={contextValue}>
{children}
</UndoRedoContext.Provider>
);
}
export function useBuilderUndoRedo(): UndoRedoContextValue {
const context = useContext(UndoRedoContext);
if (!context) {
throw new Error('useBuilderUndoRedo must be used within BuilderUndoRedoProvider');
}
return context;
}