|
|
| 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<string[]>([]); |
| const [currentHistoryIndex, setCurrentHistoryIndex] = useState<number>(-1); |
| const [canvasWidth, setCanvasWidth] = useState<number>(DEFAULT_CANVAS_WIDTH); |
| const [canvasHeight, setCanvasHeight] = useState<number>(DEFAULT_CANVAS_HEIGHT); |
| const [isLoading, setIsLoading] = useState<boolean>(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) { |
| 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 => { |
| |
| |
| 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); |
| } |
| |
| setCurrentHistoryIndex(newStack.length - 1); |
| return newStack; |
| }); |
| dbSaveCanvasState(dataURL, effectiveWidth, effectiveHeight); |
| }, [currentHistoryIndex, canvasWidth, canvasHeight]); |
|
|
| const updateCanvasState = useCallback((dataURL: string, width: number, height: number) => { |
| |
| handleDrawEnd(dataURL, width, height); |
| }, [handleDrawEnd]); |
|
|
|
|
| const handleUndo = () => { |
| if (currentHistoryIndex > 0) { |
| setCurrentHistoryIndex(prevIndex => prevIndex - 1); |
| |
| |
| |
| |
| |
| } |
| }; |
|
|
| const handleRedo = () => { |
| if (currentHistoryIndex < historyStack.length - 1) { |
| setCurrentHistoryIndex(prevIndex => prevIndex + 1); |
| |
| } |
| }; |
|
|
| return { |
| historyStack, |
| currentHistoryIndex, |
| canvasWidth, |
| canvasHeight, |
| isLoading, |
| currentDataURL, |
| handleDrawEnd, |
| handleUndo, |
| handleRedo, |
| updateCanvasState, |
| }; |
| }; |
|
|