Skip to content

Commit

Permalink
feat: deduplicate collab avatars based on id (excalidraw#5309)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwelle authored Jun 15, 2022
1 parent ec35d5d commit 5feacd9
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 48 deletions.
11 changes: 1 addition & 10 deletions src/actions/actionNavigate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 1 addition & 2 deletions src/actions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ExcalidrawProps,
BinaryFiles,
} from "../types";
import { ToolButtonSize } from "../components/ToolButton";

export type ActionSource = "ui" | "keyboard" | "contextMenu" | "api";

Expand Down Expand Up @@ -119,7 +118,7 @@ export type PanelComponentProps = {
appState: AppState;
updateData: (formData?: any) => void;
appProps: ExcalidrawProps;
data?: Partial<{ id: string; size: ToolButtonSize }>;
data?: Record<string, any>;
};

export interface Action {
Expand Down
21 changes: 4 additions & 17 deletions src/components/LayerUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -380,22 +379,10 @@ const LayerUI = ({
},
)}
>
<UserList>
{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]) => (
<Tooltip
label={client.username || "Unknown user"}
key={clientId}
>
{actionManager.renderAction("goToCollaborator", {
id: clientId,
})}
</Tooltip>
))}
</UserList>
<UserList
collaborators={appState.collaborators}
actionManager={actionManager}
/>
{renderTopRightUI?.(deviceType.isMobile, appState)}
</div>
</div>
Expand Down
19 changes: 5 additions & 14 deletions src/components/MobileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,20 +202,11 @@ export const MobileMenu = ({
{appState.collaborators.size > 0 && (
<fieldset>
<legend>{t("labels.collaborators")}</legend>
<UserList mobile>
{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]) => (
<React.Fragment key={clientId}>
{actionManager.renderAction("goToCollaborator", {
id: clientId,
})}
</React.Fragment>
))}
</UserList>
<UserList
mobile
collaborators={appState.collaborators}
actionManager={actionManager}
/>
</fieldset>
)}
</Stack.Col>
Expand Down
44 changes: 39 additions & 5 deletions src/components/UserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Collaborator>();

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 ? (
<Tooltip
label={collaborator.username || "Unknown user"}
key={clientId}
>
{avatarJSX}
</Tooltip>
) : (
<React.Fragment key={clientId}>{avatarJSX}</React.Fragment>
);
});

export const UserList = ({ children, className, mobile }: UserListProps) => {
return (
<div className={clsx("UserList", className, { UserList_mobile: mobile })}>
{children}
{avatars}
</div>
);
};
2 changes: 2 additions & 0 deletions src/packages/excalidraw/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" };
Expand Down

0 comments on commit 5feacd9

Please sign in to comment.