BlokusPokus
BlokusPokus

Reputation: 1

How to properly handle key up for modifier keys? Bug when releasing modifier key last

I am building a shortcut practice application using React and am encountering a bug that I don't understand. To detect when a wrong combination is entered, I validate the maximum length of the key sequence and use validateCombination() when the length returns to 0 to check if it is incorrect. This works well, except when modifier keys are the last keys released, in which case it does not detect the wrong combination.

import React, { useEffect } from "react";
import { vsCodeShortchutMac } from "./shortcutData";

interface KeySequenceListenerProps {
  keySequence: string;
  setInputHistory: React.Dispatch<React.SetStateAction<{ text: string; status: "skipped" | "found" | "wrong" }[]>>;
  inputHistory: { text: string; status: "skipped" | "found" | "wrong" }[];
  currentShortcutIndex: number;
  gameStarted: boolean;
}

const BadShortcut: React.FC<KeySequenceListenerProps> = ({
  inputHistory,
  setInputHistory,
  currentShortcutIndex,
  gameStarted,
}) => {
  let numberOfKeysStillPressed = 0;
  let currentKeys = "";
  const currentShortcut = vsCodeShortchutMac[currentShortcutIndex];
  const pressedKeys = new Set<string>(); // Track pressed keys

  useEffect(() => {
    if (!gameStarted) return;

    const handleKeyDown = (e: KeyboardEvent) => {
      e.preventDefault();
      if (!pressedKeys.has(e.key)) {
        pressedKeys.add(e.key);
        currentKeys += e.key;
        numberOfKeysStillPressed++;
        console.log(`Key down: ${e.key}, currentKeys: ${currentKeys}, numberOfKeysStillPressed: ${numberOfKeysStillPressed}`);
      }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      e.preventDefault();
      console.log(`Key up event: ${e.key}`); // Debugging log
      if (pressedKeys.has(e.key)) {
        pressedKeys.delete(e.key);
        numberOfKeysStillPressed--;
        console.log(`Key up: ${e.key}, numberOfKeysStillPressed: ${numberOfKeysStillPressed}`);
        if (numberOfKeysStillPressed <= 0) {
          validateCombination();
          currentKeys = "";
          numberOfKeysStillPressed = 0;
        }
      }
    };

    const validateCombination = () => {
      if (currentKeys !== currentShortcut.key) {
        const wrongKey = `${currentKeys} - (Wrong)`;
        setInputHistory((prev) => [
          ...prev,
          { text: wrongKey, status: "wrong" },
        ]);
      }
      console.log("Reset currentKeys");
    };

    document.addEventListener("keydown", handleKeyDown);
    document.addEventListener("keyup", handleKeyUp);

    return () => {
      document.removeEventListener("keydown", handleKeyDown);
      document.removeEventListener("keyup", handleKeyUp);
    };

  }, [gameStarted, currentShortcut.key, setInputHistory, inputHistory, currentKeys]);

  return null;
};

export default BadShortcut;

You can see in the log that when I release the modifier key last, it does not detect it, but with regular characters it will.


BadShortcut.tsx:32 Key down: Shift, currentKeys: Shift, numberOfKeysStillPressed: 1
BadShortcut.tsx:32 Key down: A, currentKeys: ShiftA, numberOfKeysStillPressed: 2
BadShortcut.tsx:38 Key up event: A
BadShortcut.tsx:42 Key up: A, numberOfKeysStillPressed: 1
BadShortcut.tsx:38 Key up event: Shift
BadShortcut.tsx:42 Key up: Shift, numberOfKeysStillPressed: 0
BadShortcut.tsx:59 Reset currentKeys
BadShortcut.tsx:32 Key down: a, currentKeys: a, numberOfKeysStillPressed: 1
BadShortcut.tsx:32 Key down: Shift, currentKeys: aShift, numberOfKeysStillPressed: 2
BadShortcut.tsx:38 Key up event: A
BadShortcut.tsx:38 Key up event: Shift
BadShortcut.tsx:42 Key up: Shift, numberOfKeysStillPressed: 1
BadShortcut.tsx:32 Key down: Shift, currentKeys: aShiftShift, numberOfKeysStillPressed: 2
BadShortcut.tsx:38 Key up event: A
BadShortcut.tsx:38 Key up event: Shift
BadShortcut.tsx:42 Key up: Shift, numberOfKeysStillPressed: 1
BadShortcut.tsx:32 Key down: Shift, currentKeys: aShiftShiftShift, numberOfKeysStillPressed: 2
BadShortcut.tsx:32 Key down: A, currentKeys: aShiftShiftShiftA, numberOfKeysStillPressed: 3
BadShortcut.tsx:38 Key up event: A
BadShortcut.tsx:42 Key up: A, numberOfKeysStillPressed: 2
BadShortcut.tsx:38 Key up event: Shift
BadShortcut.tsx:42 Key up: Shift, numberOfKeysStillPressed: 1

Upvotes: 0

Views: 31

Answers (0)

Related Questions