import { useSync } from '@tldraw/sync' import { AssetRecordType, getHashForString, TLBookmarkAsset, Tldraw, TLUiMenuGroup, TLUiOverrides, } from 'tldraw' import { useParams } from 'react-router-dom' import { ChatBoxTool } from '@/tools/ChatBoxTool' import { ChatBoxShape } from '@/shapes/ChatBoxShapeUtil' import { VideoChatTool } from '@/tools/VideoChatTool' import { VideoChatShape } from '@/shapes/VideoChatShapeUtil' import { multiplayerAssetStore } from '../client/multiplayerAssetStore' import { customSchema } from '../../worker/TldrawDurableObject' import { EmbedShape } from '@/shapes/EmbedShapeUtil' import { EmbedTool } from '@/tools/EmbedTool' import React, { useState } from 'react'; import { ChatBox } from '@/shapes/ChatBoxShapeUtil'; import { components, uiOverrides } from '@/ui-overrides' const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev` const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape] const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools export function Board() { const { slug } = useParams<{ slug: string }>(); // Ensure this is inside the Board component const roomId = slug || 'default-room'; // Declare roomId here const store = useSync({ uri: `${WORKER_URL}/connect/${roomId}`, assets: multiplayerAssetStore, shapeUtils: shapeUtils, schema: customSchema, }); const [isChatBoxVisible, setChatBoxVisible] = useState(false); const [userName, setUserName] = useState(''); const [isVideoChatVisible, setVideoChatVisible] = useState(false); // Added state for video chat visibility const handleNameChange = (event: React.ChangeEvent) => { setUserName(event.target.value); }; const customUiOverrides: TLUiOverrides = { ...uiOverrides, contextMenu: (editor, contextMenuSchema, helpers) => { const defaultContextMenu = uiOverrides.contextMenu ? uiOverrides.contextMenu(editor, contextMenuSchema, helpers) : contextMenuSchema const newContextMenu: TLUiMenuGroup[] = [ ...defaultContextMenu, { id: 'external-link', type: 'group', checkbox: false, disabled: false, readonlyOk: true, children: [ { id: 'add-external-link', type: 'item', readonlyOk: true, label: 'Add External Link', icon: 'link', onSelect: () => { const selectedShapes = editor.getSelectedShapes() if (selectedShapes.length === 1) { const shape = selectedShapes[0] const externalUrl = `${window.location.origin}/board/${roomId}?shapeId=${shape.id}` // Here you can implement the logic to copy the link to clipboard or show it to the user console.log('External link:', externalUrl) // For example, to copy to clipboard: navigator.clipboard.writeText(externalUrl).then(() => { editor.setToast({ id: 'external-link-copied', title: 'External link copied to clipboard' }) }) } }, }, ], }, ] return newContextMenu }, } return (
{ editor.registerExternalAssetHandler('url', unfurlBookmarkUrl) editor.setCurrentTool('hand') }} /> {isChatBoxVisible && (
)} {isVideoChatVisible && ( // Render the button to join video chat )}
) } // How does our server handle bookmark unfurling? async function unfurlBookmarkUrl({ url }: { url: string }): Promise { const asset: TLBookmarkAsset = { id: AssetRecordType.createId(getHashForString(url)), typeName: 'asset', type: 'bookmark', meta: {}, props: { src: url, description: '', image: '', favicon: '', title: '', }, } try { const response = await fetch(`${WORKER_URL}/unfurl?url=${encodeURIComponent(url)}`) const data = await response.json() as { description: string, image: string, favicon: string, title: string } asset.props.description = data?.description ?? '' asset.props.image = data?.image ?? '' asset.props.favicon = data?.favicon ?? '' asset.props.title = data?.title ?? '' } catch (e) { console.error(e) } return asset }