Commit d3f967e0 authored by Ategon's avatar Ategon
Browse files

Show keybinds for reactions and clean up keybind handling

parent a950c658
Loading
Loading
Loading
Loading
Loading
+14 −22
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import reactionWork from "../assets/reactions/work.png";
import reactionYay from "../assets/reactions/yay.png";
import { useAppContext } from "../contexts/AppContext";
import { CanvasCore } from "../lib/canvas";
import { KeybindManager, REACTION_KEYBINDS } from "../lib/keybinds";
import Network from "../lib/network";
import {
  colorToCss,
@@ -698,14 +699,6 @@ export const CursorPresenceLayer = () => {
      remoteRenderer.clear();
    };

    const isTypingTarget = (target: EventTarget | null) =>
      target instanceof HTMLElement &&
      Boolean(
        target.closest(
          'input, textarea, select, [contenteditable="true"], [role="textbox"]',
        ),
      );

    const showReactionCooldown = (cooldown: CursorReactionCooldown) => {
      if (cooldown.until <= Date.now()) return;
      reactionBlockedUntil = Math.max(reactionBlockedUntil, cooldown.until);
@@ -728,17 +721,11 @@ export const CursorPresenceLayer = () => {
      }, REACTION_DURATION + 50);
    };

    const handleKeyDown = (event: KeyboardEvent) => {
    const sendReaction = (reaction: number) => {
      if (
        !reactionsEnabledRef.current ||
        !localActive.current ||
        !socket.connected ||
        event.repeat ||
        event.altKey ||
        event.ctrlKey ||
        event.metaKey ||
        isTypingTarget(event.target) ||
        !/^[0-9]$/.test(event.key)
        !socket.connected
      ) {
        return;
      }
@@ -749,18 +736,15 @@ export const CursorPresenceLayer = () => {
          showReactionCooldown(reactionBlockedCooldown);
        }
        toast.error(REACTION_COOLDOWN_MESSAGE);
        event.preventDefault();
        return;
      }
      reactionBlockedCooldown = undefined;
      if (now - lastReactionAt < REACTION_COOLDOWN_DURATION) {
        toast.error(REACTION_COOLDOWN_MESSAGE);
        event.preventDefault();
        return;
      }

      lastReactionAt = now;
      const reaction = event.key === "0" ? 9 : Number(event.key) - 1;
      socket.emit("cursorReaction", reaction, (result) => {
        if (!mounted) return;
        if (!result.success) {
@@ -776,8 +760,15 @@ export const CursorPresenceLayer = () => {
        ).matches;
        appendReaction(localElement.current, reaction, reduceMotion);
      });
      event.preventDefault();
    };
    const reactionKeybindHandlers = REACTION_KEYBINDS.map(
      (keybind, reaction) => {
        const handle = () => sendReaction(reaction);
        KeybindManager.on(keybind, handle);
        return [keybind, handle] as const;
      },
    );

    const handleResize = () => {
      remoteRenderer.resize();
      scheduleViewport();
@@ -792,7 +783,6 @@ export const CursorPresenceLayer = () => {
    canvas.on("surfacePointer", handleSurfacePointer);
    PanZoom.on("viewportMove", handleViewportMove);
    PanZoom.on("initialize", handleInitialize);
    document.addEventListener("keydown", handleKeyDown);
    window.addEventListener("blur", hideLocalCursor);
    window.addEventListener("resize", handleResize);

@@ -829,7 +819,9 @@ export const CursorPresenceLayer = () => {
      canvas.off("surfacePointer", handleSurfacePointer);
      PanZoom.off("viewportMove", handleViewportMove);
      PanZoom.off("initialize", handleInitialize);
      document.removeEventListener("keydown", handleKeyDown);
      for (const [keybind, handle] of reactionKeybindHandlers) {
        KeybindManager.off(keybind, handle);
      }
      window.removeEventListener("blur", hideLocalCursor);
      window.removeEventListener("resize", handleResize);
      localActive.current = false;
+98 −226
Original line number Diff line number Diff line
@@ -23,219 +23,105 @@ interface EmittedKeybind {

export const enforceObjectType: EnforceObjectType<IKeybind[]> = (v) => v;

const KEYBINDS = enforceObjectType({
  PAN_UP: [{ key: "KeyW" }, { key: "ArrowUp" }],
  PAN_DOWN: [{ key: "KeyS" }, { key: "ArrowDown" }],
  PAN_LEFT: [{ key: "KeyA" }, { key: "ArrowLeft" }],
  PAN_RIGHT: [{ key: "KeyD" }, { key: "ArrowRight" }],
  ZOOM_OUT: [
    {
      key: "KeyQ",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
    {
      key: "Minus",
      ctrl: false,
export const REACTION_KEYBINDS = [
  "REACTION_LOVE",
  "REACTION_JAM",
  "REACTION_CUTE",
  "REACTION_POG",
  "REACTION_YAY",
  "REACTION_BONGO",
  "REACTION_SHOCKED",
  "REACTION_WORK",
  "REACTION_CRY",
  "REACTION_AWE",
] as const;

const unmodifiedKeybind = (...keys: IKeybind["key"][]): IKeybind[] =>
  keys.map((key) => ({
    key,
    alt: false,
      meta: false,
      shift: false,
    },
  ],
  ZOOM_IN: [
    {
      key: "KeyE",
    ctrl: false,
      alt: false,
    meta: false,
    shift: false,
    },
    {
      key: "Equal",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
  ],
  ZOOM_OUT_SMALL: [
    {
      key: "KeyQ",
      ctrl: false,
      alt: false,
      meta: false,
      shift: true,
    },
    {
      key: "Minus",
      ctrl: false,
      alt: false,
      meta: false,
      shift: true,
    },
  ],
  ZOOM_IN_SMALL: [
    {
      key: "KeyE",
      ctrl: false,
  }));

const shiftKeybind = (...keys: IKeybind["key"][]): IKeybind[] =>
  keys.map((key) => ({
    key,
    alt: false,
      meta: false,
      shift: true,
    },
    {
      key: "Equal",
    ctrl: false,
      alt: false,
    meta: false,
    shift: true,
    },
  ],
  PIXEL_WHOIS: [
    {
      key: "LCLICK",
      shift: true,
    },
    { key: "LONG_PRESS" },
  ],
  TEMPLATE_MOVE: [
    {
      key: "LCLICK",
      alt: true,
    },
  ],
  TOGGLE_TEMPLATE: [
    {
      key: "KeyT",
    },
  ],
  TOGGLE_BLANK: [
    {
      key: "KeyV", // legacy pxls keybind
    },
    {
      key: "KeyB",
    },
  ],
  TOGGLE_HEATMAP: [
    {
      key: "KeyH",
    },
  ],
  TOGGLE_GRID: [
    {
      key: "KeyG",
    },
  ],
  TOGGLE_UI: [
    {
      key: "KeyU",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
  ],
  TOGGLE_SETTINGS: [
    {
      key: "KeyO",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
  ],
  TOGGLE_INFO: [
    {
      key: "KeyI",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
  ],
  TOGGLE_MOD_MENU: [
    {
      key: "KeyM",
    },
  ],
  DESELECT_COLOR: [
    {
      key: "Escape",
    },
  ],
  SWAP_COLORS: [
    {
      key: "KeyX",
      ctrl: false,
      alt: false,
      meta: false,
      shift: false,
    },
  ],
  PICK_COLOR: [
    {
      key: "KeyC",
      ctrl: false,
  }));

const ctrlKeybind = (...keys: IKeybind["key"][]): IKeybind[] =>
  keys.map((key) => ({
    key,
    alt: false,
      meta: false,
      shift: false,
    },
  ],
  PICK_CANVAS_COLOR: [
    {
      key: "MCLICK",
    },
  ],
  MOD_SELECT: [
    {
      key: "LCLICK",
    ctrl: true,
    },
  ],
  CAPTURE_CANVAS: [
    {
      key: "KeyP",
      ctrl: false,
      alt: false,
    meta: false,
    shift: false,
    },
  ],
  CAPTURE_VIEW: [
    {
      key: "KeyL",
  }));

const altKeybind = (...keys: IKeybind["key"][]): IKeybind[] =>
  keys.map((key) => ({
    key,
    alt: true,
    ctrl: false,
      alt: false,
    meta: false,
    shift: false,
    },
  ],
  CAPTURE_REGION: [
    {
      key: "KeyL",
      ctrl: false,
      alt: false,
      meta: false,
      shift: true,
    },
  ],
  CAPTURE_TEMPLATE: [
    {
      key: "KeyP",
      ctrl: false,
      alt: false,
      meta: false,
      shift: true,
    },
  ],
  KEYBINDS: [
    {
      key: "Slash",
      shift: true,
    },
  }));

const isTypingTarget = (target: EventTarget | null) =>
  target instanceof HTMLElement &&
  Boolean(
    target.closest(
      'input, textarea, select, [contenteditable="true"], [role="textbox"]',
    ),
  );

const KEYBINDS = enforceObjectType({
  PAN_UP: unmodifiedKeybind("KeyW", "ArrowUp"),
  PAN_DOWN: unmodifiedKeybind("KeyS", "ArrowDown"),
  PAN_LEFT: unmodifiedKeybind("KeyA", "ArrowLeft"),
  PAN_RIGHT: unmodifiedKeybind("KeyD", "ArrowRight"),
  ZOOM_OUT: unmodifiedKeybind("KeyQ", "Minus"),
  ZOOM_IN: unmodifiedKeybind("KeyE", "Equal"),
  ZOOM_OUT_SMALL: shiftKeybind("KeyQ", "Minus"),
  ZOOM_IN_SMALL: shiftKeybind("KeyE", "Equal"),
  PIXEL_WHOIS: [
    ...shiftKeybind("LCLICK"),
    ...unmodifiedKeybind("LONG_PRESS"),
  ],
  TEMPLATE_MOVE: altKeybind("LCLICK"),
  TOGGLE_TEMPLATE: unmodifiedKeybind("KeyT"),
  TOGGLE_BLANK: unmodifiedKeybind("KeyV", "KeyB"),
  TOGGLE_HEATMAP: unmodifiedKeybind("KeyH"),
  TOGGLE_GRID: unmodifiedKeybind("KeyG"),
  TOGGLE_UI: unmodifiedKeybind("KeyU"),
  TOGGLE_SETTINGS: unmodifiedKeybind("KeyO"),
  TOGGLE_INFO: unmodifiedKeybind("KeyI"),
  TOGGLE_MOD_MENU: unmodifiedKeybind("KeyM"),
  DESELECT_COLOR: unmodifiedKeybind("Escape"),
  SWAP_COLORS: unmodifiedKeybind("KeyX"),
  PICK_COLOR: unmodifiedKeybind("KeyC"),
  PICK_CANVAS_COLOR: unmodifiedKeybind("MCLICK"),
  MOD_SELECT: ctrlKeybind("LCLICK"),
  CAPTURE_CANVAS: unmodifiedKeybind("KeyP"),
  CAPTURE_VIEW: unmodifiedKeybind("KeyL"),
  CAPTURE_REGION: shiftKeybind("KeyL"),
  CAPTURE_TEMPLATE: shiftKeybind("KeyP"),
  REACTION_LOVE: unmodifiedKeybind("Digit1", "Numpad1"),
  REACTION_JAM: unmodifiedKeybind("Digit2", "Numpad2"),
  REACTION_CUTE: unmodifiedKeybind("Digit3", "Numpad3"),
  REACTION_POG: unmodifiedKeybind("Digit4", "Numpad4"),
  REACTION_YAY: unmodifiedKeybind("Digit5", "Numpad5"),
  REACTION_BONGO: unmodifiedKeybind("Digit6", "Numpad6"),
  REACTION_SHOCKED: unmodifiedKeybind("Digit7", "Numpad7"),
  REACTION_WORK: unmodifiedKeybind("Digit8", "Numpad8"),
  REACTION_CRY: unmodifiedKeybind("Digit9", "Numpad9"),
  REACTION_AWE: unmodifiedKeybind("Digit0", "Numpad0"),
  KEYBINDS: shiftKeybind("Slash"),
});

class KeybindManager_ extends EventEmitter<{
@@ -266,13 +152,7 @@ class KeybindManager_ extends EventEmitter<{
  handleKeydown = (e: KeyboardEvent) => {
    if (!this.enabled) return;

    const blacklistedElements = ["INPUT"];

    if (e.target instanceof HTMLElement) {
      if (blacklistedElements.indexOf(e.target.tagName) > -1) {
        return;
      }
    }
    if (isTypingTarget(e.target)) return;

    if (e.key === "Alt") e.preventDefault();
    if (e.key === "Control") e.preventDefault();
@@ -282,15 +162,7 @@ class KeybindManager_ extends EventEmitter<{
  handleKeyup = (e: KeyboardEvent) => {
    if (!this.enabled) return;

    // discard if in an input element

    const blacklistedElements = ["INPUT"];

    if (e.target instanceof HTMLElement) {
      if (blacklistedElements.indexOf(e.target.tagName) > -1) {
        return;
      }
    }
    if (isTypingTarget(e.target)) return;

    const isHandled = this.handleInteraction(
      {