fix: unify settings layout prep when entering settings from outside (#19373)

## Summary

Clicking **Compose** in the emails tab without a connected account
redirects to the New Account settings page, but the settings nav drawer
was left in its previous (collapsed / "main") state — producing a
visibly half-broken transition.

### Root cause

The "enter settings" preparation (memorize previous URL + drawer state,
expand the desktop drawer, switch the mobile drawer to `'settings'`) was
duplicated **inline in three different places**:
- `NavigationDrawerOtherSection.handleSettingsClick`
- `MultiWorkspaceDropdownDefaultComponents` Settings link
- Implicitly expected (but missing) from every `useNavigateSettings`
caller

Every other entry point — `ComposeEmailButton`, `ComposeEmailCommand`,
`AIChatCreditsExhaustedMessage`, several workflow/role components — just
called `navigateSettings(...)` and skipped the prep entirely,
reproducing the bug.

### Fix

- Move the full prep into `useOpenSettingsMenu`, with a
`useIsSettingsPage()` short-circuit so internal navigation doesn't
clobber the memorized return target.
- `useNavigateSettings` delegates to `openSettingsMenu()` before
navigating — fixing every caller in one place.
- Collapse the duplicated inline logic in `NavigationDrawerOtherSection`
and `MultiWorkspaceDropdownDefaultComponents` to a single call.

Net **−9 lines**, single source of truth, no behavior change for the
existing happy paths.

## Test plan

- [x] \`nx typecheck twenty-front\` passes
- [x] \`oxlint\` + \`prettier\` clean on all 4 changed files
- [x] Existing \`useNavigateSettings\` tests pass (4/4)
- [ ] Manual: Compose button on a Person/Company/Opportunity emails tab
with no connected account → settings drawer renders fully expanded,
"Exit Settings" returns to the record
- [ ] Manual: "Settings" entry in the main nav drawer still works
(return path memorized)
- [ ] Manual: "Settings" entry in the multi-workspace dropdown still
works, and right-click → open in new tab still works (kept
\`UndecoratedLink\`)
- [ ] Manual: Navigating between settings pages does not overwrite the
memorized return URL

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Félix Malfait
2026-04-07 09:50:31 +02:00
committed by GitHub
parent 3f87d27d5d
commit 955aa9191f
4 changed files with 30 additions and 44 deletions
@@ -1,9 +1,11 @@
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
import { useNavigate } from 'react-router-dom';
import { type SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
export const useNavigateSettings = () => {
const navigate = useNavigate();
const { openSettingsMenu } = useOpenSettingsMenu();
return <T extends SettingsPath>(
to: T,
@@ -14,6 +16,8 @@ export const useNavigateSettings = () => {
state?: any;
},
) => {
openSettingsMenu();
const path = getSettingsPath(to, params, queryParams);
return navigate(path, options);
};
@@ -1,19 +1,11 @@
import { useLingui } from '@lingui/react/macro';
import { useLocation, useNavigate } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconHelpCircle, IconSettings } from 'twenty-ui/display';
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
@@ -22,22 +14,12 @@ import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
import { isNavigationSectionOpenFamilyState } from '@/ui/navigation/navigation-drawer/states/isNavigationSectionOpenFamilyState';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
export const NavigationDrawerOtherSection = () => {
const { t } = useLingui();
const location = useLocation();
const navigate = useNavigate();
const navigateSettings = useNavigateSettings();
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useAtomState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetAtomState(
navigationDrawerExpandedMemorizedState,
);
const setNavigationMemorizedUrl = useSetAtomState(
navigationMemorizedUrlState,
);
const { openSettingsMenu } = useOpenSettingsMenu();
const { toggleNavigationSection } = useNavigationSection('Other');
const isNavigationSectionOpen = useAtomFamilyStateValue(
@@ -46,11 +28,7 @@ export const NavigationDrawerOtherSection = () => {
);
const handleSettingsClick = () => {
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
openSettingsMenu();
navigate(getSettingsPath(SettingsPath.ProfilePage));
navigateSettings(SettingsPath.ProfilePage);
};
return (
@@ -1,17 +1,35 @@
import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useLocation } from 'react-router-dom';
export const useOpenSettingsMenu = () => {
const setIsNavigationDrawerExpanded = useSetAtomState(
isNavigationDrawerExpandedState,
const location = useLocation();
const isSettingsPage = useIsSettingsPage();
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useAtomState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetAtomState(
navigationDrawerExpandedMemorizedState,
);
const setNavigationMemorizedUrl = useSetAtomState(
navigationMemorizedUrlState,
);
const setCurrentMobileNavigationDrawer = useSetAtomState(
currentMobileNavigationDrawerState,
);
const openSettingsMenu = () => {
if (isSettingsPage) {
return;
}
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
setCurrentMobileNavigationDrawer('settings');
};
@@ -15,13 +15,10 @@ import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useColorScheme } from '@/ui/theme/hooks/useColorScheme';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { CombinedGraphQLErrors } from '@apollo/client/errors';
@@ -29,7 +26,6 @@ import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { isNonEmptyString } from '@sniptt/guards';
import { useLocation } from 'react-router-dom';
import { AppPath, SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import {
@@ -85,15 +81,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
multiWorkspaceDropdownState,
);
const location = useLocation();
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useAtomState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetAtomState(
navigationDrawerExpandedMemorizedState,
);
const setNavigationMemorizedUrl = useSetAtomState(
navigationMemorizedUrlState,
);
const { openSettingsMenu } = useOpenSettingsMenu();
const handleSupport = () => {
window.FrontChat?.('show');
@@ -239,9 +227,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
<UndecoratedLink
to={getSettingsPath(SettingsPath.ProfilePage)}
onClick={() => {
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
openSettingsMenu();
closeDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
}}
>