Просмотр исходного кода

♻️Refactor: Decouple sidebar visibility from HeaderBar nav clicks

The `handleNavLinkClick` function in `HeaderBar.js` was previously
forcing the sidebar to be visible for all non-'home' navigation links
on non-mobile devices. This interfered with the intended logic in
`StyleContext` which controls sidebar visibility based on the current
route.

This commit modifies `handleNavLinkClick` to:
- Only apply specific style dispatches (setting inner padding and sider
  to false) for the 'home' link, which may have unique layout requirements.
- Remove the logic that unconditionally set sidebar visibility and inner
  padding for other navigation links.
- Continue to close the mobile menu загрязнения (`setMobileMenuOpen(false)`) on any nav link click.

This change ensures that `StyleContext` is the single source of truth
for determining sidebar visibility based on the route, resolving an
issue where clicking a non-console link Pferde (e.g., 'Pricing', 'About')
would incorrectly display the sidebar, especially when the link was
clicked Pferde a second time while already on that page.
Apple\Apple 9 месяцев назад
Родитель
Сommit
4640d0a4aa
2 измененных файлов с 65 добавлено и 68 удалено
  1. 19 11
      web/src/components/HeaderBar.js
  2. 46 57
      web/src/context/Style/index.js

+ 19 - 11
web/src/components/HeaderBar.js

@@ -136,7 +136,6 @@ const HeaderBar = () => {
   }, [i18n]);
   }, [i18n]);
 
 
   useEffect(() => {
   useEffect(() => {
-    // 模拟加载用户状态的过程
     const timer = setTimeout(() => {
     const timer = setTimeout(() => {
       setIsLoading(false);
       setIsLoading(false);
     }, 500);
     }, 500);
@@ -152,17 +151,25 @@ const HeaderBar = () => {
     if (itemKey === 'home') {
     if (itemKey === 'home') {
       styleDispatch({ type: 'SET_INNER_PADDING', payload: false });
       styleDispatch({ type: 'SET_INNER_PADDING', payload: false });
       styleDispatch({ type: 'SET_SIDER', payload: false });
       styleDispatch({ type: 'SET_SIDER', payload: false });
-    } else {
-      styleDispatch({ type: 'SET_INNER_PADDING', payload: true });
-      if (!styleState.isMobile) {
-        styleDispatch({ type: 'SET_SIDER', payload: true });
-      }
     }
     }
     setMobileMenuOpen(false);
     setMobileMenuOpen(false);
   };
   };
 
 
-  const renderNavLinks = (isMobileView = false) =>
-    mainNavLinks.map((link) => {
+  const renderNavLinks = (isMobileView = false, isLoading = false) => {
+    if (isLoading) {
+      const skeletonLinkClasses = isMobileView
+        ? 'flex items-center gap-1 p-3 w-full rounded-md'
+        : 'flex items-center gap-1 p-2 rounded-md';
+      return Array(4)
+        .fill(null)
+        .map((_, index) => (
+          <div key={index} className={skeletonLinkClasses}>
+            <Skeleton.Title style={{ width: isMobileView ? 100 : 60, height: 16 }} />
+          </div>
+        ));
+    }
+
+    return mainNavLinks.map((link) => {
       const commonLinkClasses = isMobileView
       const commonLinkClasses = isMobileView
         ? 'flex items-center gap-1 p-3 w-full text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors font-semibold'
         ? 'flex items-center gap-1 p-3 w-full text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors font-semibold'
         : 'flex items-center gap-1 p-2 text-sm text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors rounded-md font-semibold';
         : 'flex items-center gap-1 p-2 text-sm text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors rounded-md font-semibold';
@@ -196,6 +203,7 @@ const HeaderBar = () => {
         </Link>
         </Link>
       );
       );
     });
     });
+  };
 
 
   const renderUserArea = () => {
   const renderUserArea = () => {
     if (isLoading) {
     if (isLoading) {
@@ -349,7 +357,7 @@ const HeaderBar = () => {
             <div className="md:hidden">
             <div className="md:hidden">
               <Button
               <Button
                 icon={
                 icon={
-                  isConsoleRoute 
+                  isConsoleRoute
                     ? (styleState.showSider ? <IconClose className="text-lg" /> : <IconMenu className="text-lg" />)
                     ? (styleState.showSider ? <IconClose className="text-lg" /> : <IconMenu className="text-lg" />)
                     : (mobileMenuOpen ? <IconClose className="text-lg" /> : <IconMenu className="text-lg" />)
                     : (mobileMenuOpen ? <IconClose className="text-lg" /> : <IconMenu className="text-lg" />)
                 }
                 }
@@ -416,7 +424,7 @@ const HeaderBar = () => {
             )}
             )}
 
 
             <nav className="hidden md:flex items-center gap-1 lg:gap-2 ml-6">
             <nav className="hidden md:flex items-center gap-1 lg:gap-2 ml-6">
-              {renderNavLinks()}
+              {renderNavLinks(false, isLoading)}
             </nav>
             </nav>
           </div>
           </div>
 
 
@@ -496,7 +504,7 @@ const HeaderBar = () => {
           `}
           `}
         >
         >
           <nav className="flex flex-col gap-1">
           <nav className="flex flex-col gap-1">
-            {renderNavLinks(true)}
+            {renderNavLinks(true, isLoading)}
           </nav>
           </nav>
         </div>
         </div>
       </div>
       </div>

+ 46 - 57
web/src/context/Style/index.js

@@ -1,21 +1,34 @@
 // contexts/User/index.jsx
 // contexts/User/index.jsx
 
 
-import React, { useState, useEffect } from 'react';
-import { isMobile } from '../../helpers/index.js';
+import React, { useState, useEffect, useCallback } from 'react';
+import { useLocation } from 'react-router-dom';
+import { isMobile as getIsMobile } from '../../helpers/index.js';
 
 
 export const StyleContext = React.createContext({
 export const StyleContext = React.createContext({
   dispatch: () => null,
   dispatch: () => null,
 });
 });
 
 
 export const StyleProvider = ({ children }) => {
 export const StyleProvider = ({ children }) => {
+  const location = useLocation();
+  const initialIsMobile = getIsMobile();
+
+  const initialPathname = location.pathname;
+  let initialShowSiderValue = false;
+  let initialInnerPaddingValue = false;
+
+  if (initialPathname.includes('/console')) {
+    initialShowSiderValue = !initialIsMobile;
+    initialInnerPaddingValue = true;
+  }
+
   const [state, setState] = useState({
   const [state, setState] = useState({
-    isMobile: isMobile(),
-    showSider: false,
+    isMobile: initialIsMobile,
+    showSider: initialShowSiderValue,
     siderCollapsed: false,
     siderCollapsed: false,
-    shouldInnerPadding: false,
+    shouldInnerPadding: initialInnerPaddingValue,
   });
   });
 
 
-  const dispatch = (action) => {
+  const dispatch = useCallback((action) => {
     if ('type' in action) {
     if ('type' in action) {
       switch (action.type) {
       switch (action.type) {
         case 'TOGGLE_SIDER':
         case 'TOGGLE_SIDER':
@@ -39,64 +52,40 @@ export const StyleProvider = ({ children }) => {
     } else {
     } else {
       setState((prev) => ({ ...prev, ...action }));
       setState((prev) => ({ ...prev, ...action }));
     }
     }
-  };
+  }, []);
 
 
   useEffect(() => {
   useEffect(() => {
-    const updateIsMobile = () => {
-      const mobileDetected = isMobile();
-      dispatch({ type: 'SET_MOBILE', payload: mobileDetected });
-
-      // If on mobile, we might want to auto-hide the sidebar
-      if (mobileDetected && state.showSider) {
-        dispatch({ type: 'SET_SIDER', payload: false });
-      }
-    };
-
-    updateIsMobile();
-
-    const updateShowSider = () => {
-      // check pathname
-      const pathname = window.location.pathname;
-      if (
-        pathname === '' ||
-        pathname === '/' ||
-        pathname.includes('/home') ||
-        pathname.includes('/chat')
-      ) {
-        dispatch({ type: 'SET_SIDER', payload: false });
-        dispatch({ type: 'SET_INNER_PADDING', payload: false });
-      } else if (pathname === '/setup') {
-        dispatch({ type: 'SET_SIDER', payload: false });
-        dispatch({ type: 'SET_INNER_PADDING', payload: false });
-      } else {
-        // Only show sidebar on non-mobile devices by default
-        dispatch({ type: 'SET_SIDER', payload: !isMobile() });
-        dispatch({ type: 'SET_INNER_PADDING', payload: true });
-      }
-    };
-
-    updateShowSider();
-
-    const updateSiderCollapsed = () => {
-      const isCollapsed =
-        localStorage.getItem('default_collapse_sidebar') === 'true';
-      dispatch({ type: 'SET_SIDER_COLLAPSED', payload: isCollapsed });
+    const updateMobileStatus = () => {
+      dispatch({ type: 'SET_MOBILE', payload: getIsMobile() });
     };
     };
+    window.addEventListener('resize', updateMobileStatus);
+    return () => window.removeEventListener('resize', updateMobileStatus);
+  }, [dispatch]);
 
 
-    updateSiderCollapsed();
+  useEffect(() => {
+    if (state.isMobile && state.showSider) {
+      dispatch({ type: 'SET_SIDER', payload: false });
+    }
+  }, [state.isMobile, state.showSider, dispatch]);
 
 
-    // Add event listeners to handle window resize
-    const handleResize = () => {
-      updateIsMobile();
-    };
+  useEffect(() => {
+    const currentPathname = location.pathname;
+    const currentlyMobile = getIsMobile();
 
 
-    window.addEventListener('resize', handleResize);
+    if (currentPathname === '/console' || currentPathname.startsWith('/console/')) {
+      dispatch({ type: 'SET_SIDER', payload: !currentlyMobile });
+      dispatch({ type: 'SET_INNER_PADDING', payload: true });
+    } else {
+      dispatch({ type: 'SET_SIDER', payload: false });
+      dispatch({ type: 'SET_INNER_PADDING', payload: false });
+    }
+  }, [location.pathname, dispatch]);
 
 
-    // Cleanup event listener on component unmount
-    return () => {
-      window.removeEventListener('resize', handleResize);
-    };
-  }, []);
+  useEffect(() => {
+    const isCollapsed =
+      localStorage.getItem('default_collapse_sidebar') === 'true';
+    dispatch({ type: 'SET_SIDER_COLLAPSED', payload: isCollapsed });
+  }, [dispatch]);
 
 
   return (
   return (
     <StyleContext.Provider value={[state, dispatch]}>
     <StyleContext.Provider value={[state, dispatch]}>