mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	Add collaborators names (#1223)
* add random usernames * add username state * add username input * ability to set names * fix tests * set username oon mobile * remove auto generated names * remove commented code * always string * updaate snapshots * maintain username when clearing canvas * Update src/renderer/renderScene.ts Co-Authored-By: Lipis <lipiridis@gmail.com> * add border * fix styles Co-authored-by: Pete Hunt <petehunt@users.noreply.github.com> Co-authored-by: Faustino Kialungila <faustino.kialungila@gmail.com> Co-authored-by: Lipis <lipiridis@gmail.com>
This commit is contained in:
		@@ -10,6 +10,7 @@ import { getShortcutKey } from "../utils";
 | 
			
		||||
import useIsMobile from "../is-mobile";
 | 
			
		||||
import { register } from "./register";
 | 
			
		||||
import { newElementWith } from "../element/mutateElement";
 | 
			
		||||
import { AppState } from "../types";
 | 
			
		||||
 | 
			
		||||
export const actionChangeViewBackgroundColor = register({
 | 
			
		||||
  name: "changeViewBackgroundColor",
 | 
			
		||||
@@ -35,12 +36,15 @@ export const actionChangeViewBackgroundColor = register({
 | 
			
		||||
 | 
			
		||||
export const actionClearCanvas = register({
 | 
			
		||||
  name: "clearCanvas",
 | 
			
		||||
  perform: (elements) => {
 | 
			
		||||
  perform: (elements, appState: AppState) => {
 | 
			
		||||
    return {
 | 
			
		||||
      elements: elements.map((element) =>
 | 
			
		||||
        newElementWith(element, { isDeleted: true }),
 | 
			
		||||
      ),
 | 
			
		||||
      appState: getDefaultAppState(),
 | 
			
		||||
      appState: {
 | 
			
		||||
        ...getDefaultAppState(),
 | 
			
		||||
        username: appState.username,
 | 
			
		||||
      },
 | 
			
		||||
      commitToHistory: true,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ export function getDefaultAppState(): AppState {
 | 
			
		||||
    cursorButton: "up",
 | 
			
		||||
    scrolledOutside: false,
 | 
			
		||||
    name: `excalidraw-${getDateTime()}`,
 | 
			
		||||
    username: "",
 | 
			
		||||
    isCollaborating: false,
 | 
			
		||||
    isResizing: false,
 | 
			
		||||
    isRotating: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -437,6 +437,7 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
    } = {};
 | 
			
		||||
    const pointerViewportCoords: SceneState["remotePointerViewportCoords"] = {};
 | 
			
		||||
    const remoteSelectedElementIds: SceneState["remoteSelectedElementIds"] = {};
 | 
			
		||||
    const pointerUsernames: { [id: string]: string } = {};
 | 
			
		||||
    this.state.collaborators.forEach((user, socketID) => {
 | 
			
		||||
      if (user.selectedElementIds) {
 | 
			
		||||
        for (const id of Object.keys(user.selectedElementIds)) {
 | 
			
		||||
@@ -449,6 +450,9 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
      if (!user.pointer) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (user.username) {
 | 
			
		||||
        pointerUsernames[socketID] = user.username;
 | 
			
		||||
      }
 | 
			
		||||
      pointerViewportCoords[socketID] = sceneCoordsToViewportCoords(
 | 
			
		||||
        {
 | 
			
		||||
          sceneX: user.pointer.x,
 | 
			
		||||
@@ -483,6 +487,7 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
        remotePointerViewportCoords: pointerViewportCoords,
 | 
			
		||||
        remotePointerButton: cursorButton,
 | 
			
		||||
        remoteSelectedElementIds: remoteSelectedElementIds,
 | 
			
		||||
        remotePointerUsernames: pointerUsernames,
 | 
			
		||||
        shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
@@ -884,6 +889,7 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
                socketID,
 | 
			
		||||
                pointerCoords,
 | 
			
		||||
                button,
 | 
			
		||||
                username,
 | 
			
		||||
                selectedElementIds,
 | 
			
		||||
              } = decryptedData.payload;
 | 
			
		||||
              this.setState((state) => {
 | 
			
		||||
@@ -894,6 +900,7 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
                user.pointer = pointerCoords;
 | 
			
		||||
                user.button = button;
 | 
			
		||||
                user.selectedElementIds = selectedElementIds;
 | 
			
		||||
                user.username = username;
 | 
			
		||||
                state.collaborators.set(socketID, user);
 | 
			
		||||
                return state;
 | 
			
		||||
              });
 | 
			
		||||
@@ -947,6 +954,7 @@ export class App extends React.Component<any, AppState> {
 | 
			
		||||
          pointerCoords: payload.pointerCoords,
 | 
			
		||||
          button: payload.button || "up",
 | 
			
		||||
          selectedElementIds: this.state.selectedElementIds,
 | 
			
		||||
          username: this.state.username,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
      return this._broadcastSocketData(
 | 
			
		||||
 
 | 
			
		||||
@@ -133,6 +133,12 @@ export const LayerUI = React.memo(
 | 
			
		||||
                      <RoomDialog
 | 
			
		||||
                        isCollaborating={appState.isCollaborating}
 | 
			
		||||
                        collaboratorCount={appState.collaborators.size}
 | 
			
		||||
                        username={appState.username}
 | 
			
		||||
                        onUsernameChange={(username) => {
 | 
			
		||||
                          setAppState({
 | 
			
		||||
                            username,
 | 
			
		||||
                          });
 | 
			
		||||
                        }}
 | 
			
		||||
                        onRoomCreate={onRoomCreate}
 | 
			
		||||
                        onRoomDestroy={onRoomDestroy}
 | 
			
		||||
                      />
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,8 @@ export function MobileMenu({
 | 
			
		||||
                  <RoomDialog
 | 
			
		||||
                    isCollaborating={appState.isCollaborating}
 | 
			
		||||
                    collaboratorCount={appState.collaborators.size}
 | 
			
		||||
                    username={appState.username}
 | 
			
		||||
                    onUsernameChange={(username) => setAppState({ username })}
 | 
			
		||||
                    onRoomCreate={onRoomCreate}
 | 
			
		||||
                    onRoomDestroy={onRoomDestroy}
 | 
			
		||||
                  />
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,33 @@
 | 
			
		||||
  background-color: #eee;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.RoomDialog-usernameContainer {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  margin: 1.5em 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.RoomDialog-usernameLabel {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.RoomDialog-username {
 | 
			
		||||
  min-width: 0;
 | 
			
		||||
  flex: 1 1 auto;
 | 
			
		||||
  margin-left: 1em;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  border: none;
 | 
			
		||||
  height: 2.5rem;
 | 
			
		||||
  line-height: 2.5rem;
 | 
			
		||||
  padding: 0 0.5rem;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  border-radius: var(--space-factor);
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #eee;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.RoomDialog-link:hover {
 | 
			
		||||
  background-color: #eee;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,14 +11,19 @@ import { AppState } from "../types";
 | 
			
		||||
 | 
			
		||||
function RoomModal({
 | 
			
		||||
  activeRoomLink,
 | 
			
		||||
  username,
 | 
			
		||||
  onUsernameChange,
 | 
			
		||||
  onRoomCreate,
 | 
			
		||||
  onRoomDestroy,
 | 
			
		||||
}: {
 | 
			
		||||
  activeRoomLink: string;
 | 
			
		||||
  username: string;
 | 
			
		||||
  onUsernameChange: (username: string) => void;
 | 
			
		||||
  onRoomCreate: () => void;
 | 
			
		||||
  onRoomDestroy: () => void;
 | 
			
		||||
}) {
 | 
			
		||||
  const roomLinkInput = useRef<HTMLInputElement>(null);
 | 
			
		||||
 | 
			
		||||
  function copyRoomLink() {
 | 
			
		||||
    copyTextToSystemClipboard(activeRoomLink);
 | 
			
		||||
    if (roomLinkInput.current) {
 | 
			
		||||
@@ -71,6 +76,17 @@ function RoomModal({
 | 
			
		||||
              onPointerDown={selectInput}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="RoomDialog-usernameContainer">
 | 
			
		||||
            <label className="RoomDialog-usernameLabel" htmlFor="username">
 | 
			
		||||
              Username:
 | 
			
		||||
            </label>
 | 
			
		||||
            <input
 | 
			
		||||
              id="username"
 | 
			
		||||
              value={username || ""}
 | 
			
		||||
              className="RoomDialog-username"
 | 
			
		||||
              onChange={(event) => onUsernameChange(event.target.value)}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
          <p>{`🔒 ${t("roomDialog.desc_privacy")}`}</p>
 | 
			
		||||
          <p>
 | 
			
		||||
            <span role="img" aria-hidden="true">
 | 
			
		||||
@@ -99,11 +115,15 @@ function RoomModal({
 | 
			
		||||
export function RoomDialog({
 | 
			
		||||
  isCollaborating,
 | 
			
		||||
  collaboratorCount,
 | 
			
		||||
  username,
 | 
			
		||||
  onUsernameChange,
 | 
			
		||||
  onRoomCreate,
 | 
			
		||||
  onRoomDestroy,
 | 
			
		||||
}: {
 | 
			
		||||
  isCollaborating: AppState["isCollaborating"];
 | 
			
		||||
  collaboratorCount: number;
 | 
			
		||||
  username: string;
 | 
			
		||||
  onUsernameChange: (username: string) => void;
 | 
			
		||||
  onRoomCreate: () => void;
 | 
			
		||||
  onRoomDestroy: () => void;
 | 
			
		||||
}) {
 | 
			
		||||
@@ -149,6 +169,8 @@ export function RoomDialog({
 | 
			
		||||
        >
 | 
			
		||||
          <RoomModal
 | 
			
		||||
            activeRoomLink={activeRoomLink}
 | 
			
		||||
            username={username}
 | 
			
		||||
            onUsernameChange={onUsernameChange}
 | 
			
		||||
            onRoomCreate={onRoomCreate}
 | 
			
		||||
            onRoomDestroy={onRoomDestroy}
 | 
			
		||||
          />
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ export type SocketUpdateDataSource = {
 | 
			
		||||
      pointerCoords: { x: number; y: number };
 | 
			
		||||
      button: "down" | "up";
 | 
			
		||||
      selectedElementIds: AppState["selectedElementIds"];
 | 
			
		||||
      username: string;
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -330,6 +330,7 @@ export function renderScene(
 | 
			
		||||
  // Paint remote pointers
 | 
			
		||||
  for (const clientId in sceneState.remotePointerViewportCoords) {
 | 
			
		||||
    let { x, y } = sceneState.remotePointerViewportCoords[clientId];
 | 
			
		||||
    const username = sceneState.remotePointerUsernames[clientId];
 | 
			
		||||
 | 
			
		||||
    const width = 9;
 | 
			
		||||
    const height = 14;
 | 
			
		||||
@@ -383,6 +384,41 @@ export function renderScene(
 | 
			
		||||
    context.lineTo(x, y);
 | 
			
		||||
    context.fill();
 | 
			
		||||
    context.stroke();
 | 
			
		||||
 | 
			
		||||
    if (!isOutOfBounds && username) {
 | 
			
		||||
      const offsetX = x + width;
 | 
			
		||||
      const offsetY = y + height;
 | 
			
		||||
      const paddingHorizontal = 4;
 | 
			
		||||
      const paddingVertical = 4;
 | 
			
		||||
      const measure = context.measureText(username);
 | 
			
		||||
      const measureHeight =
 | 
			
		||||
        measure.actualBoundingBoxDescent + measure.actualBoundingBoxAscent;
 | 
			
		||||
 | 
			
		||||
      // Border
 | 
			
		||||
      context.fillStyle = stroke;
 | 
			
		||||
      context.globalAlpha = globalAlpha;
 | 
			
		||||
      context.fillRect(
 | 
			
		||||
        offsetX - 1,
 | 
			
		||||
        offsetY - 1,
 | 
			
		||||
        measure.width + 2 * paddingHorizontal + 2,
 | 
			
		||||
        measureHeight + 2 * paddingVertical + 2,
 | 
			
		||||
      );
 | 
			
		||||
      // Background
 | 
			
		||||
      context.fillStyle = background;
 | 
			
		||||
      context.fillRect(
 | 
			
		||||
        offsetX,
 | 
			
		||||
        offsetY,
 | 
			
		||||
        measure.width + 2 * paddingHorizontal,
 | 
			
		||||
        measureHeight + 2 * paddingVertical,
 | 
			
		||||
      );
 | 
			
		||||
      context.fillStyle = "#ffffff";
 | 
			
		||||
      context.fillText(
 | 
			
		||||
        username,
 | 
			
		||||
        offsetX + paddingHorizontal,
 | 
			
		||||
        offsetY + paddingVertical + measure.actualBoundingBoxAscent,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context.strokeStyle = strokeStyle;
 | 
			
		||||
    context.fillStyle = fillStyle;
 | 
			
		||||
    context.globalAlpha = globalAlpha;
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ export function exportToCanvas(
 | 
			
		||||
      remotePointerViewportCoords: {},
 | 
			
		||||
      remoteSelectedElementIds: {},
 | 
			
		||||
      shouldCacheIgnoreZoom: false,
 | 
			
		||||
      remotePointerUsernames: {},
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      renderScrollbars: false,
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export type SceneState = {
 | 
			
		||||
  remotePointerViewportCoords: { [id: string]: { x: number; y: number } };
 | 
			
		||||
  remotePointerButton?: { [id: string]: string | undefined };
 | 
			
		||||
  remoteSelectedElementIds: { [elementId: string]: string[] };
 | 
			
		||||
  remotePointerUsernames: { [id: string]: string };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SceneScroll = {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -233,6 +234,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -348,6 +350,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -616,6 +619,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -772,6 +776,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -967,6 +972,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -1221,6 +1227,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -1589,6 +1596,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2205,6 +2213,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2320,6 +2329,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2435,6 +2445,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2550,6 +2561,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2687,6 +2699,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2824,6 +2837,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -2961,6 +2975,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -3076,6 +3091,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -3191,6 +3207,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -3328,6 +3345,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -3443,6 +3461,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": true,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -3513,6 +3532,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -4379,6 +4399,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -4793,6 +4814,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -5116,6 +5138,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -5352,6 +5375,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -5521,6 +5545,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -6339,6 +6364,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -7050,6 +7076,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -7658,6 +7685,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -8168,6 +8196,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -8629,6 +8658,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -8997,6 +9027,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -9276,6 +9307,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -9486,6 +9518,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -10359,6 +10392,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -11123,6 +11157,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -11782,6 +11817,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -12336,6 +12372,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -12707,6 +12744,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -12761,6 +12799,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": true,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -12815,6 +12854,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
@@ -13106,6 +13146,7 @@ Object {
 | 
			
		||||
  "selectionElement": null,
 | 
			
		||||
  "shouldCacheIgnoreZoom": false,
 | 
			
		||||
  "showShortcutsDialog": false,
 | 
			
		||||
  "username": "",
 | 
			
		||||
  "viewBackgroundColor": "#ffffff",
 | 
			
		||||
  "zoom": 1,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ export type AppState = {
 | 
			
		||||
  cursorButton: "up" | "down";
 | 
			
		||||
  scrolledOutside: boolean;
 | 
			
		||||
  name: string;
 | 
			
		||||
  username: string;
 | 
			
		||||
  isCollaborating: boolean;
 | 
			
		||||
  isResizing: boolean;
 | 
			
		||||
  isRotating: boolean;
 | 
			
		||||
@@ -53,6 +54,7 @@ export type AppState = {
 | 
			
		||||
      };
 | 
			
		||||
      button?: "up" | "down";
 | 
			
		||||
      selectedElementIds?: AppState["selectedElementIds"];
 | 
			
		||||
      username?: string | null;
 | 
			
		||||
    }
 | 
			
		||||
  >;
 | 
			
		||||
  shouldCacheIgnoreZoom: boolean;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user