heartwood every commit a ring
3.7 KB raw
import React, { useContext, useState } from "react";
import { useRouter } from "next/router";
import { GlobalHotKeys, configure } from "react-hotkeys";
import PropTypes from "prop-types";
import { toast } from "react-toastify";

import { Context } from "../components/context";
import KeyHelpOverlay from "./keyHelpOverlay";
import contextStrings from "../l10n/context";

configure({
  ignoreTags: [],
  ignoreEventsCondition: (keyEvent) => {
    if (keyEvent.key === "Enter" || keyEvent.key === " ") {
      return true;
    }
    const t = keyEvent.target;
    const isTypingField =
      t &&
      (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable);
    if (isTypingField && keyEvent.key === "?") {
      return true;
    }
    return false;
  },
});

const keyMap = {
  RESET: "alt+r",
  ADD_LOG: "alt+a",
  PAUSE_TOGGLE: "alt+p",
  TIMER_PAGE: "alt+t",
  LOG_PAGE: "alt+l",
  ABOUT_PAGE: "alt+o",
  SUMMARY_PAGE: "alt+s",
  CLEAR_LOG: "alt+c",
  LOG_NEXT: "ArrowDown",
  LOG_PREVIOUS: "ArrowUp",
  LOG_EDIT: "alt+e",
  LOG_DELETE_SINGLE: "alt+d",
  KEY_HELP: ["shift+/", "?"],
};

const HotKeysMapping = (props) => {
  const { state, dispatch } = useContext(Context);
  const [showHelp, setShowHelp] = useState(false);

  const router = useRouter();

  const onLogPage = () => router.pathname === "/log";

  const handlers = {
    RESET: () => dispatch({ type: "NEW_TIMER" }),
    ADD_LOG: (event) => {
      event.preventDefault();
      if (state.note.trim()) {
        dispatch({ type: "ADD_LOG", note: state.note });
        contextStrings.setLanguage(state.language);
        toast.success(contextStrings.addedEntry);
      }
    },
    PAUSE_TOGGLE: (event) => {
      event.preventDefault();
      dispatch({
        type: state.timerPausedAt ? "RESUME_TIMER" : "PAUSE_TIMER",
      });
    },
    TIMER_PAGE: (event) => {
      event.preventDefault();
      router.push("/");
    },
    LOG_PAGE: () => router.push("/log"),
    ABOUT_PAGE: () => router.push("/about"),
    SUMMARY_PAGE: () => router.push("/summary"),
    CLEAR_LOG: () => {
      if (state.log.length === 0) return;
      contextStrings.setLanguage(state.language);
      if (!window.confirm(contextStrings.confirmClearLog)) return;
      dispatch({ type: "CLEAR_LOG" });
      toast.error(contextStrings.resetLog);
    },
    LOG_NEXT: (event) => {
      event.preventDefault();
      if (onLogPage()) {
        dispatch({ type: "TOGGLE_EDITION", edit: false });
        dispatch({ type: "NEXT_LOG_ITEM" });
      }
    },
    LOG_PREVIOUS: (event) => {
      event.preventDefault();
      if (onLogPage()) {
        dispatch({ type: "TOGGLE_EDITION", edit: false });
        dispatch({ type: "PREVIOUS_LOG_ITEM" });
      }
    },
    LOG_EDIT: (event) => {
      event.preventDefault();
      if (!state.logSelectedEntry) return;
      if (onLogPage()) dispatch({ type: "TOGGLE_EDITION", edit: true });
    },
    LOG_DELETE_SINGLE: (event) => {
      event.preventDefault();
      if (!state.logSelectedEntry) return;
      if (!onLogPage()) return;
      contextStrings.setLanguage(state.language);
      if (!window.confirm(contextStrings.confirmDeleteEntry)) return;
      dispatch({ type: "REMOVE_LOG" });
      toast.error(contextStrings.deletedEntry);
    },
    KEY_HELP: (event) => {
      event.preventDefault();
      setShowHelp((v) => !v);
    },
  };

  return (
    <GlobalHotKeys keyMap={keyMap} handlers={handlers}>
      {props.children}
      <KeyHelpOverlay open={showHelp} onClose={() => setShowHelp(false)} />
    </GlobalHotKeys>
  );
};

HotKeysMapping.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.arrayOf(PropTypes.element),
  ]),
};

export default HotKeysMapping;