Make Sasquatch bubble open reliably on first tap (mobile)
The "1st tap does nothing, 2nd/3rd works" bug came from relying solely on pointer-capture tap detection, which is flaky on the first interaction after mount in mobile WebViews — and the earlier e.preventDefault() on pointerup was suppressing the click we could otherwise use. Fix: add a plain onClick as a reliable open path (the most dependable "tapped" signal in a WebView), extract an idempotent openPanel() used by both pointerup and click, and drop the preventDefault so the click isn't swallowed. The justOpenedRef lock still blocks the ghost-click that would re-close it. Double firing is harmless since openPanel just sets visible=true. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
ebbd0fd125
commit
834f33d53e
1 changed files with 25 additions and 14 deletions
|
|
@ -392,14 +392,15 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
|
||||||
setDragPos({ x: newX, y: newY });
|
setDragPos({ x: newX, y: newY });
|
||||||
}, [setDragPos]);
|
}, [setDragPos]);
|
||||||
|
|
||||||
|
// Single source of truth for opening the panel. Sets the "just opened" lock
|
||||||
|
// so a stray follow-up event from the same tap can't immediately close it.
|
||||||
|
const openPanel = useCallback(() => {
|
||||||
|
justOpenedRef.current = Date.now();
|
||||||
|
setIsMinimized(false);
|
||||||
|
setIsVisible(true);
|
||||||
|
}, [setIsMinimized]);
|
||||||
|
|
||||||
const onPointerUp = useCallback((e: React.PointerEvent) => {
|
const onPointerUp = useCallback((e: React.PointerEvent) => {
|
||||||
// Suppress the synthesized mouse "click" that follows a touch pointerup.
|
|
||||||
// Without this, on mobile WebViews the ghost click lands on whatever element
|
|
||||||
// is now under the finger (the panel unmounts the bubble the instant we
|
|
||||||
// open) and can immediately re-close the panel — the "opens then closes"
|
|
||||||
// bug.
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
try {
|
try {
|
||||||
if (dragRef.current) dragRef.current.releasePointerCapture(e.pointerId);
|
if (dragRef.current) dragRef.current.releasePointerCapture(e.pointerId);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -409,15 +410,24 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
|
||||||
dragStart.current = null;
|
dragStart.current = null;
|
||||||
dragMoved.current = false;
|
dragMoved.current = false;
|
||||||
setTimeout(() => setIsDragging(false), 50);
|
setTimeout(() => setIsDragging(false), 50);
|
||||||
|
// If this pointer sequence was a genuine tap (no real drag), open here.
|
||||||
|
// A parallel onClick handler also opens as a reliable fallback for mobile
|
||||||
|
// WebViews where the pointer-capture tap can be flaky on first interaction;
|
||||||
|
// openPanel is idempotent so double-firing is harmless.
|
||||||
if (!wasDrag) {
|
if (!wasDrag) {
|
||||||
// Always open the panel — even on pages without a context message.
|
openPanel();
|
||||||
// Set a short "just opened" lock so a stray follow-up event from the same
|
|
||||||
// tap can't immediately minimize/close it.
|
|
||||||
justOpenedRef.current = Date.now();
|
|
||||||
setIsMinimized(false);
|
|
||||||
setIsVisible(true);
|
|
||||||
}
|
}
|
||||||
}, [setIsMinimized]);
|
}, [openPanel]);
|
||||||
|
|
||||||
|
// Reliable open path: plain click. On mobile WebViews the pointer-capture tap
|
||||||
|
// detection can miss the very first interaction after mount (the "1st tap does
|
||||||
|
// nothing, 2nd works" bug). A normal onClick is the most dependable "I tapped
|
||||||
|
// this" signal, so we use it as a fallback — but ignore it if the gesture was
|
||||||
|
// actually a drag.
|
||||||
|
const onBubbleClick = useCallback(() => {
|
||||||
|
if (dragMoved.current) return;
|
||||||
|
openPanel();
|
||||||
|
}, [openPanel]);
|
||||||
|
|
||||||
const bubbleStyle = useMemo(() => {
|
const bubbleStyle = useMemo(() => {
|
||||||
if (dragPos) {
|
if (dragPos) {
|
||||||
|
|
@ -682,6 +692,7 @@ Instructions:
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
onPointerMove={onPointerMove}
|
onPointerMove={onPointerMove}
|
||||||
onPointerUp={onPointerUp}
|
onPointerUp={onPointerUp}
|
||||||
|
onClick={onBubbleClick}
|
||||||
title="Press and drag to move · Tap to open"
|
title="Press and drag to move · Tap to open"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue