React: Implementing Long Press Button Functionality

27 Views Asked by At

I'm attempting to implement a long press button functionality in React where pressing and holding the '0' digit button for 3 seconds turns it into a '+' symbol, and when it's just clicked, it should print '0'. The desired behavior is similar to the dialpad in our phones. However, in my implementation, when I long press the '0' digit button, it does print '+', but it also prints two '0's along with it. How can I resolve this issue?

Here is my code:

interface PhoneDialPadPopoverProps {
  isLoading: boolean;
  onConnect: (digits: string) => void;
}

const PhoneDialPadPopover = ({
  isLoading,
  onConnect,
}: PhoneDialPadPopoverProps) => {
  // State variables
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
  const [enteredDigits, setEnteredDigits] = useState('');
  const [isLongPressing0, setIsLongPressing0] = useState(false); // Flag to track long press
  const [longPressTimer, setLongPressTimer] = useState<NodeJS.Timeout | null>(
    null
  );
  const theme = useTheme();

  const mdUp = useMediaQuery((theme: Theme) => theme.breakpoints.up('md'));

  const open = Boolean(anchorEl);
  const id = open ? 'connect-dial-pad-popover' : undefined;

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleButtonClick = (digit: string) => {
    const newDigits = enteredDigits + digit;
    setEnteredDigits(newDigits);
    onConnect(digit);
  };

  const handleLongPress = () => {
    setLongPressTimer(null); // Clear existing timer
    handleButtonClick('+'); // Trigger '+' button action
  };

  const handleMouseDown = () => {
    // Start the timer for long press
    const timer = setTimeout(handleLongPress, 3000); // Adjust the delay as needed
    setLongPressTimer(timer);
  };

  const handleMouseUp = () => {
    // Clear the timer for long press
    if (longPressTimer) {
      clearTimeout(longPressTimer);
      setLongPressTimer(null);
    }
    // If it was a long press, prevent the regular click event
    if (isLongPressing0) {
      setIsLongPressing0(false);
    } else {
      // Regular click event for '0' digit only if it's not a long press
      if (!isLongPressing0) {
        handleButtonClick('0');
      }
    }
  };


  const dialerButtons = [
    { digit: '1' },
    { digit: '2', letters: 'ABC' },
    { digit: '3', letters: 'DEF' },
    { digit: '4', letters: 'GHI' },
    { digit: '5', letters: 'JKL' },
    { digit: '6', letters: 'MNO' },
    { digit: '7', letters: 'PQRS' },
    { digit: '8', letters: 'TUV' },
    { digit: '9', letters: 'WXYZ' },
    { digit: '*' },
    { digit: '0', letters: '+' },
    { digit: '#' },
  ];

  return (
    <div>
      <Button
        aria-describedby={id}
        variant={'outlined-icon'}
        color="grey"
        onClick={handleClick}
        startIcon={
          isLoading ? (
            <CircularProgress size={16} />
          ) : (
            <Iconify icon="mdi:dialpad" />
          )
        }
      ></Button>
      <StyledPopover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        marginThreshold={mdUp ? 64 : 32}
      >
        <IconButton
          aria-label="close"
          onClick={handleClose}
          color="grey"
          sx={{
            position: 'absolute',
            top: theme.spacing(1),
            right: theme.spacing(2),
          }}
        >
          <Iconify icon="mdi:close" />
        </IconButton>
        <TextField
          variant="outlined"
          fullWidth
          value={enteredDigits}
          InputProps={{
            readOnly: true,
            inputProps: {
              style: { textAlign: 'center', fontWeight: 'bold' },
              'aria-readonly': true,
            },
          }}
          style={{ marginTop: '25px' }}
        />

        <Box display="grid" gridTemplateColumns="repeat(3, 1fr)" gap={1}>
          {dialerButtons.map((button, index) => (
            <button
              key={index}
              onMouseDown={() =>
                button.digit === '0' ? handleMouseDown() : null
              }
              onMouseUp={handleMouseUp}
              onClick={() => handleButtonClick(button.digit)}
              style={{
                border: 'none',
                background: 'none',
                outline: 'none',
                padding: theme.spacing(1),
              }}
            >
              <Typography variant="body2" align="center" fontWeight="bold">
                {button.digit}
              </Typography>
              {button.letters && (
                <Typography variant="body2" align="center" color="grey">
                  {button.letters}
                </Typography>
              )}
            </button>
          ))}
        </Box>
      </StyledPopover>
    </div>
  );
};

export default PhoneDialPadPopover;

"I attempted to implement a long press functionality for the '0' digit button in a React component. I expected that when I long pressed the '0' button, it would print a '+' symbol, and when I single-clicked it, it would print '0'. However, when I long pressed the button, it printed '+00' instead of just '+'. I tried adjusting the code logic and timer delay, but it still resulted in the same issue."

0

There are 0 best solutions below