All files / client/src/hooks use-pin-state.ts

96.66% Statements 29/30
100% Branches 10/10
100% Functions 7/7
96.66% Lines 29/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90                  27x       27x         27x     27x 14x 14x             27x     14x 2x 2x 2x           14x 14x 14x         27x   2x   2x 1x 1x 1x             27x 14x 3x   11x 9x 9x 8x     3x     27x                          
import { useState, useEffect, useCallback } from "react";
import type { PinMode } from "@shared/types/arduino.types";
 
interface UsePinStateParams {
  resetPinStates: () => void;
}
 
export function usePinState({ resetPinStates }: UsePinStateParams) {
  // Analog pins detected in the code that need sliders (internal pin numbers 14..19)
  const [analogPinsUsed, setAnalogPinsUsed] = useState<number[]>([]);
 
  // Detected explicit pinMode(...) declarations found during parsing.
  // We store modes for pins so that we can apply them when the simulation starts.
  const [detectedPinModes, setDetectedPinModes] = useState<
    Record<number, PinMode>
  >({});
 
  // Pins that have a detected pinMode(...) declaration which conflicts with analogRead usage
  const [pendingPinConflicts, setPendingPinConflicts] = useState<number[]>([]);
 
  // Pin Monitor visibility state (persisted to localStorage)
  const [pinMonitorVisible, setPinMonitorVisible] = useState<boolean>(() => {
    try {
      return globalThis.localStorage.getItem("unoPinMonitorVisible") === "1";
    } catch {
      return false; // Hidden by default
    }
  });
 
  // Listen for pin monitor visibility change events from settings dialog
  useEffect(() => {
    type BoolDetailEvent = CustomEvent<{ value: boolean }>;
 
    const handler = (ev: BoolDetailEvent) => {
      try {
        const newValue = Boolean(ev?.detail?.value);
        setPinMonitorVisible(newValue);
      } catch {
        // ignore
      }
    };
 
    document.addEventListener("pinMonitorVisibleChange", handler as EventListener);
    return () =>
      document.removeEventListener("pinMonitorVisibleChange", handler as EventListener);
  }, []);
 
  // Centralized helper to reset UI pin-related state. Pass { keepDetected: true }
  // to preserve detected pinMode declarations and pending conflicts when desired.
  const resetPinUI = useCallback(
    (opts?: { keepDetected?: boolean }) => {
      resetPinStates();
      // Only clear detected/derived data when keepDetected is not requested.
      if (!opts?.keepDetected) {
        setAnalogPinsUsed([]);
        setDetectedPinModes({});
        setPendingPinConflicts([]);
      }
    },
    [resetPinStates],
  );
 
  // Helper function to convert pin strings to numbers (A0-A5 → 14-19, digital → as-is)
  const pinToNumber = (pinStr: string): number | null => {
    if (/^\d+$/.test(pinStr)) {
      return Number.parseInt(pinStr, 10);
    }
    if (/^A\d+$/i.test(pinStr)) {
      const analogIndex = Number.parseInt(pinStr.slice(1), 10);
      if (analogIndex >= 0 && analogIndex <= 5) {
        return 14 + analogIndex; // A0->14, A1->15, ..., A5->19
      }
    }
    return null;
  };
 
  return {
    analogPinsUsed,
    setAnalogPinsUsed,
    detectedPinModes,
    setDetectedPinModes,
    pendingPinConflicts,
    setPendingPinConflicts,
    pinMonitorVisible,
    setPinMonitorVisible,
    resetPinUI,
    pinToNumber,
  };
}