mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-14 12:47:33 +00:00
fix(mobile): prevent menu tap from triggering unintended dashboard navigation
The mobile sidebar menu button redirects users to `cloudcli.ai/dashboard` when a
session was active. The redirect happened because
the menu was opened on `touchstart`, which mounted the sidebar before the touch
sequence completed; the follow-up tap/click then landed on the sidebar header
anchor.
This change rewrites mobile menu interaction handling in `MainContent.jsx` to
eliminate touch/click event leakage and ghost-click behavior.
Key changes:
- Added `suppressNextMenuClickRef` to guard against synthetic click events that
fire after a touch interaction.
- Added `openMobileMenu(event)` helper to centralize `preventDefault`,
`stopPropagation`, and `onMenuClick()` invocation.
- Added `handleMobileMenuTouchEnd(event)`:
- opens the menu on `touchend` instead of `touchstart`
- sets a short suppression window (350ms) for the next click.
- Added `handleMobileMenuClick(event)`:
- ignores/suppresses click events during the suppression window
- otherwise opens the menu normally.
- Updated all mobile menu button instances in `MainContent.jsx` (loading state,
no-project state, active header state) to use:
- `onTouchEnd={handleMobileMenuTouchEnd}`
- `onClick={handleMobileMenuClick}`
- Removed the previous `onTouchStart` path that caused premature DOM mutation.
Behavioral impact:
- Mobile sidebar still opens reliably with one tap.
- Tap no longer leaks to newly-mounted sidebar header links.
- Prevents accidental redirects while preserving existing menu UX.
This commit is contained in:
@@ -63,6 +63,7 @@ function MainContent({
|
|||||||
const [isResizing, setIsResizing] = useState(false);
|
const [isResizing, setIsResizing] = useState(false);
|
||||||
const [editorExpanded, setEditorExpanded] = useState(false);
|
const [editorExpanded, setEditorExpanded] = useState(false);
|
||||||
const resizeRef = useRef(null);
|
const resizeRef = useRef(null);
|
||||||
|
const suppressNextMenuClickRef = useRef(false);
|
||||||
|
|
||||||
const { preferences } = useUiPreferences();
|
const { preferences } = useUiPreferences();
|
||||||
const { autoExpandTools, showRawParameters, showThinking, autoScrollToBottom, sendByCtrlEnter } = preferences;
|
const { autoExpandTools, showRawParameters, showThinking, autoScrollToBottom, sendByCtrlEnter } = preferences;
|
||||||
@@ -164,6 +165,34 @@ function MainContent({
|
|||||||
refreshTasks?.();
|
refreshTasks?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openMobileMenu = (event) => {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMenuClick();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMobileMenuTouchEnd = (event) => {
|
||||||
|
suppressNextMenuClickRef.current = true;
|
||||||
|
openMobileMenu(event);
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
suppressNextMenuClickRef.current = false;
|
||||||
|
}, 350);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMobileMenuClick = (event) => {
|
||||||
|
if (suppressNextMenuClickRef.current) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openMobileMenu(event);
|
||||||
|
};
|
||||||
|
|
||||||
// Handle resize functionality
|
// Handle resize functionality
|
||||||
const handleMouseDown = (e) => {
|
const handleMouseDown = (e) => {
|
||||||
if (isMobile) return; // Disable resize on mobile
|
if (isMobile) return; // Disable resize on mobile
|
||||||
@@ -218,7 +247,8 @@ function MainContent({
|
|||||||
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={handleMobileMenuClick}
|
||||||
|
onTouchEnd={handleMobileMenuTouchEnd}
|
||||||
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -256,7 +286,8 @@ function MainContent({
|
|||||||
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={handleMobileMenuClick}
|
||||||
|
onTouchEnd={handleMobileMenuTouchEnd}
|
||||||
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -297,11 +328,8 @@ function MainContent({
|
|||||||
<div className="flex items-center space-x-2 min-w-0 flex-1">
|
<div className="flex items-center space-x-2 min-w-0 flex-1">
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={handleMobileMenuClick}
|
||||||
onTouchStart={(e) => {
|
onTouchEnd={handleMobileMenuTouchEnd}
|
||||||
e.preventDefault();
|
|
||||||
onMenuClick();
|
|
||||||
}}
|
|
||||||
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 touch-manipulation active:scale-95 pwa-menu-button flex-shrink-0"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 touch-manipulation active:scale-95 pwa-menu-button flex-shrink-0"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -682,4 +710,4 @@ function MainContent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(MainContent);
|
export default React.memo(MainContent);
|
||||||
|
|||||||
Reference in New Issue
Block a user