From 13f5787c3147d362d4c7e84a50e9f60cd51b1d53 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Wed, 18 May 2022 16:59:30 +0100 Subject: [PATCH] [improvement] Add ability to copy to new multiplayer room (#693) * Add ability to copy to new multiplayer room * cleanup --- apps/www/components/MultiplayerEditor.tsx | 4 +- apps/www/hooks/useMultiplayerState.ts | 7 ++ apps/www/package.json | 4 +- apps/www/pages/api/create.ts | 73 +++++++++++++++++++ apps/www/pages/r/[id].tsx | 1 + .../Primitives/icons/MultiplayerIcon.tsx | 4 +- .../MultiplayerMenu/MultiplayerMenu.tsx | 22 ++++-- yarn.lock | 5 ++ 8 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 apps/www/pages/api/create.ts diff --git a/apps/www/components/MultiplayerEditor.tsx b/apps/www/components/MultiplayerEditor.tsx index 85d4b1b28..864f23452 100644 --- a/apps/www/components/MultiplayerEditor.tsx +++ b/apps/www/components/MultiplayerEditor.tsx @@ -1,10 +1,10 @@ import { createClient } from '@liveblocks/client' import { LiveblocksProvider, RoomProvider } from '@liveblocks/react' -import { Tldraw, useFileSystem } from '@tldraw/tldraw' +import { Tldraw, TldrawApp, useFileSystem } from '@tldraw/tldraw' import { useAccountHandlers } from 'hooks/useAccountHandlers' import { useMultiplayerAssets } from 'hooks/useMultiplayerAssets' import { useMultiplayerState } from 'hooks/useMultiplayerState' -import { FC } from 'react' +import React, { FC } from 'react' import { styled } from 'styles' const client = createClient({ diff --git a/apps/www/hooks/useMultiplayerState.ts b/apps/www/hooks/useMultiplayerState.ts index 19c5ee254..c348add00 100644 --- a/apps/www/hooks/useMultiplayerState.ts +++ b/apps/www/hooks/useMultiplayerState.ts @@ -209,9 +209,16 @@ export function useMultiplayerState(roomId: string) { // Update the document with initial content handleChanges() + // Zoom to fit the content + app.zoomToFit() + if (app.zoom > 1) { + app.resetZoom() + } + setLoading(false) } } + setupDocument() return () => { diff --git a/apps/www/package.json b/apps/www/package.json index 7ba4b41f5..0a9e7caee 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -29,6 +29,8 @@ "@tldraw/tldraw": "^1.12.4", "@types/next-auth": "^3.15.0", "aws-sdk": "^2.1053.0", + "lz-string": "^1.4.4", + "nanoid": "^3.3.4", "next": "^12.0.7", "next-auth": "^4.0.5", "next-pwa": "^5.4.4", @@ -45,4 +47,4 @@ "typescript": "^4.5.2" }, "gitHead": "838fabdbff1a66d4d7ee8aa5c5d117bc55acbff2" -} \ No newline at end of file +} diff --git a/apps/www/pages/api/create.ts b/apps/www/pages/api/create.ts new file mode 100644 index 000000000..8eccd723b --- /dev/null +++ b/apps/www/pages/api/create.ts @@ -0,0 +1,73 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { TDDocument } from '@tldraw/tldraw' + +export default async function CreateMultiplayerRoom(req: NextApiRequest, res: NextApiResponse) { + const { body } = req + + try { + const json = JSON.parse(body) as { + roomId: string + pageId: string + document: TDDocument + } + const { roomId, pageId, document } = json + + const requestBody = { + liveblocksType: 'LiveObject', + data: { + version: 2.1, + shapes: { + liveblocksType: 'LiveMap', + data: {}, + }, + bindings: { + liveblocksType: 'LiveMap', + data: {}, + }, + assets: { + liveblocksType: 'LiveMap', + data: {}, + }, + }, + } + + requestBody.data.shapes.data = document.pages[pageId].shapes ?? {} + requestBody.data.bindings.data = document.pages[pageId].bindings ?? {} + requestBody.data.assets.data = document.assets ?? {} + + const auth = await fetch('https://liveblocks.io/api/authorize', { + headers: { + Authorization: `Bearer ${process.env.LIVEBLOCKS_SECRET_KEY}`, + 'Content-Type': 'application/json', + }, + }).then((d) => d.json()) + + // // GET + // const result = await fetch(`https://liveblocks.net/api/v1/room/1652883688619/storage`, { + // method: 'GET', + // headers: { + // Authorization: `Bearer ${auth.token}`, + // 'Content-Type': 'application/json', + // }, + // }).then((d) => d.json()) + + //POST + const result = await fetch(`https://liveblocks.net/api/v1/room/${roomId}/storage`, { + method: 'POST', + mode: 'cors', + cache: 'no-cache', + body: JSON.stringify(requestBody), + headers: { + Authorization: `Bearer ${auth.token}`, + 'Content-Type': 'application/json', + }, + }) + + if (result.status === 200) { + res.send({ status: 'success', roomId }) + } + } catch (e) { + res.send({ status: 'error' }) + // noop + } +} diff --git a/apps/www/pages/r/[id].tsx b/apps/www/pages/r/[id].tsx index b5271ffdd..4bd2fb01a 100644 --- a/apps/www/pages/r/[id].tsx +++ b/apps/www/pages/r/[id].tsx @@ -19,6 +19,7 @@ export default function Room({ id, isUser, isSponsor }: RoomProps) { export const getServerSideProps: GetServerSideProps = async (context) => { const session = await getSession(context) const id = context.query.id?.toString() + return { props: { id, diff --git a/packages/tldraw/src/components/Primitives/icons/MultiplayerIcon.tsx b/packages/tldraw/src/components/Primitives/icons/MultiplayerIcon.tsx index 101c9a25a..d3454c0db 100644 --- a/packages/tldraw/src/components/Primitives/icons/MultiplayerIcon.tsx +++ b/packages/tldraw/src/components/Primitives/icons/MultiplayerIcon.tsx @@ -4,8 +4,8 @@ export function MultiplayerIcon() { return ( diff --git a/packages/tldraw/src/components/TopPanel/MultiplayerMenu/MultiplayerMenu.tsx b/packages/tldraw/src/components/TopPanel/MultiplayerMenu/MultiplayerMenu.tsx index 552ea279c..304f9ed73 100644 --- a/packages/tldraw/src/components/TopPanel/MultiplayerMenu/MultiplayerMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/MultiplayerMenu/MultiplayerMenu.tsx @@ -7,6 +7,7 @@ import { SmallIcon } from '~components/Primitives/SmallIcon' import { MultiplayerIcon } from '~components/Primitives/icons' import type { TDSnapshot } from '~types' import { TLDR } from '~state/TLDR' +import { Utils } from '@tldraw/core' const roomSelector = (state: TDSnapshot) => state.room @@ -42,20 +43,27 @@ export const MultiplayerMenu = React.memo(function MultiplayerMenu() { }, []) const handleCopyToMultiplayerRoom = React.useCallback(async () => { + const body = JSON.stringify({ + roomId: Utils.uniqueId(), + pageId: app.currentPageId, + document: app.document, + }) + const myHeaders = new Headers({ 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', }) - const res = await fetch('http://tldraw.com/api/create-multiplayer-room', { + const res = await fetch(`/api/create`, { headers: myHeaders, method: 'POST', - mode: 'cors', - cache: 'no-cache', - body: JSON.stringify(app.document), + mode: 'no-cors', + body, }).then((res) => res.json()) - window.location.href = `http://tldraw.com/r/${res.roomId}` + if (res?.roomId) { + window.location.href = `/r/${res.roomId}` + } }, []) return ( @@ -71,7 +79,9 @@ export const MultiplayerMenu = React.memo(function MultiplayerMenu() { Create a Multiplayer Project - {/* Copy to Multiplayer Room */} + + Copy to Multiplayer Room + ) diff --git a/yarn.lock b/yarn.lock index e5d2527e0..841734971 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8482,6 +8482,11 @@ nanoid@^3.1.30, nanoid@^3.1.31, nanoid@^3.2.0: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + napi-build-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"