import { BaseBoxShapeUtil, TLBaseShape } from "tldraw" import { useEffect, useState, useRef } from "react" import { WORKER_URL } from "../routes/Board" import DailyIframe from "@daily-co/daily-js" // Add these constants at the top of the file const DAILY_DOMAIN = import.meta.env.VITE_DAILY_DOMAIN as string const DAILY_API_KEY = import.meta.env.VITE_DAILY_API_KEY as string export type IVideoChatShape = TLBaseShape< "VideoChat", { w: number h: number roomUrl: string | null userName: string } > // Simplified component using Daily Prebuilt const VideoChatComponent = ({ roomUrl }: { roomUrl: string }) => { const wrapperRef = useRef(null) const callFrameRef = useRef | null>(null) useEffect(() => { if (!wrapperRef.current || !roomUrl) return // Create and configure the Daily call frame callFrameRef.current = DailyIframe.createFrame(wrapperRef.current, { iframeStyle: { width: "100%", height: "100%", border: "0", borderRadius: "4px", }, showLeaveButton: true, showFullscreenButton: true, }) // Join the room callFrameRef.current.join({ url: roomUrl }) // Cleanup return () => { if (callFrameRef.current) { callFrameRef.current.destroy() } } }, [roomUrl]) return (
) } export class VideoChatShape extends BaseBoxShapeUtil { static override type = "VideoChat" indicator(_shape: IVideoChatShape) { return null } getDefaultProps(): IVideoChatShape["props"] { return { roomUrl: null, w: 640, h: 480, userName: "", } } async ensureRoomExists(shape: IVideoChatShape) { if (shape.props.roomUrl !== null) { return } try { // Create room directly with Daily.co API const response = await fetch("https://api.daily.co/v1/rooms", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${DAILY_API_KEY}`, }, body: JSON.stringify({ name: `canvas-room-${shape.id}`, privacy: "public", properties: { enable_recording: true, start_audio_off: true, start_video_off: true, enable_chat: true, max_participants: 8, }, }), }) if (!response.ok) { throw new Error(`Failed to create room: ${response.statusText}`) } const data = await response.json() const roomUrl = `https://${DAILY_DOMAIN}/${(data as any).name}` // Update the shape with the room URL this.editor.updateShape({ id: shape.id, type: "VideoChat", props: { ...shape.props, roomUrl, }, }) } catch (error) { console.error("Failed to create Daily room:", error) throw error } } component(shape: IVideoChatShape) { const [isInRoom, setIsInRoom] = useState(false) const [error, setError] = useState("") const [isLoading, setIsLoading] = useState(false) useEffect(() => { setIsLoading(true) this.ensureRoomExists(shape) .catch((err) => setError(err.message)) .finally(() => setIsLoading(false)) }, []) if (isLoading) { return (
Initializing video chat...
) } if (!shape.props.roomUrl) { return (
Creating room...
) } return (
{!isInRoom ? ( ) : ( )} {error && (
{error}
)}
) } }