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 { 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<IEmbedShape> {
</g>
)
}
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<IEmbedShape> {
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,
})
}
})
}}

View File

@ -36,15 +36,10 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
}
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: {

View File

@ -3,8 +3,11 @@
/// <reference types="@cloudflare/workers-types" />
export interface Environment {
TLDRAW_BUCKET: R2Bucket
TLDRAW_DURABLE_OBJECT: DurableObjectNamespace
DAILY_API_KEY: string;
DAILY_DOMAIN: string;
}
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
}

View File

@ -62,6 +62,7 @@ const { preflight, corsify } = cors({
maxAge: 86400,
credentials: true,
})
const router = AutoRouter<IRequest, [env: Environment, ctx: ExecutionContext]>({
before: [preflight],
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 {
const { name, properties } = (await request.json()) as {
name: string
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" },
},
)
}
// 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