Repository: excalidraw/excalidraw
File: src/components/App.tsx (TypeScript)

Code before:

  private handleCanvasPointerUp = (
    event: React.PointerEvent<HTMLCanvasElement>,
  ) => {
    this.lastPointerUp = event;
    const isTouchScreen = ["pen", "touch"].includes(event.pointerType);
    if (isTouchScreen || isEraserActive(this.state)) {
      const scenePointer = viewportCoordsToSceneCoords(
        { clientX: event.clientX, clientY: event.clientY },
        this.state,
      );
      const hitElement = this.getElementAtPosition(
        scenePointer.x,
        scenePointer.y,
      );

      if (isEraserActive(this.state)) {
        const elements = this.scene.getElements().map((ele) => {
          if (this.elementIdsToErase[ele.id]) {
            return newElementWith(ele, { isDeleted: true });
          } else if (hitElement && ele.id === hitElement.id) {
            return newElementWith(ele, { isDeleted: true });
          }
          return ele;
        });
        this.updateScene({ elements, commitToHistory: true });
        this.elementIdsToErase = {};
        this.removePointer(event);
        return;
      }
      this.hitLinkElement = this.getElementLinkAtPosition(
        scenePointer,
        hitElement,
      );
    }
    if (
      this.hitLinkElement &&
      !this.state.selectedElementIds[this.hitLinkElement.id]
    ) {
      this.redirectToLink(event, isTouchScreen);
    }

    this.removePointer(event);
  };

  private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
    event: React.PointerEvent<HTMLCanvasElement>,
  ): void => {
    // deal with opening context menu on touch devices
    if (event.pointerType === "touch") {
      invalidateContextMenu = false;

Review comment (suggestion):
I don't think we should be using this API internally as it was designed for host app purposes. Right now it doesn't hurt, but long-term we might make it so that `updateScene` doesn't notify host apps etc.

We should figure out how to make it clear that these APIs are for host app consumption only.

Code after:
  private handleCanvasPointerUp = (
    event: React.PointerEvent<HTMLCanvasElement>,
  ) => {
    this.lastPointerUp = event;
    const isTouchScreen = ["pen", "touch"].includes(event.pointerType);

    if (isTouchScreen || isEraserActive(this.state)) {
      const scenePointer = viewportCoordsToSceneCoords(
        { clientX: event.clientX, clientY: event.clientY },
        this.state,
      );
      const hitElement = this.getElementAtPosition(
        scenePointer.x,
        scenePointer.y,
      );
      const pointerDownEvent = this.initialPointerDownState(event);
      pointerDownEvent.hit.element = hitElement;
      this.eraseElements(pointerDownEvent);
      if (isTouchScreen) {
        this.hitLinkElement = this.getElementLinkAtPosition(
          scenePointer,
          hitElement,
        );
      }
    }
    if (
      this.hitLinkElement &&
      !this.state.selectedElementIds[this.hitLinkElement.id]
    ) {
      this.redirectToLink(event, isTouchScreen);
    }

    this.removePointer(event);
  };

  private maybeOpenContextMenuAfterPointerDownOnTouchDevices = (
    event: React.PointerEvent<HTMLCanvasElement>,
  ): void => {
    // deal with opening context menu on touch devices
    if (event.pointerType === "touch") {
      invalidateContextMenu = false;

      if (touchTimeout) {
        // If there's already a touchTimeout, this means that there's another
        // touch down and we are doing another touch, so we shouldn't open the
        // context menu.
        invalidateContextMenu = true;
      } else {
        // open the context menu with the first touch's clientX and clientY
        // if the touch is not moving
        touchTimeout = window.setTimeout(() => {