diff --git a/src/shapes/EmbedShapeUtil.tsx b/src/shapes/EmbedShapeUtil.tsx index ebc2026..84a448f 100644 --- a/src/shapes/EmbedShapeUtil.tsx +++ b/src/shapes/EmbedShapeUtil.tsx @@ -1,5 +1,5 @@ import { BaseBoxShapeUtil, TLBaseShape } from "tldraw" -import { useCallback, useState } from "react" +import { useCallback, useState, useEffect } from "react" //import Embed from "react-embed" export type IEmbedShape = TLBaseShape< @@ -9,7 +9,7 @@ export type IEmbedShape = TLBaseShape< h: number url: string | null interactionState?: { - scrollPosition?: { x: number; y: number } + scrollPosition: { x: number; y: number } currentTime?: number // for videos // other state you want to sync } @@ -133,12 +133,26 @@ export class EmbedShape extends BaseBoxShapeUtil { ) } - component(shape: IEmbedShape) { const [inputUrl, setInputUrl] = useState(shape.props.url || "") const [error, setError] = useState("") const [copyStatus, setCopyStatus] = useState(false) + // Add an effect to handle incoming sync updates + useEffect((): void => { + if (shape.props.interactionState?.scrollPosition) { + const iframe = document.querySelector("iframe") + if (iframe?.contentWindow) { + // Dispatch custom event to iframe + iframe.contentWindow.dispatchEvent( + new CustomEvent("syncScroll", { + detail: shape.props.interactionState.scrollPosition, + }), + ) + } + } + }, [shape.props.interactionState?.scrollPosition]) + const handleSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault() @@ -327,11 +341,36 @@ export class EmbedShape extends BaseBoxShapeUtil { loading="lazy" referrerPolicy="no-referrer" onLoad={(e) => { - // Add message listener for iframe communication + const iframe = e.currentTarget as HTMLIFrameElement + + // Inject scroll monitoring script + const script = ` + window.addEventListener('scroll', () => { + window.parent.postMessage({ + type: 'scroll', + scrollPosition: { + x: window.scrollX, + y: window.scrollY + } + }, '*'); + }); + + // Listen for scroll updates from other users + window.addEventListener('syncScroll', (e) => { + window.scrollTo(e.detail.x, e.detail.y); + }); + ` + + // @ts-ignore + iframe.contentWindow?.eval(script as string) + + // Listen for messages from iframe window.addEventListener("message", (event) => { - const iframe = e.currentTarget as HTMLIFrameElement if (event.source === iframe.contentWindow) { - handleIframeInteraction(event.data) + handleIframeInteraction({ + ...shape.props.interactionState, + scrollPosition: event.data.scrollPosition, + }) } }) }} diff --git a/src/shapes/VideoChatShapeUtil.tsx b/src/shapes/VideoChatShapeUtil.tsx index 7326c52..328531c 100644 --- a/src/shapes/VideoChatShapeUtil.tsx +++ b/src/shapes/VideoChatShapeUtil.tsx @@ -36,15 +36,10 @@ export class VideoChatShape extends BaseBoxShapeUtil { } try { - const apiKey = import.meta.env["VITE_DAILY_API_KEY"] - console.log("API Key available:", !!apiKey) - if (!apiKey) throw new Error("Daily API key is missing") - - const response = await fetch("https://api.daily.co/v1/rooms", { + const response = await fetch("/api/create-room", { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${apiKey.trim()}`, }, body: JSON.stringify({ properties: { diff --git a/worker/types.ts b/worker/types.ts index 3a756ae..de0ec5e 100644 --- a/worker/types.ts +++ b/worker/types.ts @@ -3,8 +3,11 @@ /// export interface Environment { - TLDRAW_BUCKET: R2Bucket - TLDRAW_DURABLE_OBJECT: DurableObjectNamespace - DAILY_API_KEY: string; - DAILY_DOMAIN: string; -} \ No newline at end of file + TLDRAW_BUCKET: R2Bucket + TLDRAW_DURABLE_OBJECT: DurableObjectNamespace + DAILY_API_KEY: string + VITE_DAILY_API_KEY: string + VITE_DAILY_DOMAIN: string + VITE_GOOGLE_CLIENT_ID: string + VITE_GOOGLE_MAPS_API_KEY: string +} diff --git a/worker/worker.ts b/worker/worker.ts index 32606a5..cdfd0dc 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -62,6 +62,7 @@ const { preflight, corsify } = cors({ maxAge: 86400, credentials: true, }) + const router = AutoRouter({ before: [preflight], finally: [ @@ -124,61 +125,43 @@ const router = AutoRouter({ }) }) - .post("/daily/rooms", async (request, env) => { + .post("/api/create-room", async (request) => { try { - const { name, properties } = (await request.json()) as { - name: string - properties: Record - } - - // Create a room using Daily.co API - const dailyResponse = await fetch("https://api.daily.co/v1/rooms", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${env.DAILY_API_KEY}`, - }, - body: JSON.stringify({ - name, - properties, - }), - }) - - const dailyData = await dailyResponse.json() - - if (!dailyResponse.ok) { - return new Response( - JSON.stringify({ - message: - (dailyData as any).info || "Failed to create Daily.co room", - }), - { - status: 400, - headers: { "Content-Type": "application/json" }, - }, - ) - } + // Replace with your actual video chat service API call + const room = await createVideoRoom() return new Response( JSON.stringify({ - url: `https://${env.DAILY_DOMAIN}/${(dailyData as any).name}`, + url: room.url, }), { - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", // Configure appropriately for production + }, }, ) } catch (error) { - return new Response( - JSON.stringify({ - message: error instanceof Error ? error.message : "Unknown error", - }), - { - status: 500, - headers: { "Content-Type": "application/json" }, + return new Response(JSON.stringify({ error: "Failed to create room" }), { + status: 500, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", }, - ) + }) } }) + // Handle OPTIONS for CORS + .options("/api/create-room", () => { + return new Response(null, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST", + "Access-Control-Allow-Headers": "Content-Type", + }, + }) + }) + // export our router for cloudflare export default router