Make filters and sorts work on record page pagination (#12460)

Fixes #7929 

This PR implements a system to capture and preserve the filters and
sorts when navigating from an index view to a record show page. This
information is stored in a context store component state.

This allows users to navigate between records inside the record page
while maintaining context from the index view.
This commit is contained in:
Raphaël Bosi
2025-06-11 18:01:03 +02:00
committed by GitHub
parent 23cbeec227
commit 27d0a3766f
18 changed files with 296 additions and 195 deletions

View File

@ -2,6 +2,8 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreRecordShowParentViewComponentState } from '@/context-store/states/contextStoreRecordShowParentViewComponentState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
@ -13,10 +15,10 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useCallback } from 'react';
import { useRecoilValue } from 'recoil';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { IconBrowserMaximize } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
@ -60,6 +62,11 @@ export const RecordShowRightDrawerOpenRecordButton = ({
tabListComponentIdInRecordPage,
);
const parentViewState = useRecoilComponentCallbackStateV2(
contextStoreRecordShowParentViewComponentState,
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const navigate = useNavigateApp();
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
@ -68,43 +75,54 @@ export const RecordShowRightDrawerOpenRecordButton = ({
const { closeDropdown } = useDropdownV2();
const handleOpenRecord = useCallback(() => {
const tabIdToOpen =
activeTabIdInRightDrawer === 'home'
? objectNameSingular === CoreObjectNameSingular.Note ||
objectNameSingular === CoreObjectNameSingular.Task
? 'richText'
: 'timeline'
: activeTabIdInRightDrawer;
const handleOpenRecord = useRecoilCallback(
({ snapshot, reset }) =>
() => {
const tabIdToOpen =
activeTabIdInRightDrawer === 'home'
? objectNameSingular === CoreObjectNameSingular.Note ||
objectNameSingular === CoreObjectNameSingular.Task
? 'richText'
: 'timeline'
: activeTabIdInRightDrawer;
setActiveTabIdInRecordPage(tabIdToOpen);
setActiveTabIdInRecordPage(tabIdToOpen);
navigate(AppPath.RecordShowPage, {
const parentView = snapshot.getLoadable(parentViewState).getValue();
if (parentView?.parentViewObjectNameSingular !== objectNameSingular) {
reset(parentViewState);
}
navigate(AppPath.RecordShowPage, {
objectNameSingular,
objectRecordId: recordId,
});
closeDropdown(
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId),
);
closeCommandMenu();
},
[
actionMenuId,
activeTabIdInRightDrawer,
closeCommandMenu,
closeDropdown,
navigate,
objectNameSingular,
objectRecordId: recordId,
});
closeDropdown(
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId),
);
closeCommandMenu();
}, [
actionMenuId,
activeTabIdInRightDrawer,
closeCommandMenu,
closeDropdown,
navigate,
objectNameSingular,
recordId,
setActiveTabIdInRecordPage,
]);
parentViewState,
recordId,
setActiveTabIdInRecordPage,
],
);
useScopedHotkeys(
['ctrl+Enter,meta+Enter'],
handleOpenRecord,
AppHotkeyScope.CommandMenuOpen,
[closeCommandMenu, navigate, objectNameSingular, recordId],
[handleOpenRecord],
);
if (!isDefined(record)) {