diff --git a/packages/twenty-front/src/hooks/__tests__/useIsMatchingLocation.test.tsx b/packages/twenty-front/src/hooks/__tests__/useIsMatchingLocation.test.tsx
deleted file mode 100644
index b775f8988..000000000
--- a/packages/twenty-front/src/hooks/__tests__/useIsMatchingLocation.test.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import { renderHook } from '@testing-library/react';
-import { useIsMatchingLocation } from '../useIsMatchingLocation';
-import { MemoryRouter } from 'react-router-dom';
-import { AppBasePath } from '@/types/AppBasePath';
-
-const Wrapper =
- (initialIndex = 0) =>
- ({ children }: { children: React.ReactNode }) => (
-
- {children}
-
- );
-
-describe('useIsMatchingLocation', () => {
- it('returns true when paths match with no basePath', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(),
- });
-
- expect(result.current.isMatchingLocation('/example')).toBe(true);
- });
-
- it('returns false when paths do not match with no basePath', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(),
- });
-
- expect(result.current.isMatchingLocation('/non-match')).toBe(false);
- });
-
- it('returns true when paths match with basePath', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(2),
- });
-
- expect(
- result.current.isMatchingLocation('example', AppBasePath.Settings),
- ).toBe(true);
- });
-
- it('returns false when paths do not match with basePath', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(),
- });
-
- expect(
- result.current.isMatchingLocation('non-match', AppBasePath.Settings),
- ).toBe(false);
- });
-
- it('handles trailing slashes in basePath correctly', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(2),
- });
-
- expect(
- result.current.isMatchingLocation(
- 'example',
- (AppBasePath.Settings + '/') as AppBasePath,
- ),
- ).toBe(true);
- });
-
- it('handles without basePath correctly', () => {
- const { result } = renderHook(() => useIsMatchingLocation(), {
- wrapper: Wrapper(),
- });
-
- expect(result.current.isMatchingLocation('example')).toBe(true);
- });
-});
diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
index 29c4973e9..32f0bd3c8 100644
--- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
+++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
@@ -8,9 +8,9 @@ import { useRecoilValue } from 'recoil';
import { OnboardingStatus } from '~/generated/graphql';
-import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
import { UNTESTED_APP_PATHS } from '~/testing/constants/UntestedAppPaths';
+import { isMatchingLocation } from '~/utils/isMatchingLocation';
jest.mock('@/onboarding/hooks/useOnboardingStatus');
const setupMockOnboardingStatus = (
@@ -28,13 +28,13 @@ const setupMockIsWorkspaceActivationStatusEqualsTo = (
.mockReturnValueOnce(isWorkspaceSuspended);
};
-jest.mock('~/hooks/useIsMatchingLocation');
-const mockUseIsMatchingLocation = jest.mocked(useIsMatchingLocation);
+jest.mock('~/utils/isMatchingLocation');
+const mockIsMatchingLocation = jest.mocked(isMatchingLocation);
const setupMockIsMatchingLocation = (pathname: string) => {
- mockUseIsMatchingLocation.mockReturnValueOnce({
- isMatchingLocation: (path: string) => path === pathname,
- });
+ mockIsMatchingLocation.mockImplementation(
+ (_location, path) => path === pathname,
+ );
};
jest.mock('@/auth/hooks/useIsLogged');
diff --git a/packages/twenty-front/src/hooks/useIsMatchingLocation.ts b/packages/twenty-front/src/hooks/useIsMatchingLocation.ts
deleted file mode 100644
index 70c175e3e..000000000
--- a/packages/twenty-front/src/hooks/useIsMatchingLocation.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { matchPath, useLocation } from 'react-router-dom';
-
-import { AppBasePath } from '@/types/AppBasePath';
-import { isNonEmptyString } from '@sniptt/guards';
-import { useCallback } from 'react';
-import { isDefined } from 'twenty-shared/utils';
-
-const addTrailingSlash = (path: string) =>
- path.endsWith('/') ? path : path + '/';
-
-const getConstructedPath = (path: string, basePath?: AppBasePath) => {
- if (!isNonEmptyString(basePath)) return path;
-
- return addTrailingSlash(basePath) + path;
-};
-
-export const useIsMatchingLocation = () => {
- const location = useLocation();
-
- const isMatchingLocation = useCallback(
- (path: string, basePath?: AppBasePath) => {
- const match = matchPath(
- getConstructedPath(path, basePath),
- location.pathname,
- );
- return isDefined(match);
- },
- [location.pathname],
- );
-
- return {
- isMatchingLocation,
- };
-};
diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
index ed3845b14..99c59c409 100644
--- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
+++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
@@ -5,24 +5,24 @@ import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath';
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
-import { useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { OnboardingStatus } from '~/generated/graphql';
-import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
+import { isMatchingLocation } from '~/utils/isMatchingLocation';
export const usePageChangeEffectNavigateLocation = () => {
- const { isMatchingLocation } = useIsMatchingLocation();
const isLoggedIn = useIsLogged();
const onboardingStatus = useOnboardingStatus();
const isWorkspaceSuspended = useIsWorkspaceActivationStatusEqualsTo(
WorkspaceActivationStatus.SUSPENDED,
);
const { defaultHomePagePath } = useDefaultHomePagePath();
+ const location = useLocation();
const someMatchingLocationOf = (appPaths: AppPath[]): boolean =>
- appPaths.some((appPath) => isMatchingLocation(appPath));
+ appPaths.some((appPath) => isMatchingLocation(location, appPath));
const onGoingUserCreationPaths = [
AppPath.Invite,
AppPath.SignInUp,
@@ -61,7 +61,10 @@ export const usePageChangeEffectNavigateLocation = () => {
return AppPath.PlanRequired;
}
- if (isWorkspaceSuspended && !isMatchingLocation(AppPath.SettingsCatchAll)) {
+ if (
+ isWorkspaceSuspended &&
+ !isMatchingLocation(location, AppPath.SettingsCatchAll)
+ ) {
return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
SettingsPath.Billing
}`;
@@ -79,21 +82,21 @@ export const usePageChangeEffectNavigateLocation = () => {
if (
onboardingStatus === OnboardingStatus.PROFILE_CREATION &&
- !isMatchingLocation(AppPath.CreateProfile)
+ !isMatchingLocation(location, AppPath.CreateProfile)
) {
return AppPath.CreateProfile;
}
if (
onboardingStatus === OnboardingStatus.SYNC_EMAIL &&
- !isMatchingLocation(AppPath.SyncEmails)
+ !isMatchingLocation(location, AppPath.SyncEmails)
) {
return AppPath.SyncEmails;
}
if (
onboardingStatus === OnboardingStatus.INVITE_TEAM &&
- !isMatchingLocation(AppPath.InviteTeam)
+ !isMatchingLocation(location, AppPath.InviteTeam)
) {
return AppPath.InviteTeam;
}
@@ -101,18 +104,18 @@ export const usePageChangeEffectNavigateLocation = () => {
if (
onboardingStatus === OnboardingStatus.COMPLETED &&
someMatchingLocationOf([...onboardingPaths, ...onGoingUserCreationPaths]) &&
- !isMatchingLocation(AppPath.ResetPassword) &&
+ !isMatchingLocation(location, AppPath.ResetPassword) &&
isLoggedIn
) {
return defaultHomePagePath;
}
- if (isMatchingLocation(AppPath.Index) && isLoggedIn) {
+ if (isMatchingLocation(location, AppPath.Index) && isLoggedIn) {
return defaultHomePagePath;
}
if (
- isMatchingLocation(AppPath.RecordIndexPage) &&
+ isMatchingLocation(location, AppPath.RecordIndexPage) &&
!isDefined(objectMetadataItem)
) {
return AppPath.NotFound;
diff --git a/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts b/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts
index 54fb9ce5f..0779de0a1 100644
--- a/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts
+++ b/packages/twenty-front/src/modules/apollo/hooks/useApolloFactory.ts
@@ -11,8 +11,8 @@ import { tokenPairState } from '@/auth/states/tokenPairState';
import { workspacesState } from '@/auth/states/workspaces';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
-import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
+import { isMatchingLocation } from '~/utils/isMatchingLocation';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { AppPath } from '@/types/AppPath';
@@ -25,7 +25,6 @@ export const useApolloFactory = (options: Partial> = {}) => {
const [isDebugMode] = useRecoilState(isDebugModeState);
const navigate = useNavigate();
- const { isMatchingLocation } = useIsMatchingLocation();
const setTokenPair = useSetRecoilState(tokenPairState);
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
currentWorkspaceState,
@@ -74,10 +73,10 @@ export const useApolloFactory = (options: Partial> = {}) => {
setCurrentUserWorkspace(null);
setWorkspaces([]);
if (
- !isMatchingLocation(AppPath.Verify) &&
- !isMatchingLocation(AppPath.SignInUp) &&
- !isMatchingLocation(AppPath.Invite) &&
- !isMatchingLocation(AppPath.ResetPassword)
+ !isMatchingLocation(location, AppPath.Verify) &&
+ !isMatchingLocation(location, AppPath.SignInUp) &&
+ !isMatchingLocation(location, AppPath.Invite) &&
+ !isMatchingLocation(location, AppPath.ResetPassword)
) {
setPreviousUrl(`${location.pathname}${location.search}`);
navigate(AppPath.SignInUp);
diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
index 0749116df..30d1850ba 100644
--- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
+++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
@@ -36,15 +36,14 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils';
import { AnalyticsType } from '~/generated/graphql';
-import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
import { useInitializeQueryParamState } from '~/modules/app/hooks/useInitializeQueryParamState';
+import { isMatchingLocation } from '~/utils/isMatchingLocation';
import { getPageTitleFromPath } from '~/utils/title-utils';
// TODO: break down into smaller functions and / or hooks
// - moved usePageChangeEffectNavigateLocation into dedicated hook
export const PageChangeEffect = () => {
const navigate = useNavigate();
- const { isMatchingLocation } = useIsMatchingLocation();
const [previousLocation, setPreviousLocation] = useState('');
@@ -126,7 +125,6 @@ export const PageChangeEffect = () => {
}
}
}, [
- isMatchingLocation,
previousLocation,
resetTableSelections,
unfocusRecordTableRow,
@@ -139,28 +137,28 @@ export const PageChangeEffect = () => {
useEffect(() => {
switch (true) {
- case isMatchingLocation(AppPath.RecordIndexPage): {
+ case isMatchingLocation(location, AppPath.RecordIndexPage): {
setHotkeyScope(RecordIndexHotkeyScope.RecordIndex, {
goto: true,
keyboardShortcutMenu: true,
});
break;
}
- case isMatchingLocation(AppPath.RecordShowPage): {
+ case isMatchingLocation(location, AppPath.RecordShowPage): {
setHotkeyScope(PageHotkeyScope.CompanyShowPage, {
goto: true,
keyboardShortcutMenu: true,
});
break;
}
- case isMatchingLocation(AppPath.OpportunitiesPage): {
+ case isMatchingLocation(location, AppPath.OpportunitiesPage): {
setHotkeyScope(PageHotkeyScope.OpportunitiesPage, {
goto: true,
keyboardShortcutMenu: true,
});
break;
}
- case isMatchingLocation(AppPath.TasksPage): {
+ case isMatchingLocation(location, AppPath.TasksPage): {
setHotkeyScope(PageHotkeyScope.TaskPage, {
goto: true,
keyboardShortcutMenu: true,
@@ -168,42 +166,50 @@ export const PageChangeEffect = () => {
break;
}
- case isMatchingLocation(AppPath.SignInUp): {
+ case isMatchingLocation(location, AppPath.SignInUp): {
setHotkeyScope(PageHotkeyScope.SignInUp);
break;
}
- case isMatchingLocation(AppPath.Invite): {
+ case isMatchingLocation(location, AppPath.Invite): {
setHotkeyScope(PageHotkeyScope.SignInUp);
break;
}
- case isMatchingLocation(AppPath.CreateProfile): {
+ case isMatchingLocation(location, AppPath.CreateProfile): {
setHotkeyScope(PageHotkeyScope.CreateProfile);
break;
}
- case isMatchingLocation(AppPath.CreateWorkspace): {
+ case isMatchingLocation(location, AppPath.CreateWorkspace): {
setHotkeyScope(PageHotkeyScope.CreateWorkspace);
break;
}
- case isMatchingLocation(AppPath.SyncEmails): {
+ case isMatchingLocation(location, AppPath.SyncEmails): {
setHotkeyScope(PageHotkeyScope.SyncEmail);
break;
}
- case isMatchingLocation(AppPath.InviteTeam): {
+ case isMatchingLocation(location, AppPath.InviteTeam): {
setHotkeyScope(PageHotkeyScope.InviteTeam);
break;
}
- case isMatchingLocation(AppPath.PlanRequired): {
+ case isMatchingLocation(location, AppPath.PlanRequired): {
setHotkeyScope(PageHotkeyScope.PlanRequired);
break;
}
- case isMatchingLocation(SettingsPath.ProfilePage, AppBasePath.Settings): {
+ case isMatchingLocation(
+ location,
+ SettingsPath.ProfilePage,
+ AppBasePath.Settings,
+ ): {
setHotkeyScope(PageHotkeyScope.ProfilePage, {
goto: true,
keyboardShortcutMenu: true,
});
break;
}
- case isMatchingLocation(SettingsPath.Domain, AppBasePath.Settings): {
+ case isMatchingLocation(
+ location,
+ SettingsPath.Domain,
+ AppBasePath.Settings,
+ ): {
setHotkeyScope(PageHotkeyScope.Settings, {
goto: false,
keyboardShortcutMenu: true,
@@ -211,6 +217,7 @@ export const PageChangeEffect = () => {
break;
}
case isMatchingLocation(
+ location,
SettingsPath.WorkspaceMembersPage,
AppBasePath.Settings,
): {
@@ -221,7 +228,7 @@ export const PageChangeEffect = () => {
break;
}
}
- }, [isMatchingLocation, setHotkeyScope]);
+ }, [location, setHotkeyScope]);
useEffect(() => {
setTimeout(() => {
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts
index 5314cfb24..f5b644309 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts
@@ -1,6 +1,6 @@
import { useCallback, useState } from 'react';
import { SubmitHandler, UseFormReturn } from 'react-hook-form';
-import { useParams, useSearchParams } from 'react-router-dom';
+import { useLocation, useParams, useSearchParams } from 'react-router-dom';
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { signInUpModeState } from '@/auth/states/signInUpModeState';
@@ -15,7 +15,7 @@ import { AppPath } from '@/types/AppPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useRecoilState } from 'recoil';
-import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
+import { isMatchingLocation } from '~/utils/isMatchingLocation';
import { useAuth } from '../../hooks/useAuth';
export const useSignInUp = (form: UseFormReturn