From e63a0ec5be33a93832ae31526a50c9dc6d9f088b Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Sat, 6 Feb 2021 23:33:52 +0530 Subject: [PATCH] feat: allow host to pass color for collaborators (#2943) * feat: allow host to pass color for collaborators * remove user prop as its not used anywhere * update changelog and readme * add pr link --- src/actions/actionNavigate.tsx | 2 +- src/clients.ts | 9 ++++++++- src/excalidraw-app/index.tsx | 1 - src/packages/excalidraw/CHANGELOG.md | 1 + src/packages/excalidraw/README.md | 8 +------- src/packages/excalidraw/index.tsx | 7 ++----- src/renderer/renderScene.ts | 4 ++-- src/types.ts | 7 ++++--- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/actions/actionNavigate.tsx b/src/actions/actionNavigate.tsx index ea9012f9..2da47779 100644 --- a/src/actions/actionNavigate.tsx +++ b/src/actions/actionNavigate.tsx @@ -42,7 +42,7 @@ export const actionGoToCollaborator = register({ return null; } - const { background, stroke } = getClientColors(clientId); + const { background, stroke } = getClientColors(clientId, appState); const shortName = getClientInitials(collaborator.username); return ( diff --git a/src/clients.ts b/src/clients.ts index 01de96ef..147902f4 100644 --- a/src/clients.ts +++ b/src/clients.ts @@ -1,6 +1,13 @@ import colors from "./colors"; +import { AppState } from "./types"; -export const getClientColors = (clientId: string) => { +export const getClientColors = (clientId: string, appState: AppState) => { + if (appState?.collaborators) { + const currentUser = appState.collaborators.get(clientId); + if (currentUser?.color) { + return currentUser.color; + } + } // Naive way of getting an integer out of the clientId const sum = clientId.split("").reduce((a, str) => a + str.charCodeAt(0), 0); diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 55079c86..96d8af30 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -281,7 +281,6 @@ function ExcalidrawWrapper() { width={dimensions.width} height={dimensions.height} initialData={initialStatePromiseRef.current.promise} - user={{ name: collabAPI?.username }} onCollabButtonClick={collabAPI?.onCollabButtonClick} isCollaborating={collabAPI?.isCollaborating()} onPointerUpdate={collabAPI?.onPointerUpdate} diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index b9eddc7d..298eeee8 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -18,6 +18,7 @@ Please add the latest change on the top under the correct section. ### Features +- Allow host to pass [color](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L36) for [collaborator](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L27) [#2943](https://github.com/excalidraw/excalidraw/pull/2943). The unused prop `user` is now removed. - Add `zenModeEnabled` and `gridModeEnabled` prop which enables zen mode and grid mode respectively [#2901](https://github.com/excalidraw/excalidraw/pull/2901). When this prop is used, the zen mode / grid mode will be fully controlled by the host app. - Add `viewModeEnabled` prop which enabled the view mode [#2840](https://github.com/excalidraw/excalidraw/pull/2840). When this prop is used, the view mode will not show up in context menu is so it is fully controlled by host. - Expose `getAppState` on `excalidrawRef` [#2834](https://github.com/excalidraw/excalidraw/pull/2834). diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md index 98e6cda9..1cb208ba 100644 --- a/src/packages/excalidraw/README.md +++ b/src/packages/excalidraw/README.md @@ -106,7 +106,6 @@ export default function App() { onChange={(elements, state) => { console.log("Latest elements:", elements, "Latest state:", state); }} - user={{ name: "Excalidraw User" }} onPointerUpdate={(pointerData) => console.log(pointerData)} onCollabButtonClick={() => { window.alert("You clicked on collab button"); @@ -130,7 +129,6 @@ export default function App() { | [`offsetTop`](#offsetTop) | Number | `0` | top position relative to which Excalidraw should render | | [`onChange`](#onChange) | Function | | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw elements and the current app state. | | [`initialData`](#initialData) |
{elements?: ExcalidrawElement[], appState?: AppState } 
| null | The initial data with which app loads. | -| [`user`](#user) | `{ name?: string }` | | User details. The name refers to the name of the user to be shown | | [`excalidrawRef`](#excalidrawRef) | [`createRef`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`callbackRef`](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs) or
{ current: { readyPromise: resolvablePromise } }
| | Ref to be passed to Excalidraw | | [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked | | [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode | @@ -257,10 +255,6 @@ This helps to load Excalidraw with `initialData`. It must be an object or a [pro You might want to use this when you want to load excalidraw with some initial elements and app state. -#### `user` - -This is the user name which shows during collaboration. Defaults to `{name: ''}`. - #### `excalidrawRef` You can pass a `ref` when you want to access some excalidraw APIs. We expose the below APIs: @@ -269,7 +263,7 @@ You can pass a `ref` when you want to access some excalidraw APIs. We expose the | --- | --- | --- | | ready | `boolean` | This is set to true once Excalidraw is rendered | | readyPromise | [resolvablePromise](https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L317) | This promise will be resolved with the api once excalidraw has rendered. This will be helpful when you want do some action on the host app once this promise resolves. For this to work you will have to pass ref as shown [here](#readyPromise) | -| updateScene |
(sceneData)) => void 
| updates the scene with the sceneData | +| updateScene |
(sceneData)) => void 
| updates the scene with the sceneData | | resetScene | `({ resetLoadingState: boolean }) => void` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. | | getSceneElementsIncludingDeleted |
 () => ExcalidrawElement[]
| Returns all the elements including the deleted in the scene | | getSceneElements |
 () => ExcalidrawElement[]
| Returns all the elements excluding the deleted in the scene | diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index ace79978..82f9f5dd 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -18,7 +18,6 @@ const Excalidraw = (props: ExcalidrawProps) => { offsetTop, onChange, initialData, - user, excalidrawRef, onCollabButtonClick, isCollaborating, @@ -59,7 +58,6 @@ const Excalidraw = (props: ExcalidrawProps) => { offsetTop={offsetTop} onChange={onChange} initialData={initialData} - user={user} excalidrawRef={excalidrawRef} onCollabButtonClick={onCollabButtonClick} isCollaborating={isCollaborating} @@ -82,13 +80,12 @@ const areEqual = ( prevProps: PublicExcalidrawProps, nextProps: PublicExcalidrawProps, ) => { - const { initialData: prevInitialData, user: prevUser, ...prev } = prevProps; - const { initialData: nextInitialData, user: nextUser, ...next } = nextProps; + const { initialData: prevInitialData, ...prev } = prevProps; + const { initialData: nextInitialData, ...next } = nextProps; const prevKeys = Object.keys(prevProps) as (keyof typeof prev)[]; const nextKeys = Object.keys(nextProps) as (keyof typeof next)[]; return ( - prevUser?.name === nextUser?.name && prevKeys.length === nextKeys.length && prevKeys.every((key) => prev[key] === next[key]) ); diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts index 6c9fd837..f6831bb7 100644 --- a/src/renderer/renderScene.ts +++ b/src/renderer/renderScene.ts @@ -313,7 +313,7 @@ export const renderScene = ( if (sceneState.remoteSelectedElementIds[element.id]) { selectionColors.push( ...sceneState.remoteSelectedElementIds[element.id].map((socketId) => { - const { background } = getClientColors(socketId); + const { background } = getClientColors(socketId, appState); return background; }), ); @@ -441,7 +441,7 @@ export const renderScene = ( y = Math.max(y, 0); y = Math.min(y, normalizedCanvasHeight - height); - const { background, stroke } = getClientColors(clientId); + const { background, stroke } = getClientColors(clientId, appState); const strokeStyle = context.strokeStyle; const fillStyle = context.fillStyle; diff --git a/src/types.ts b/src/types.ts index 2bc97672..ba0a3497 100644 --- a/src/types.ts +++ b/src/types.ts @@ -33,6 +33,10 @@ export type Collaborator = { selectedElementIds?: AppState["selectedElementIds"]; username?: string | null; userState?: UserIdleState; + color?: { + background: string; + stroke: string; + }; }; export type AppState = { @@ -166,9 +170,6 @@ export interface ExcalidrawProps { appState: AppState, ) => void; initialData?: ImportedDataState | null | Promise; - user?: { - name?: string | null; - }; excalidrawRef?: ForwardRef; onCollabButtonClick?: () => void; isCollaborating?: boolean;