Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/collabydraw/canvas-engine/CanvasEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
5 changes: 4 additions & 1 deletion apps/collabydraw/components/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
TrashIcon,
DownloadIcon,
Upload,
Search,
Linkedin,
Share2,
Star,
Expand Down Expand Up @@ -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<number | null>(null);
const [clearDialogOpen, setClearDialogOpen] = useState(false);
const { theme, setTheme } = useTheme();
Expand Down Expand Up @@ -118,6 +120,7 @@ export function AppSidebar({ isOpen, onClose, canvasColor, setCanvasColor, isMob
<SidebarItem icon={TrashIcon} label="Clear canvas" onClick={() => setClearDialogOpen(true)} />
<SidebarItem icon={DownloadIcon} label="Export Drawing" onClick={onExportCanvas} />
<SidebarItem icon={Upload} label="Import Drawing" onClick={onImportCanvas} />
<SidebarItem icon={Search} label="Find on canvas" onClick={onSearchCanvas} />
<SidebarItem icon={Share2} label="Live collaboration" onClick={() => setIsShareOpen(true)} />
{session?.user && session?.user.id ? (
<CreateRoomDialog open={isShareOpen} onOpenChange={setIsShareOpen} />
Expand Down
3 changes: 3 additions & 0 deletions apps/collabydraw/components/MobileCommandBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface MobileCommandBarProps {
onClearCanvas?: () => void;
onExportCanvas?: () => void;
onImportCanvas?: () => void;
onSearchCanvas?: () => void;
}

export function MobileCommandBar({ canvasColor,
Expand Down Expand Up @@ -81,6 +82,7 @@ export function MobileCommandBar({ canvasColor,
onClearCanvas,
onExportCanvas,
onImportCanvas,
onSearchCanvas,
}: MobileCommandBarProps) {
const [colorPickerOpen, setColorPickerOpen] = useState(false);

Expand Down Expand Up @@ -119,6 +121,7 @@ export function MobileCommandBar({ canvasColor,
onClearCanvas={onClearCanvas}
onExportCanvas={onExportCanvas}
onImportCanvas={onImportCanvas}
onSearchCanvas={onSearchCanvas}
/>
</SheetContent>
</Sheet>
Expand Down
78 changes: 62 additions & 16 deletions apps/collabydraw/components/canvas/CanvasBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 '';
Expand Down Expand Up @@ -111,29 +112,22 @@ 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)
}, [theme])

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) {
Expand Down Expand Up @@ -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;

Expand All @@ -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]);
Expand Down Expand Up @@ -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 <ScreenLoading />
}

return (
<div className={cn("collabydraw h-screen overflow-hidden",
canvasEngineState.activeTool === "eraser"
Expand Down Expand Up @@ -386,6 +431,7 @@ export default function CanvasBoard() {
<ToolMenuWelcome />
</div>
)}


{matches && (
<ZoomControl
Expand Down