From 5feacd9a3b8c0f39b6bb225ea2387d05daa2ed61 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Wed, 15 Jun 2022 15:35:57 +0200 Subject: [PATCH] feat: deduplicate collab avatars based on `id` (#5309) --- src/actions/actionNavigate.tsx | 11 +------ src/actions/types.ts | 3 +- src/components/LayerUI.tsx | 21 +++---------- src/components/MobileMenu.tsx | 19 ++++-------- src/components/UserList.tsx | 44 ++++++++++++++++++++++++---- src/packages/excalidraw/CHANGELOG.md | 2 ++ src/types.ts | 2 ++ 7 files changed, 54 insertions(+), 48 deletions(-) diff --git a/src/actions/actionNavigate.tsx b/src/actions/actionNavigate.tsx index bdf6865d6570..26ce7bdbf685 100644 --- a/src/actions/actionNavigate.tsx +++ b/src/actions/actionNavigate.tsx @@ -31,16 +31,7 @@ export const actionGoToCollaborator = register({ }; }, PanelComponent: ({ appState, updateData, data }) => { - const clientId: string | undefined = data?.id; - if (!clientId) { - return null; - } - - const collaborator = appState.collaborators.get(clientId); - - if (!collaborator) { - return null; - } + const [clientId, collaborator] = data as [string, Collaborator]; const { background, stroke } = getClientColors(clientId, appState); diff --git a/src/actions/types.ts b/src/actions/types.ts index f9cd9186fb69..098c69ada84e 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -6,7 +6,6 @@ import { ExcalidrawProps, BinaryFiles, } from "../types"; -import { ToolButtonSize } from "../components/ToolButton"; export type ActionSource = "ui" | "keyboard" | "contextMenu" | "api"; @@ -119,7 +118,7 @@ export type PanelComponentProps = { appState: AppState; updateData: (formData?: any) => void; appProps: ExcalidrawProps; - data?: Partial<{ id: string; size: ToolButtonSize }>; + data?: Record; }; export interface Action { diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 83715ffdcfe7..f8190a99f9bd 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -25,7 +25,6 @@ import { PasteChartDialog } from "./PasteChartDialog"; import { Section } from "./Section"; import { HelpDialog } from "./HelpDialog"; import Stack from "./Stack"; -import { Tooltip } from "./Tooltip"; import { UserList } from "./UserList"; import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library"; import { JSONExportDialog } from "./JSONExportDialog"; @@ -380,22 +379,10 @@ const LayerUI = ({ }, )} > - - {appState.collaborators.size > 0 && - Array.from(appState.collaborators) - // Collaborator is either not initialized or is actually the current user. - .filter(([_, client]) => Object.keys(client).length !== 0) - .map(([clientId, client]) => ( - - {actionManager.renderAction("goToCollaborator", { - id: clientId, - })} - - ))} - + {renderTopRightUI?.(deviceType.isMobile, appState)} diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index f59fc42fda7c..e5771e5ab296 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -202,20 +202,11 @@ export const MobileMenu = ({ {appState.collaborators.size > 0 && (
{t("labels.collaborators")} - - {Array.from(appState.collaborators) - // Collaborator is either not initialized or is actually the current user. - .filter( - ([_, client]) => Object.keys(client).length !== 0, - ) - .map(([clientId, client]) => ( - - {actionManager.renderAction("goToCollaborator", { - id: clientId, - })} - - ))} - +
)} diff --git a/src/components/UserList.tsx b/src/components/UserList.tsx index 7cd1e12483b2..b9f7a36a08dc 100644 --- a/src/components/UserList.tsx +++ b/src/components/UserList.tsx @@ -2,17 +2,51 @@ import "./UserList.scss"; import React from "react"; import clsx from "clsx"; +import { AppState, Collaborator } from "../types"; +import { Tooltip } from "./Tooltip"; +import { ActionManager } from "../actions/manager"; -type UserListProps = { - children: React.ReactNode; +export const UserList: React.FC<{ className?: string; mobile?: boolean; -}; + collaborators: AppState["collaborators"]; + actionManager: ActionManager; +}> = ({ className, mobile, collaborators, actionManager }) => { + const uniqueCollaborators = new Map(); + + collaborators.forEach((collaborator, socketId) => { + uniqueCollaborators.set( + // filter on user id, else fall back on unique socketId + collaborator.id || socketId, + collaborator, + ); + }); + + const avatars = + uniqueCollaborators.size > 0 && + Array.from(uniqueCollaborators) + .filter(([_, client]) => Object.keys(client).length !== 0) + .map(([clientId, collaborator]) => { + const avatarJSX = actionManager.renderAction("goToCollaborator", [ + clientId, + collaborator, + ]); + + return mobile ? ( + + {avatarJSX} + + ) : ( + {avatarJSX} + ); + }); -export const UserList = ({ children, className, mobile }: UserListProps) => { return (
- {children} + {avatars}
); }; diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index f6dc31513734..8f5265dbbcf6 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -17,6 +17,8 @@ Please add the latest change on the top under the correct section. #### Features +- Added support for supplying user `id` in the Collaborator object (see `collaborators` in [`updateScene()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#updateScene)), which will be used to deduplicate users when rendering collaborator avatar list. Cursors will still be rendered for every user. [#5309](https://github.com/excalidraw/excalidraw/pull/5309) + - Export API to [set](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#setCursor) and [reset](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#resetCursor) mouse cursor on the canvas [#5215](https://github.com/excalidraw/excalidraw/pull/5215). - Export [`sceneCoordsToViewportCoords`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#onPointerDown) and [`viewportCoordsToSceneCoords`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#onPointerDown) utilities [#5187](https://github.com/excalidraw/excalidraw/pull/5187). diff --git a/src/types.ts b/src/types.ts index beee25385d4c..b8d59a9a8afb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -48,6 +48,8 @@ export type Collaborator = { // The url of the collaborator's avatar, defaults to username intials // if not present avatarUrl?: string; + // user id. If supplied, we'll filter out duplicates when rendering user avatars. + id?: string; }; export type DataURL = string & { _brand: "DataURL" };