import { useState, useEffect, useCallback } from 'react'; import { saveCanvasState as dbSaveCanvasState, loadCanvasState as dbLoadCanvasState, LoadedCanvasState } from '../services/dbService'; import { MAX_HISTORY_STEPS, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT } from '../constants'; import { getBlankCanvasDataURL } from '../utils/canvasUtils'; export interface CanvasHistoryHook { historyStack: string[]; currentHistoryIndex: number; canvasWidth: number; canvasHeight: number; isLoading: boolean; currentDataURL: string | null; handleDrawEnd: (dataURL: string, newWidth?: number, newHeight?: number) => void; handleUndo: () => void; handleRedo: () => void; updateCanvasState: (dataURL: string, width: number, height: number) => void; } export const useCanvasHistory = (): CanvasHistoryHook => { const [historyStack, setHistoryStack] = useState([]); const [currentHistoryIndex, setCurrentHistoryIndex] = useState(-1); const [canvasWidth, setCanvasWidth] = useState(DEFAULT_CANVAS_WIDTH); const [canvasHeight, setCanvasHeight] = useState(DEFAULT_CANVAS_HEIGHT); const [isLoading, setIsLoading] = useState(true); const currentDataURL = historyStack.length > 0 && currentHistoryIndex >= 0 ? historyStack[currentHistoryIndex] : null; const loadInitialCanvas = useCallback(async () => { setIsLoading(true); const savedState: LoadedCanvasState | null = await dbLoadCanvasState(); if (savedState && savedState.dataURL) { // Ensure dataURL exists setCanvasWidth(savedState.width); setCanvasHeight(savedState.height); setHistoryStack([savedState.dataURL]); setCurrentHistoryIndex(0); } else { const initialWidth = DEFAULT_CANVAS_WIDTH; const initialHeight = DEFAULT_CANVAS_HEIGHT; setCanvasWidth(initialWidth); setCanvasHeight(initialHeight); const blankCanvas = getBlankCanvasDataURL(initialWidth, initialHeight); setHistoryStack([blankCanvas]); setCurrentHistoryIndex(0); await dbSaveCanvasState(blankCanvas, initialWidth, initialHeight); } setIsLoading(false); }, []); useEffect(() => { loadInitialCanvas(); }, [loadInitialCanvas]); const handleDrawEnd = useCallback((dataURL: string, newWidth?: number, newHeight?: number) => { const effectiveWidth = newWidth ?? canvasWidth; const effectiveHeight = newHeight ?? canvasHeight; if (newWidth && newWidth !== canvasWidth) setCanvasWidth(newWidth); if (newHeight && newHeight !== canvasHeight) setCanvasHeight(newHeight); setHistoryStack(prevStack => { // If currentHistoryIndex is not at the end, it means we've undone some steps. // New drawing should overwrite the "redo" history. const newStackBase = prevStack.slice(0, currentHistoryIndex + 1); let newStack = [...newStackBase, dataURL]; if (newStack.length > MAX_HISTORY_STEPS) { newStack = newStack.slice(newStack.length - MAX_HISTORY_STEPS); } // Update currentHistoryIndex to point to the new state (end of the new stack) setCurrentHistoryIndex(newStack.length - 1); return newStack; }); dbSaveCanvasState(dataURL, effectiveWidth, effectiveHeight); }, [currentHistoryIndex, canvasWidth, canvasHeight]); // Added canvasWidth, canvasHeight as they are used for effective dimensions. const updateCanvasState = useCallback((dataURL: string, width: number, height: number) => { // This function is for direct updates, like loading an image or AI result handleDrawEnd(dataURL, width, height); }, [handleDrawEnd]); const handleUndo = () => { if (currentHistoryIndex > 0) { setCurrentHistoryIndex(prevIndex => prevIndex - 1); // Autosave current state when undoing to ensure persistence of the "undone to" state. // This is implicit as currentDataURL will update, and if the app were to reload, // it should load the state at currentHistoryIndex. // The currentDataURL used by CanvasComponent will be historyStack[newIndex]. // dbSaveCanvasState(historyStack[currentHistoryIndex - 1], canvasWidth, canvasHeight); // This might be too aggressive, rely on drawEnd } }; const handleRedo = () => { if (currentHistoryIndex < historyStack.length - 1) { setCurrentHistoryIndex(prevIndex => prevIndex + 1); // dbSaveCanvasState(historyStack[currentHistoryIndex + 1], canvasWidth, canvasHeight); // Same as undo, might be too aggressive } }; return { historyStack, currentHistoryIndex, canvasWidth, canvasHeight, isLoading, currentDataURL, handleDrawEnd, handleUndo, handleRedo, updateCanvasState, }; };