Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
import { useSync } from '@tldraw/sync'
|
|
import { ReactNode, useEffect, useState } from 'react'
|
|
import { useParams } from 'react-router-dom'
|
|
import { Tldraw } from 'tldraw'
|
|
import { getBookmarkPreview } from '../getBookmarkPreview'
|
|
import { multiplayerAssetStore } from '../multiplayerAssetStore'
|
|
|
|
export function Room() {
|
|
const { roomId } = useParams<{ roomId: string }>()
|
|
|
|
// Create a store connected to multiplayer.
|
|
const store = useSync({
|
|
// We need to know the websockets URI...
|
|
uri: `${window.location.origin}/api/connect/${roomId}`,
|
|
// ...and how to handle static assets like images & videos
|
|
assets: multiplayerAssetStore,
|
|
})
|
|
|
|
return (
|
|
<RoomWrapper roomId={roomId}>
|
|
<Tldraw
|
|
// we can pass the connected store into the Tldraw component which will handle
|
|
// loading states & enable multiplayer UX like cursors & a presence menu
|
|
store={store}
|
|
options={{ deepLinks: true }}
|
|
onMount={(editor) => {
|
|
// when the editor is ready, we need to register our bookmark unfurling service
|
|
editor.registerExternalAssetHandler('url', getBookmarkPreview)
|
|
}}
|
|
/>
|
|
</RoomWrapper>
|
|
)
|
|
}
|
|
|
|
function RoomWrapper({ children, roomId }: { children: ReactNode; roomId?: string }) {
|
|
const [didCopy, setDidCopy] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!didCopy) return
|
|
const timeout = setTimeout(() => setDidCopy(false), 3000)
|
|
return () => clearTimeout(timeout)
|
|
}, [didCopy])
|
|
|
|
return (
|
|
<div className="RoomWrapper">
|
|
<div className="RoomWrapper-header">
|
|
<WifiIcon />
|
|
<div>{roomId}</div>
|
|
<button
|
|
className="RoomWrapper-copy"
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(window.location.href)
|
|
setDidCopy(true)
|
|
}}
|
|
aria-label="copy room link"
|
|
>
|
|
Copy link
|
|
{didCopy && <div className="RoomWrapper-copied">Copied!</div>}
|
|
</button>
|
|
</div>
|
|
<div className="RoomWrapper-content">{children}</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function WifiIcon() {
|
|
return (
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
strokeWidth="1.5"
|
|
stroke="currentColor"
|
|
width={16}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M8.288 15.038a5.25 5.25 0 0 1 7.424 0M5.106 11.856c3.807-3.808 9.98-3.808 13.788 0M1.924 8.674c5.565-5.565 14.587-5.565 20.152 0M12.53 18.22l-.53.53-.53-.53a.75.75 0 0 1 1.06 0Z"
|
|
/>
|
|
</svg>
|
|
)
|
|
}
|