diff --git a/apps/collabydraw/canvas-engine/CanvasEngine.ts b/apps/collabydraw/canvas-engine/CanvasEngine.ts index cc43a81..f06c058 100644 --- a/apps/collabydraw/canvas-engine/CanvasEngine.ts +++ b/apps/collabydraw/canvas-engine/CanvasEngine.ts @@ -194,7 +194,22 @@ export class CanvasEngine { // console.log("✅Connected to WebSocket…"); } } + +public getShapes(): Shape[] { + return this.existingShapes; +} + public loadShapes(shapes: Shape[]): void { + if (!shapes || !Array.isArray(shapes)) { + console.error("Invalid shapes data provided to loadShapes."); + return; + } + + this.existingShapes = shapes; + + this.notifyShapeCountChange(); + this.clearCanvas(); +} private connectWebSocket() { if ( this.socket && diff --git a/apps/collabydraw/components/AppSidebar.tsx b/apps/collabydraw/components/AppSidebar.tsx index 3068f52..9351592 100644 --- a/apps/collabydraw/components/AppSidebar.tsx +++ b/apps/collabydraw/components/AppSidebar.tsx @@ -17,6 +17,7 @@ import { TrashIcon, DownloadIcon, Upload, + Search, Linkedin, Share2, Star, @@ -46,9 +47,10 @@ interface SidebarProps { onClearCanvas?: () => void; onExportCanvas?: () => void; onImportCanvas?: () => void; + onSearchCanvas?: () => void; } -export function AppSidebar({ isOpen, onClose, canvasColor, setCanvasColor, isMobile, roomName, isStandalone, onClearCanvas, onExportCanvas, onImportCanvas }: SidebarProps) { +export function AppSidebar({ isOpen, onClose, canvasColor, setCanvasColor, isMobile, roomName, isStandalone, onClearCanvas, onExportCanvas, onImportCanvas,onSearchCanvas }: SidebarProps) { const [stars, setStars] = useState(null); const [clearDialogOpen, setClearDialogOpen] = useState(false); const { theme, setTheme } = useTheme(); @@ -118,6 +120,7 @@ export function AppSidebar({ isOpen, onClose, canvasColor, setCanvasColor, isMob setClearDialogOpen(true)} /> + setIsShareOpen(true)} /> {session?.user && session?.user.id ? ( diff --git a/apps/collabydraw/components/MobileCommandBar.tsx b/apps/collabydraw/components/MobileCommandBar.tsx index 3f88e93..e45e075 100644 --- a/apps/collabydraw/components/MobileCommandBar.tsx +++ b/apps/collabydraw/components/MobileCommandBar.tsx @@ -47,6 +47,7 @@ interface MobileCommandBarProps { onClearCanvas?: () => void; onExportCanvas?: () => void; onImportCanvas?: () => void; + onSearchCanvas?: () => void; } export function MobileCommandBar({ canvasColor, @@ -81,6 +82,7 @@ export function MobileCommandBar({ canvasColor, onClearCanvas, onExportCanvas, onImportCanvas, + onSearchCanvas, }: MobileCommandBarProps) { const [colorPickerOpen, setColorPickerOpen] = useState(false); @@ -119,6 +121,7 @@ export function MobileCommandBar({ canvasColor, onClearCanvas={onClearCanvas} onExportCanvas={onExportCanvas} onImportCanvas={onImportCanvas} + onSearchCanvas={onSearchCanvas} /> diff --git a/apps/collabydraw/components/canvas/CanvasBoard.tsx b/apps/collabydraw/components/canvas/CanvasBoard.tsx index 8e21774..c8d4811 100644 --- a/apps/collabydraw/components/canvas/CanvasBoard.tsx +++ b/apps/collabydraw/components/canvas/CanvasBoard.tsx @@ -9,7 +9,7 @@ import { useMediaQuery } from "@/hooks/useMediaQuery"; import { CanvasEngine } from "@/canvas-engine/CanvasEngine"; import { RoomParticipants } from "@repo/common/types"; import { getRoomParamsFromHash } from "@/utils/roomParams"; -import { BgFill, canvasBgLight, FillStyle, FontFamily, FontSize, LOCALSTORAGE_CANVAS_KEY, Mode, RoughStyle, StrokeEdge, StrokeFill, StrokeStyle, StrokeWidth, TextAlign, ToolType } from "@/types/canvas"; +import { BgFill, canvasBgLight, FillStyle, FontFamily, FontSize, LOCALSTORAGE_CANVAS_KEY, Mode, RoughStyle, StrokeEdge, StrokeFill, StrokeStyle, StrokeWidth, TextAlign, ToolType, Shape } from "@/types/canvas"; import { MobileCommandBar } from "../MobileCommandBar"; import ScreenLoading from "../ScreenLoading"; import AppMenuButton from "../AppMenuButton"; @@ -61,7 +61,8 @@ export default function CanvasBoard() { token: null as string | null, encryptionKey: null as string | null, }); - + + useEffect(() => { const getHash = () => { if (typeof window === 'undefined') return ''; @@ -111,8 +112,9 @@ export default function CanvasBoard() { window.removeEventListener('hashchange', handleHashChange); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname, searchParams, status, session]); - + useEffect(() => { setCanvasEngineState(prev => ({ ...prev, canvasColor: canvasBgLight[0] })); console.log('Theme = ', theme) @@ -120,20 +122,12 @@ export default function CanvasBoard() { useEffect(() => { if (canvasEngineState.engine && theme) { + canvasEngineState.engine.setTheme(theme === 'light' ? "light" : "dark"); } }, [theme, canvasEngineState.engine]); - - useEffect(() => { - const storedShapes = localStorage.getItem(LOCALSTORAGE_CANVAS_KEY); - const isEmpty = !storedShapes || JSON.parse(storedShapes).length === 0; - - setCanvasEngineState(prev => ({ - ...prev, - isCanvasEmpty: isEmpty - })); - }, []); - + + useEffect(() => { const { engine, scale } = canvasEngineState; if (engine) { @@ -192,6 +186,16 @@ export default function CanvasBoard() { return () => clearInterval(checkCanvasInterval); }, []); + const saveToLocalStorageWithExpiration = (key: string, data: Shape[], expirationInHours: number) => { + const expirationTime = new Date().getTime() + expirationInHours * 60 * 60 * 1000; + const item = { + data, + expiration: expirationTime, + }; + localStorage.setItem(key, JSON.stringify(item)); + }; + + const initializeCanvasEngine = useCallback(() => { if (!canvasRef.current) return null; @@ -216,6 +220,10 @@ export default function CanvasBoard() { ...prev, isCanvasEmpty: count === 0 })); + + // Save canvas data to localStorage with a 12-hour expiration + const shapes = engine.getShapes(); // Assuming `getShapes` retrieves the canvas data + saveToLocalStorageWithExpiration(LOCALSTORAGE_CANVAS_KEY, shapes, 12); }); return engine; }, [canvasEngineState.canvasColor, mode, theme]); @@ -281,11 +289,48 @@ export default function CanvasBoard() { }; }); }, []); - + const getFromLocalStorageWithExpiration = (key: string) => { + const itemStr = localStorage.getItem(key); + if (!itemStr) return null; + + try { + const item = JSON.parse(itemStr); + console.log("Parsed data from localStorage:", item); + + const currentTime = new Date().getTime(); + + // If item is an array (legacy or raw data), return it directly + if (Array.isArray(item)) { + return item; + } + + // If item is object with expiration, check expiration + if (item.expiration && currentTime > item.expiration) { + localStorage.removeItem(key); + return null; + } + + // Return data if present + return item.data || null; + } catch (error) { + console.error("Error parsing localStorage data:", error); + return null; + } + }; + + useEffect(() => { + const storedShapes = getFromLocalStorageWithExpiration("standalone_canvas_shapes"); + const isEmpty = !storedShapes || storedShapes.length === 0; + console.log("Stored Shapes:", storedShapes); + setCanvasEngineState(prev => ({ + ...prev, + isCanvasEmpty: isEmpty + })); + }, []); if (isLoading) { return } - + return (
)} + {matches && (