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 ea9012f9d..2da477795 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 01de96ef7..147902f44 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 55079c86d..96d8af30f 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 b9eddc7d8..298eeee8e 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 98e6cda93..1cb208ba6 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 ace799781..82f9f5dd2 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 6c9fd8370..f6831bb7f 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 2bc97672d..ba0a3497e 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;