New useNavigateApp (#9729)

Todo : 
- replace all instances of useNavigate(
- remove getSettingsPagePath
- add eslint rule to enfore usage of useNavigateApp instead of
useNavigate
This commit is contained in:
Félix Malfait
2025-01-18 13:58:12 +01:00
committed by GitHub
parent 8572471973
commit 152902d1be
115 changed files with 975 additions and 679 deletions

View File

@ -12,7 +12,6 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -20,6 +19,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { ViewType } from '@/views/types/ViewType';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const ObjectOptionsDropdownHiddenFieldsContent = () => {
const {
@ -34,7 +34,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
objectNameSingular: objectMetadataItem.nameSingular,
});
const settingsUrl = getSettingsPagePath(SettingsPath.ObjectDetail, {
const settingsUrl = getSettingsPath(SettingsPath.ObjectDetail, {
objectNamePlural,
});

View File

@ -13,7 +13,6 @@ import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-gr
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -22,6 +21,7 @@ import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMe
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const {
@ -51,13 +51,10 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
viewType,
});
const viewGroupSettingsUrl = getSettingsPagePath(
SettingsPath.ObjectFieldEdit,
{
objectNamePlural,
fieldName: recordGroupFieldMetadata?.name ?? '',
},
);
const viewGroupSettingsUrl = getSettingsPath(SettingsPath.ObjectFieldEdit, {
objectNamePlural,
fieldName: recordGroupFieldMetadata?.name ?? '',
});
const location = useLocation();
const setNavigationMemorizedUrl = useSetRecoilState(

View File

@ -17,7 +17,6 @@ import { useSearchRecordGroupField } from '@/object-record/object-options-dropdo
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -29,6 +28,7 @@ import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
const { getIcon } = useIcons();
@ -68,7 +68,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
viewBarComponentId: recordIndexId,
});
const newSelectFieldSettingsUrl = getSettingsPagePath(
const newSelectFieldSettingsUrl = getSettingsPath(
SettingsPath.ObjectNewFieldConfigure,
{
objectNamePlural,

View File

@ -4,13 +4,15 @@ import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/use
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { RecordGroupAction } from '@/object-record/record-group/types/RecordGroupActions';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { SettingsPath } from '@/types/SettingsPath';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewType } from '@/views/types/ViewType';
import { useCallback, useContext, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { IconEyeOff, IconSettings, isDefined } from 'twenty-ui';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
type UseRecordGroupActionsParams = {
viewType: ViewType;
@ -19,7 +21,7 @@ type UseRecordGroupActionsParams = {
export const useRecordGroupActions = ({
viewType,
}: UseRecordGroupActionsParams) => {
const navigate = useNavigate();
const navigate = useNavigateSettings();
const location = useLocation();
const { objectNameSingular, recordIndexId } = useRecordIndexContextOrThrow();
@ -53,9 +55,10 @@ export const useRecordGroupActions = ({
throw new Error('recordGroupFieldMetadata is not a non-empty string');
}
const settingsPath = `/settings/objects/${objectMetadataItem.namePlural}/${recordGroupFieldMetadata.name}`;
navigate(settingsPath);
navigate(SettingsPath.ObjectFieldEdit, {
objectNamePlural: objectMetadataItem.namePlural,
fieldName: recordGroupFieldMetadata.name,
});
}, [
setNavigationMemorizedUrl,
location.pathname,

View File

@ -1,7 +1,8 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL';
import { AppPath } from '@/types/AppPath';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { getAppPath } from '~/utils/navigation/getAppPath';
export const useHandleIndexIdentifierClick = ({
objectMetadataItem,
@ -16,12 +17,16 @@ export const useHandleIndexIdentifierClick = ({
);
const indexIdentifierUrl = (recordId: string) => {
const showPageURL = buildShowPageURL(
objectMetadataItem.nameSingular,
recordId,
currentViewId,
return getAppPath(
AppPath.RecordShowPage,
{
objectNameSingular: objectMetadataItem.nameSingular,
objectRecordId: recordId,
},
{
viewId: currentViewId,
},
);
return showPageURL;
};
return { indexIdentifierUrl };

View File

@ -1,17 +1,17 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useParams, useSearchParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
import { useRecordIdsFromFindManyCacheRootQuery } from '@/object-record/record-show/hooks/useRecordIdsFromFindManyCacheRootQuery';
import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL';
import { buildIndexTablePageURL } from '@/object-record/record-table/utils/buildIndexTableURL';
import { AppPath } from '@/types/AppPath';
import { useQueryVariablesFromActiveFieldsOfViewOrDefaultView } from '@/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView';
import { capitalize } from 'twenty-shared';
import { isDefined } from 'twenty-ui';
import { useNavigateApp } from '~/hooks/useNavigateApp';
export const useRecordShowPagePagination = (
propsObjectNameSingular: string,
@ -22,9 +22,9 @@ export const useRecordShowPagePagination = (
objectRecordId: paramObjectRecordId,
} = useParams();
const navigate = useNavigate();
const navigate = useNavigateApp();
const [searchParams] = useSearchParams();
const viewIdQueryParam = searchParams.get('view');
const viewIdQueryParam = searchParams.get('viewId');
const setLastShowPageRecordId = useSetRecoilState(lastShowPageRecordIdState);
@ -130,22 +130,32 @@ export const useRecordShowPagePagination = (
!isFirstRecord || (isFirstRecord && cacheIsAvailableForNavigation);
const navigateToPreviousRecord = () => {
if (isFirstRecord) {
if (isFirstRecord || !recordBefore) {
if (cacheIsAvailableForNavigation) {
const lastRecordIdFromCache =
recordIdsInCache[recordIdsInCache.length - 1];
navigate(
buildShowPageURL(
AppPath.RecordShowPage,
{
objectNameSingular,
lastRecordIdFromCache,
viewIdQueryParam,
),
objectRecordId: lastRecordIdFromCache,
},
{
viewId: viewIdQueryParam,
},
);
}
} else {
navigate(
buildShowPageURL(objectNameSingular, recordBefore.id, viewIdQueryParam),
AppPath.RecordShowPage,
{
objectNameSingular,
objectRecordId: recordBefore.id,
},
{
viewId: viewIdQueryParam,
},
);
}
};
@ -154,34 +164,47 @@ export const useRecordShowPagePagination = (
!isLastRecord || (isLastRecord && cacheIsAvailableForNavigation);
const navigateToNextRecord = () => {
if (isLastRecord) {
if (isLastRecord || !recordAfter) {
if (cacheIsAvailableForNavigation) {
const firstRecordIdFromCache = recordIdsInCache[0];
navigate(
buildShowPageURL(
AppPath.RecordShowPage,
{
objectNameSingular,
firstRecordIdFromCache,
viewIdQueryParam,
),
objectRecordId: firstRecordIdFromCache,
},
{
viewId: viewIdQueryParam,
},
);
}
} else {
navigate(
buildShowPageURL(objectNameSingular, recordAfter.id, viewIdQueryParam),
AppPath.RecordShowPage,
{
objectNameSingular,
objectRecordId: recordAfter.id,
},
{
viewId: viewIdQueryParam,
},
);
}
};
const navigateToIndexView = () => {
const indexTableURL = buildIndexTablePageURL(
objectMetadataItem.namePlural,
viewIdQueryParam,
);
setLastShowPageRecordId(objectRecordId);
navigate(indexTableURL);
navigate(
AppPath.RecordIndexPage,
{
objectNamePlural: objectMetadataItem.namePlural,
},
{
viewId: viewIdQueryParam,
},
);
};
const rankInView = recordIdsInCache.findIndex((id) => id === objectRecordId);

View File

@ -1,5 +1,4 @@
import styled from '@emotion/styled';
import qs from 'qs';
import { useCallback, useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { IconForbid, IconPencil, IconPlus, LightIconButton } from 'twenty-ui';
@ -26,6 +25,7 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { AppPath } from '@/types/AppPath';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
@ -33,6 +33,7 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { View } from '@/views/types/View';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { RelationDefinitionType } from '~/generated-metadata/graphql';
import { getAppPath } from '~/utils/navigation/getAppPath';
type RecordDetailRelationSectionProps = {
loading: boolean;
};
@ -139,9 +140,13 @@ export const RecordDetailRelationSection = ({
view: indexView?.id,
};
const filterLinkHref = `/objects/${
relationObjectMetadataItem.namePlural
}?${qs.stringify(filterQueryParams)}`;
const filterLinkHref = getAppPath(
AppPath.RecordIndexPage,
{
objectNamePlural: relationObjectMetadataItem.namePlural,
},
filterQueryParams,
);
const showContent = () => {
return (

View File

@ -1,9 +0,0 @@
export const buildShowPageURL = (
objectNameSingular: string,
recordId: string,
viewId?: string | null | undefined,
) => {
return `/object/${objectNameSingular}/${recordId}${
viewId ? `?view=${viewId}` : ''
}`;
};

View File

@ -2,13 +2,14 @@
import { IconSettings } from 'twenty-ui';
import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay';
import { useNavigate } from 'react-router-dom';
import { SettingsPath } from '@/types/SettingsPath';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
export const RecordTableEmptyStateRemote = () => {
const navigate = useNavigate();
const navigate = useNavigateSettings();
const handleButtonClick = () => {
navigate('/settings/integrations');
navigate(SettingsPath.Integrations);
};
return (

View File

@ -8,11 +8,13 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte
import { useTableColumns } from '@/object-record/record-table/hooks/useTableColumns';
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { SettingsPath } from '@/types/SettingsPath';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const RecordTableHeaderPlusButtonContent = () => {
const { objectMetadataItem } = useRecordTableContextOrThrow();
@ -55,7 +57,9 @@ export const RecordTableHeaderPlusButtonContent = () => {
<DropdownMenuItemsContainer scrollable={false}>
<UndecoratedLink
fullWidth
to={`/settings/objects/${objectMetadataItem.namePlural}`}
to={getSettingsPath(SettingsPath.Objects, {
objectNamePlural: objectMetadataItem.namePlural,
})}
onClick={() => {
setNavigationMemorizedUrl(location.pathname + location.search);
}}

View File

@ -1,6 +0,0 @@
export const buildIndexTablePageURL = (
objectNamePlural: string,
viewId?: string | null | undefined,
) => {
return `/objects/${objectNamePlural}${viewId ? `?view=${viewId}` : ''}`;
};