125 lines
2.9 KiB
TypeScript
125 lines
2.9 KiB
TypeScript
import { BaseBoxShapeUtil, TLBaseShape } from "tldraw"
|
|
import { useEffect, useState } from "react"
|
|
import { WORKER_URL } from "../routes/Board"
|
|
|
|
export type IVideoChatShape = TLBaseShape<
|
|
"VideoChat",
|
|
{
|
|
w: number
|
|
h: number
|
|
roomUrl: string | null
|
|
userName: string
|
|
}
|
|
>
|
|
|
|
export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
|
|
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
|
|
}
|
|
|
|
const response = await fetch(`${WORKER_URL}/daily/rooms`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
properties: {
|
|
enable_recording: true,
|
|
max_participants: 8,
|
|
},
|
|
}),
|
|
})
|
|
|
|
const data = await response.json()
|
|
|
|
this.editor.updateShape<IVideoChatShape>({
|
|
id: shape.id,
|
|
type: "VideoChat",
|
|
props: {
|
|
...shape.props,
|
|
roomUrl: (data as any).url,
|
|
},
|
|
})
|
|
}
|
|
|
|
component(shape: IVideoChatShape) {
|
|
const [isInRoom, setIsInRoom] = useState(false)
|
|
const [error, setError] = useState("")
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (isInRoom && shape.props.roomUrl) {
|
|
const script = document.createElement("script")
|
|
script.src = "https://www.daily.co/static/call-machine.js"
|
|
document.body.appendChild(script)
|
|
|
|
script.onload = () => {
|
|
// @ts-ignore
|
|
window.DailyIframe.createFrame({
|
|
iframeStyle: {
|
|
width: "100%",
|
|
height: "100%",
|
|
border: "0",
|
|
borderRadius: "4px",
|
|
},
|
|
showLeaveButton: true,
|
|
showFullscreenButton: true,
|
|
}).join({ url: shape.props.roomUrl })
|
|
}
|
|
}
|
|
}, [isInRoom, shape.props.roomUrl])
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
pointerEvents: "all",
|
|
width: `${shape.props.w}px`,
|
|
height: `${shape.props.h}px`,
|
|
position: "absolute",
|
|
top: "10px",
|
|
left: "10px",
|
|
zIndex: 9999,
|
|
padding: "15px",
|
|
backgroundColor: "#F0F0F0",
|
|
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
|
|
borderRadius: "4px",
|
|
}}
|
|
>
|
|
{!isInRoom ? (
|
|
<button
|
|
onClick={() => setIsInRoom(true)}
|
|
className="bg-blue-500 text-white px-4 py-2 rounded"
|
|
>
|
|
Join Room
|
|
</button>
|
|
) : (
|
|
<div
|
|
id="daily-call-iframe-container"
|
|
style={{
|
|
width: "100%",
|
|
height: "100%",
|
|
}}
|
|
/>
|
|
)}
|
|
{error && <p className="text-red-500 mt-2">{error}</p>}
|
|
</div>
|
|
)
|
|
}
|
|
}
|