Adjusted types, environment vars, load env vars from Cloudflare (?), sync iframe updates [not working]

This commit is contained in:
Jeff Emmett 2024-12-26 13:33:57 -05:00
parent 198109a919
commit 6cf1c2511b
4 changed files with 80 additions and 60 deletions

View File

@ -1,5 +1,5 @@
import { BaseBoxShapeUtil, TLBaseShape } from "tldraw" import { BaseBoxShapeUtil, TLBaseShape } from "tldraw"
import { useCallback, useState } from "react" import { useCallback, useState, useEffect } from "react"
//import Embed from "react-embed" //import Embed from "react-embed"
export type IEmbedShape = TLBaseShape< export type IEmbedShape = TLBaseShape<
@ -9,7 +9,7 @@ export type IEmbedShape = TLBaseShape<
h: number h: number
url: string | null url: string | null
interactionState?: { interactionState?: {
scrollPosition?: { x: number; y: number } scrollPosition: { x: number; y: number }
currentTime?: number // for videos currentTime?: number // for videos
// other state you want to sync // other state you want to sync
} }
@ -133,12 +133,26 @@ export class EmbedShape extends BaseBoxShapeUtil<IEmbedShape> {
</g> </g>
) )
} }
component(shape: IEmbedShape) { component(shape: IEmbedShape) {
const [inputUrl, setInputUrl] = useState(shape.props.url || "") const [inputUrl, setInputUrl] = useState(shape.props.url || "")
const [error, setError] = useState("") const [error, setError] = useState("")
const [copyStatus, setCopyStatus] = useState(false) 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( const handleSubmit = useCallback(
(e: React.FormEvent) => { (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@ -327,11 +341,36 @@ export class EmbedShape extends BaseBoxShapeUtil<IEmbedShape> {
loading="lazy" loading="lazy"
referrerPolicy="no-referrer" referrerPolicy="no-referrer"
onLoad={(e) => { 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) => { window.addEventListener("message", (event) => {
const iframe = e.currentTarget as HTMLIFrameElement
if (event.source === iframe.contentWindow) { if (event.source === iframe.contentWindow) {
handleIframeInteraction(event.data) handleIframeInteraction({
...shape.props.interactionState,
scrollPosition: event.data.scrollPosition,
})
} }
}) })
}} }}

View File

@ -36,15 +36,10 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
} }
try { try {
const apiKey = import.meta.env["VITE_DAILY_API_KEY"] const response = await fetch("/api/create-room", {
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", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${apiKey.trim()}`,
}, },
body: JSON.stringify({ body: JSON.stringify({
properties: { properties: {

View File

@ -3,8 +3,11 @@
/// <reference types="@cloudflare/workers-types" /> /// <reference types="@cloudflare/workers-types" />
export interface Environment { export interface Environment {
TLDRAW_BUCKET: R2Bucket TLDRAW_BUCKET: R2Bucket
TLDRAW_DURABLE_OBJECT: DurableObjectNamespace TLDRAW_DURABLE_OBJECT: DurableObjectNamespace
DAILY_API_KEY: string; DAILY_API_KEY: string
DAILY_DOMAIN: string; VITE_DAILY_API_KEY: string
VITE_DAILY_DOMAIN: string
VITE_GOOGLE_CLIENT_ID: string
VITE_GOOGLE_MAPS_API_KEY: string
} }

View File

@ -62,6 +62,7 @@ const { preflight, corsify } = cors({
maxAge: 86400, maxAge: 86400,
credentials: true, credentials: true,
}) })
const router = AutoRouter<IRequest, [env: Environment, ctx: ExecutionContext]>({ const router = AutoRouter<IRequest, [env: Environment, ctx: ExecutionContext]>({
before: [preflight], before: [preflight],
finally: [ finally: [
@ -124,61 +125,43 @@ const router = AutoRouter<IRequest, [env: Environment, ctx: ExecutionContext]>({
}) })
}) })
.post("/daily/rooms", async (request, env) => { .post("/api/create-room", async (request) => {
try { try {
const { name, properties } = (await request.json()) as { // Replace with your actual video chat service API call
name: string const room = await createVideoRoom()
properties: Record<string, unknown>
}
// 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" },
},
)
}
return new Response( return new Response(
JSON.stringify({ 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) { } catch (error) {
return new Response( return new Response(JSON.stringify({ error: "Failed to create room" }), {
JSON.stringify({ status: 500,
message: error instanceof Error ? error.message : "Unknown error", headers: {
}), "Content-Type": "application/json",
{ "Access-Control-Allow-Origin": "*",
status: 500,
headers: { "Content-Type": "application/json" },
}, },
) })
} }
}) })
// 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 our router for cloudflare
export default router export default router