fix: chat accepts quotes now

This commit is contained in:
gosangam
2026-08-29 11:58:33 +05:30
parent 028a8d7e1c
commit 76cd106ef7
11 changed files with 559 additions and 41 deletions
+87
View File
@@ -0,0 +1,87 @@
"use client";
/**
* Distinguishes a tap from a press-and-hold on the same control.
*
* Built on Pointer Events so one implementation covers touch, mouse and pen.
* Two details make it behave:
*
* - A hold that fires must swallow the click that follows it, or the tap
* action runs straight after the hold action.
* - Small finger movement during a hold is normal, so the press only
* cancels once it drifts past a slop threshold — otherwise holds fail
* constantly on touch.
*
* Timing and slop match the chat bubbles' gesture handler so a hold feels the
* same everywhere in the app.
*/
import { useCallback, useEffect, useRef } from "react";
const LONG_PRESS_MS = 450;
const LONG_PRESS_SLOP_PX = 8;
export function useLongPress({
onClick,
onLongPress,
disabled = false,
}: {
onClick: () => void;
onLongPress: () => void;
disabled?: boolean;
}) {
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
const start = useRef<{ x: number; y: number } | null>(null);
const fired = useRef(false);
const clear = useCallback(() => {
if (timer.current) clearTimeout(timer.current);
timer.current = null;
start.current = null;
}, []);
// A hold can end with the pointer outside the button, or with the component
// unmounting behind whatever the hold opened; either would leak the timer.
useEffect(() => clear, [clear]);
const onPointerDown = (e: React.PointerEvent) => {
if (disabled) return;
fired.current = false;
start.current = { x: e.clientX, y: e.clientY };
timer.current = setTimeout(() => {
fired.current = true;
clear();
onLongPress();
}, LONG_PRESS_MS);
};
const onPointerMove = (e: React.PointerEvent) => {
if (!start.current) return;
const dx = e.clientX - start.current.x;
const dy = e.clientY - start.current.y;
if (Math.abs(dx) > LONG_PRESS_SLOP_PX || Math.abs(dy) > LONG_PRESS_SLOP_PX) {
clear();
}
};
return {
onPointerDown,
onPointerMove,
onPointerUp: clear,
onPointerCancel: clear,
onPointerLeave: clear,
// Suppress iOS's press-and-hold callout, which would otherwise cover the
// sheet the hold just opened.
onContextMenu: (e: React.MouseEvent) => {
if (fired.current) e.preventDefault();
},
onClick: () => {
// The hold already acted; the browser's trailing click is not a tap.
if (fired.current) {
fired.current = false;
return;
}
if (!disabled) onClick();
},
};
}