Fix account hovercard sometimes not triggering (#39381)

This commit is contained in:
diondiondion 2026-06-11 18:18:44 +02:00 committed by GitHub
parent 8ba8ce5669
commit 2e3b81cc1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -30,8 +30,8 @@ export const HoverCardController: React.FC = () => {
const cardRef = useRef<HTMLDivElement | null>(null); const cardRef = useRef<HTMLDivElement | null>(null);
const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout(); const [setLeaveTimeout, cancelLeaveTimeout] = useTimeout();
const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout(); const [setEnterTimeout, cancelEnterTimeout, delayEnterTimeout] = useTimeout();
const [setMoveTimeout, cancelMoveTimeout] = useTimeout();
const [setScrollTimeout] = useTimeout(); const [setScrollTimeout] = useTimeout();
const lastMouseMoveTime = useRef<number>(0);
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
cancelEnterTimeout(); cancelEnterTimeout();
@ -50,7 +50,6 @@ export const HoverCardController: React.FC = () => {
useEffect(() => { useEffect(() => {
let isScrolling = false; let isScrolling = false;
let isUsingTouch = false; let isUsingTouch = false;
let isActiveMouseMovement = false;
let currentAnchor: HTMLElement | null = null; let currentAnchor: HTMLElement | null = null;
let currentTitle: string | null = null; let currentTitle: string | null = null;
@ -84,31 +83,40 @@ export const HoverCardController: React.FC = () => {
return; return;
} }
// Bail out if we're scrolling, a touch is active, // This 0ms timeout is needed to push processing of this code
// or if there was no active mouse movement // until after the mousemove event was run, in order to be able
if (isScrolling || !isActiveMouseMovement || isUsingTouch) { // to track the most recent value of lastMouseMoveTime.current
return; setTimeout(() => {
} // Check if mouse moved within the active movement threshold
const timeSinceLastMove = Date.now() - lastMouseMoveTime.current;
const hasRecentMovement = timeSinceLastMove < activeMovementThreshold;
// We've entered an anchor // Bail out if we're scrolling, a touch is active,
if (isHoverCardAnchor(target)) { // or if there was no active mouse movement
cancelLeaveTimeout(); if (isScrolling || !hasRecentMovement || isUsingTouch) {
return;
}
currentAnchor?.removeAttribute('aria-describedby'); // We've entered an anchor
currentAnchor = target; if (isHoverCardAnchor(target)) {
cancelLeaveTimeout();
currentTitle = target.getAttribute('title'); currentAnchor?.removeAttribute('aria-describedby');
target.removeAttribute('title'); currentAnchor = target;
setEnterTimeout(() => { currentTitle = target.getAttribute('title');
open(target); target.removeAttribute('title');
}, enterDelay);
}
// We've entered the hover card setEnterTimeout(() => {
if (target === currentAnchor || target === cardRef.current) { open(target);
cancelLeaveTimeout(); }, enterDelay);
} }
// We've entered the hover card
if (target === currentAnchor || target === cardRef.current) {
cancelLeaveTimeout();
}
}, 0);
}; };
const handleMouseLeave = (e: MouseEvent) => { const handleMouseLeave = (e: MouseEvent) => {
@ -149,18 +157,15 @@ export const HoverCardController: React.FC = () => {
isUsingTouch = false; isUsingTouch = false;
} }
const hasMoved = Math.max(e.movementX, e.movementY) > 0; const hasMoved =
Math.max(Math.abs(e.movementX), Math.abs(e.movementY)) > 0;
if (!hasMoved) { if (!hasMoved) {
return; return;
} }
delayEnterTimeout(enterDelay); delayEnterTimeout(enterDelay);
lastMouseMoveTime.current = Date.now();
cancelMoveTimeout();
isActiveMouseMovement = true;
setMoveTimeout(() => {
isActiveMouseMovement = false;
}, activeMovementThreshold);
}; };
document.body.addEventListener('touchstart', handleTouchStart, { document.body.addEventListener('touchstart', handleTouchStart, {
@ -204,8 +209,6 @@ export const HoverCardController: React.FC = () => {
setOpen, setOpen,
setAccountId, setAccountId,
setAnchor, setAnchor,
setMoveTimeout,
cancelMoveTimeout,
]); ]);
return ( return (