From 1a13258302884da960beb813f4ecb3ff9d147e83 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 19 Feb 2026 12:10:02 +0100 Subject: [PATCH] Keep migrating to jotai (#18064) And we continue! --- .../actions/components/ActionModal.tsx | 4 +- .../display/components/ActionDropdownItem.tsx | 8 +- .../RecordIndexActionMenuDropdown.tsx | 3 +- .../RecordShowRightDrawerOpenRecordButton.tsx | 8 +- .../tasks/components/TaskGroups.tsx | 4 +- .../ai/components/ThinkingStepsDisplay.tsx | 4 +- .../ai/hooks/useAgentChatScrollToBottom.ts | 4 +- .../modules/ai/hooks/useBrowsingContext.ts | 17 +- .../GotoHotkeysEffectsProvider.tsx | 8 +- .../components/CustomSlashMenuListItem.tsx | 8 +- .../client-config/hooks/useClientConfig.ts | 7 +- .../CommandMenuDefaultSelectionEffect.tsx | 4 +- .../components/CommandMenuItem.tsx | 8 +- .../components/CommandMenuItemNumberInput.tsx | 4 +- .../components/CommandMenuItemTextInput.tsx | 4 +- .../components/CommandMenuItemToggle.tsx | 8 +- .../components/CommandMenuOpenContainer.tsx | 29 +- .../command-menu/hooks/useCommandMenu.ts | 10 +- ...ommandMenuCloseAnimationCompleteCleanup.ts | 12 +- .../hooks/useCommandMenuHistory.ts | 101 +++-- .../hooks/useNavigateCommandMenu.ts | 7 +- .../hooks/useRichTextCommandMenu.ts | 8 +- .../components/WidgetSettingsFooter.tsx | 3 +- ...rtAggregateOperationSelectableListItem.tsx | 4 +- .../ChartAxisNameSelectionDropdownContent.tsx | 4 +- .../ChartColorSelectionDropdownContent.tsx | 4 +- .../ChartDataSourceDropdownContent.tsx | 4 +- ...ateGranularitySelectionDropdownContent.tsx | 4 +- ...onForAggregateOperationDropdownContent.tsx | 4 +- ...roupByFieldSelectionCompositeFieldView.tsx | 4 +- ...oupByFieldSelectionDropdownContentBase.tsx | 4 +- ...GroupByFieldSelectionRelationFieldView.tsx | 4 +- ...ioAggregateOperationSelectableListItem.tsx | 4 +- ...rtRatioOptionBooleanSelectableListItem.tsx | 4 +- ...artRatioOptionSelectSelectableListItem.tsx | 4 +- ...ChartSortByGroupByFieldDropdownContent.tsx | 19 +- .../ChartSortBySelectionDropdownContent.tsx | 4 +- .../hooks/useUpdateGraphTypeConfig.ts | 17 +- .../CommandMenuWorkflowRunViewStepContent.tsx | 3 +- .../CurrentWorkspaceMemberFavorites.tsx | 3 +- .../hooks/useKeyboardShortcutMenu.ts | 18 +- ...rentWorkspaceMemberNavigationMenuItems.tsx | 3 +- .../WorkspaceNavigationMenuItems.tsx | 9 +- .../hooks/useHandleAddToNavigationDrop.ts | 8 +- .../hooks/useSaveNavigationMenuItemsDraft.ts | 7 +- .../components/PageDragDropProvider.tsx | 7 +- ...FilterRecordFilterOperandSelectContent.tsx | 4 +- .../AdvancedFilterSubFieldSelectMenu.tsx | 3 +- ...jectFilterDropdownFilterSelectMenuItem.tsx | 8 +- .../ObjectFilterDropdownOptionSelect.tsx | 3 +- ...jectOptionsDropdownCalendarViewContent.tsx | 4 +- .../ObjectOptionsDropdownCustomView.tsx | 3 +- .../ObjectOptionsDropdownDefaultView.tsx | 3 +- .../ObjectOptionsDropdownLayoutContent.tsx | 3 +- ...jectOptionsDropdownLayoutOpenInContent.tsx | 4 +- ...tOptionsDropdownRecordGroupSortContent.tsx | 3 +- ...jectOptionsDropdownRecordGroupsContent.tsx | 3 +- ...ObjectOptionsDropdownVisibilityContent.tsx | 4 +- .../hooks/useUpdateObjectViewOptions.ts | 8 +- .../components/ObjectSortDropdownButton.tsx | 5 +- .../components/RecordBoardContainer.tsx | 3 +- .../RecordBoardStickyHeaderEffect.tsx | 4 +- .../RecordBoardCardInputContextProvider.tsx | 33 +- ...RecordCalendarCardInputContextProvider.tsx | 33 +- .../RecordFieldListInputContextProvider.tsx | 33 +- .../input/components/MultiItemFieldInput.tsx | 4 +- .../input/hooks/useOpenFilesFieldInput.tsx | 14 +- .../components/RecordIndexTableContainer.tsx | 4 +- .../hooks/useLoadRecordIndexStates.ts | 7 +- .../components/RecordInlineCell.tsx | 8 +- .../components/MergeRecordsContainer.tsx | 4 +- .../MultipleRecordPickerMenuItemContent.tsx | 7 +- .../components/SingleRecordPickerMenuItem.tsx | 8 +- .../SingleRecordPickerMenuItems.tsx | 8 +- .../components/RecordShowEffect.tsx | 11 +- .../hooks/useUpsertRecordsInStore.ts | 10 +- .../hooks/internal/useSetRecordTableData.ts | 6 +- ...RecordTableBodyFocusClickOutsideEffect.tsx | 4 +- .../components/RecordTableCellFieldInput.tsx | 35 +- .../useCloseRecordTableCellInGroup.ts | 42 +- .../useCloseRecordTableCellNoGroup.ts | 41 +- .../hooks/useOpenRecordTableCell.ts | 18 +- .../components/MultipleSelectDropdown.tsx | 4 +- .../components/PageLayoutGridOverlay.tsx | 3 +- .../components/PageLayoutRendererContent.tsx | 3 +- .../components/PageLayoutTabList.tsx | 4 +- .../components/PageLayoutTabListEffect.tsx | 4 +- .../hooks/__tests__/PageLayoutTestWrapper.tsx | 31 +- .../useCreatePageLayoutGraphWidget.test.tsx | 6 +- .../__tests__/useCreatePageLayoutTab.test.tsx | 7 +- .../usePageLayoutHandleLayoutChange.test.tsx | 53 +-- ...useCreatePageLayoutFrontComponentWidget.ts | 4 +- .../hooks/useCreatePageLayoutGraphWidget.ts | 16 +- .../hooks/useCreatePageLayoutIframeWidget.ts | 4 +- ...reatePageLayoutStandaloneRichTextWidget.ts | 16 +- .../hooks/useCreatePageLayoutTab.ts | 4 +- .../hooks/useDeletePageLayoutTab.ts | 21 +- .../hooks/useDuplicatePageLayoutTab.ts | 3 +- .../hooks/usePageLayoutHandleLayoutChange.ts | 20 +- ...setDraftPageLayoutToPersistedPageLayout.ts | 18 +- .../components/FieldWidgetInlineCell.tsx | 8 +- .../FieldWidgetInputContextProvider.tsx | 35 +- .../__stories__/FieldWidget.stories.tsx | 85 +++- .../field/hooks/useIsFieldWidgetEditing.ts | 5 +- ...tingsAccountsCalendarChannelsContainer.tsx | 4 +- ...ttingsAccountsMessageChannelsContainer.tsx | 4 +- .../components/SettingsAdminGeneral.tsx | 4 +- .../components/SettingsAdminTabContent.tsx | 4 +- .../SettingsMorphRelationMultiSelect.tsx | 4 +- .../components/WebhookEntitySelect.tsx | 4 +- .../SettingsLogicFunctionCodeEditorTab.tsx | 4 +- .../components/SettingsRolesQueryEffect.tsx | 8 +- .../components/SettingsRoleAssignment.tsx | 4 +- ...sObjectLevelObjectFieldPermissionTable.tsx | 8 +- ...LevelPermissionFieldSelectSubFieldMenu.tsx | 3 +- .../roles/role/components/SettingsRole.tsx | 4 +- .../components/SettingsRoleCreateEffect.tsx | 4 +- .../components/SettingsRoleEditEffect.tsx | 4 +- .../__stories__/SelectHeader.stories.tsx | 31 +- .../__stories__/SelectSheet.stories.tsx | 31 +- .../components/__stories__/Upload.stories.tsx | 31 +- .../__stories__/Validation.stories.tsx | 31 +- .../components/ListenRecordUpdatesEffect.tsx | 8 +- .../ui/field/input/components/DateInput.tsx | 28 +- .../field/input/components/DateTimeInput.tsx | 28 +- .../input/components/MultiSelectInput.tsx | 4 +- .../ui/input/components/IconPicker.tsx | 6 +- .../modules/ui/input/components/Select.tsx | 4 +- .../ui/input/components/SelectInput.tsx | 4 +- .../dropdown/hooks/useCloseAnyOpenDropdown.ts | 16 +- .../layout/dropdown/hooks/useCloseDropdown.ts | 9 +- .../useGoBackToPreviousDropdownFocusId.ts | 15 +- .../layout/dropdown/hooks/useOpenDropdown.ts | 7 +- ...SetFocusedDropdownIdAndMemorizePrevious.ts | 14 +- .../dropdown/hooks/useToggleDropdown.ts | 13 +- .../ui/layout/modal/components/Modal.tsx | 4 +- .../__stories__/ConfirmationModal.stories.tsx | 19 +- .../components/__stories__/Modal.stories.tsx | 19 +- .../{useModal.spec.ts => useModal.spec.tsx} | 50 +- .../ui/layout/modal/hooks/useModal.tsx | 123 +++-- .../states/isModalOpenedComponentState.ts | 4 +- .../components/SelectableList.tsx | 4 +- .../components/SelectableListItem.tsx | 8 +- .../hooks/__tests__/useSelectableList.test.ts | 18 +- .../internal/useSelectableListHotKeys.ts | 205 +++++---- .../hooks/useSelectableList.ts | 116 +++-- ...SelectableListListenToEnterHotkeyOnItem.ts | 35 +- .../isSelectedItemIdComponentFamilyState.ts | 16 +- .../states/selectableItemIdsComponentState.ts | 16 +- .../states/selectedItemIdComponentState.ts | 16 +- ...isSelectedItemIdComponentFamilySelector.ts | 28 -- .../ui/layout/tab-list/components/TabList.tsx | 4 +- .../TabListFromUrlOptionalEffect.tsx | 10 +- .../states/activeTabIdComponentState.ts | 4 +- .../table/components/SortableTableHeader.tsx | 12 +- .../hooks/__tests__/useSortedArray.test.tsx | 75 +-- .../ui/layout/table/hooks/useSortedArray.ts | 7 +- .../states/sortedFieldByTableFamilyState.ts | 4 +- .../drag-select/hooks/useDragSelect.ts | 8 +- .../usePushFocusItemToFocusStack.test.tsx | 19 +- ...RemoveFocusItemFromFocusStackById.test.tsx | 21 +- .../__tests__/useResetFocusStack.test.tsx | 21 +- .../useResetFocusStackToFocusItem.test.tsx | 21 +- .../hooks/usePushFocusItemToFocusStack.ts | 89 ++-- ...eFocusItemFromFocusStackByComponentType.ts | 67 +-- .../useRemoveFocusItemFromFocusStackById.ts | 47 +- .../focus/hooks/useResetFocusStack.ts | 22 +- .../hooks/useResetFocusStackToFocusItem.ts | 27 +- .../focus/states/currentFocusIdSelector.ts | 4 +- .../states/currentFocusedItemSelector.ts | 6 +- .../currentGlobalHotkeysConfigSelector.ts | 9 +- .../utilities/focus/states/focusStackState.ts | 4 +- .../hotkey/hooks/useGlobalHotkeys.ts | 27 +- .../hotkey/hooks/useGlobalHotkeysCallback.ts | 125 ++--- .../hotkey/hooks/useGlobalHotkeysSequence.ts | 5 +- .../hooks/useHotkeysOnFocusedElement.ts | 5 +- .../useHotkeysOnFocusedElementCallback.ts | 97 ++-- .../states/internal/pendingHotkeysState.ts | 4 +- .../states/currentPageLocationState.ts | 4 +- .../useClickOutsideListener.test.tsx | 20 +- .../hooks/useClickOutsideListener.ts | 45 +- .../hooks/useListenClickOutside.ts | 204 ++++----- ...utsideListenerIsActivatedComponentState.ts | 4 +- ...ListenerIsMouseDownInsideComponentState.ts | 4 +- ...ListenerMouseDownHappenedComponentState.ts | 4 +- .../scroll/components/ScrollWrapper.tsx | 8 +- .../scroll/hooks/useScrollRestoration.ts | 4 +- ...scrollWrapperScrollBottomComponentState.ts | 4 +- .../scrollWrapperScrollLeftComponentState.ts | 4 +- .../scrollWrapperScrollTopComponentState.ts | 4 +- .../utilities/state/jotai/MIGRATION_PLAN.md | 427 ++++++++++++++++++ .../hooks/useRecoilComponentFamilyStateV2.ts | 31 ++ .../hooks/useRecoilComponentFamilyValueV2.ts | 28 ++ .../useSetRecoilComponentFamilyStateV2.ts | 28 ++ .../jotai/types/ComponentFamilyStateV2.ts | 21 + .../utils/createComponentFamilyStateV2.ts | 57 +++ .../components/MetadataProviderEffect.tsx | 8 +- ...wBarFilterDropdownAdvancedFilterButton.tsx | 8 +- ...erDropdownAnyFieldSearchButtonMenuItem.tsx | 8 +- ...ewBarFilterDropdownFieldSelectMenuItem.tsx | 8 +- .../useSetInitialWorkflowRunRightDrawerTab.ts | 32 +- .../components/WorkflowStepFooter.tsx | 4 +- .../components/WorkflowEditActionAiAgent.tsx | 4 +- .../components/WorkflowEditActionCode.tsx | 3 +- .../WorkflowEditActionHttpRequest.tsx | 4 +- .../hooks/useVariableDropdown.ts | 3 +- .../src/pages/settings/ai/SettingsAI.tsx | 4 +- .../pages/settings/ai/SettingsAgentForm.tsx | 4 +- .../ai/components/SettingsAgentEvalsTab.tsx | 4 +- .../SettingsApplicationDetails.tsx | 4 +- .../applications/SettingsApplications.tsx | 4 +- .../SettingsAvailableApplicationDetails.tsx | 4 +- .../data-model/SettingsObjectDetailPage.tsx | 4 +- .../SettingsLogicFunctionDetail.tsx | 4 +- .../members/SettingsWorkspaceMember.tsx | 4 +- .../appearance/components/LocalePicker.tsx | 5 +- 216 files changed, 2366 insertions(+), 1594 deletions(-) rename packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/{useModal.spec.ts => useModal.spec.tsx} (64%) delete mode 100644 packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useSetRecoilComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/types/ComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/jotai/utils/createComponentFamilyStateV2.ts diff --git a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx index 397e71070e8..13d0cb9298c 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx @@ -8,7 +8,7 @@ import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { type ButtonAccent } from 'twenty-ui/input'; export type ActionModalProps = { @@ -49,7 +49,7 @@ export const ActionModal = ({ const modalId = `${actionConfig?.key}-action-modal-${actionMenuType}`; - const isModalOpened = useRecoilComponentValue( + const isModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, modalId, ); diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx index fa113424cb8..805c4464d85 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDropdownItem.tsx @@ -2,9 +2,9 @@ import { type ActionDisplayProps } from '@/action-menu/actions/display/component import { getActionLabel } from '@/action-menu/utils/getActionLabel'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; import { MenuItem } from 'twenty-ui/navigation'; @@ -31,8 +31,8 @@ export const ActionDropdownItem = ({ SelectableListComponentInstanceContext, ); - const isSelected = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelected = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, action.key, selectableListInstanceId, ); diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 34392eedb86..511ec5dc6ba 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -16,6 +16,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useContext } from 'react'; @@ -61,7 +62,7 @@ export const RecordIndexActionMenuDropdown = () => { 'more-actions', ]; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx index 45a6cf2a786..ca7e2915b3b 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx @@ -15,8 +15,8 @@ import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotke import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { t } from '@lingui/core/macro'; import { useRecoilCallback, useRecoilValue } from 'recoil'; import { AppPath } from 'twenty-shared/types'; @@ -49,7 +49,7 @@ export const RecordShowRightDrawerOpenRecordButton = ({ targetObjectId: recordId, }); - const activeTabIdInRightDrawer = useRecoilComponentValue( + const activeTabIdInRightDrawer = useRecoilComponentValueV2( activeTabIdComponentState, tabListComponentId, ); @@ -58,7 +58,7 @@ export const RecordShowRightDrawerOpenRecordButton = ({ targetObjectId: recordId, }); - const setActiveTabIdInRecordPage = useSetRecoilComponentState( + const setActiveTabIdInRecordPage = useSetRecoilComponentStateV2( activeTabIdComponentState, tabListComponentIdInRecordPage, ); diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx index d2aac6d50a2..94e39f41ce3 100644 --- a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx @@ -9,7 +9,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import groupBy from 'lodash.groupby'; import { IconPlus } from 'twenty-ui/display'; @@ -55,7 +55,7 @@ export const TaskGroups = ({ targetableObject }: TaskGroupsProps) => { activityObjectNameSingular: CoreObjectNameSingular.Task, }); - const activeTabId = useRecoilComponentValue(activeTabIdComponentState); + const activeTabId = useRecoilComponentValueV2(activeTabIdComponentState); const isLoading = (activeTabId !== 'done' && tasksLoading) || diff --git a/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx b/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx index 9d7fa0a05e5..54ad716bb06 100644 --- a/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx +++ b/packages/twenty-front/src/modules/ai/components/ThinkingStepsDisplay.tsx @@ -26,7 +26,7 @@ import { isThinkingStepPartActive } from '@/ai/utils/isThinkingStepPartActive'; import { type ThinkingStepPart } from '@/ai/utils/thinkingStepPart'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; const StyledContainer = styled.div` @@ -277,7 +277,7 @@ const ThinkingToolStepRow = ({ ? unwrappedResult.data.result : unwrappedOutput; const toolTabListComponentInstanceId = `ai-thinking-tool-tabs-${part.toolCallId ?? rawToolName}-${rowIndex}`; - const activeToolTabId = useRecoilComponentValue( + const activeToolTabId = useRecoilComponentValueV2( activeTabIdComponentState, toolTabListComponentInstanceId, ); diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChatScrollToBottom.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChatScrollToBottom.ts index b8bf2b5ee1d..7acf0f9bdc8 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAgentChatScrollToBottom.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChatScrollToBottom.ts @@ -1,7 +1,7 @@ import { AI_CHAT_SCROLL_WRAPPER_ID } from '@/ai/constants/AiChatScrollWrapperId'; import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement'; import { scrollWrapperScrollBottomComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; const SCROLL_BOTTOM_THRESHOLD_PX = 10; @@ -11,7 +11,7 @@ export const useAgentChatScrollToBottom = () => { AI_CHAT_SCROLL_WRAPPER_ID, ); - const scrollBottom = useRecoilComponentValue( + const scrollBottom = useRecoilComponentValueV2( scrollWrapperScrollBottomComponentState, AI_CHAT_SCROLL_WRAPPER_ID, ); diff --git a/packages/twenty-front/src/modules/ai/hooks/useBrowsingContext.ts b/packages/twenty-front/src/modules/ai/hooks/useBrowsingContext.ts index 4ef9052fc6c..3f51e3f9e87 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useBrowsingContext.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useBrowsingContext.ts @@ -14,9 +14,12 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useStore } from 'jotai'; import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector'; export const useGetBrowsingContext = () => { + const store = useStore(); + const getBrowsingContext = useRecoilCallback( ({ snapshot }) => (): BrowsingContext | null => { @@ -84,13 +87,11 @@ export const useGetBrowsingContext = () => { if (isDefined(pageLayoutId)) { const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const activeTabId = snapshot - .getLoadable( - activeTabIdComponentState.atomFamily({ - instanceId: tabListInstanceId, - }), - ) - .getValue(); + const activeTabId = store.get( + activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }), + ); return { ...recordContext, @@ -154,7 +155,7 @@ export const useGetBrowsingContext = () => { return null; }, - [], + [store], ); return { getBrowsingContext }; diff --git a/packages/twenty-front/src/modules/app/effect-components/GotoHotkeysEffectsProvider.tsx b/packages/twenty-front/src/modules/app/effect-components/GotoHotkeysEffectsProvider.tsx index 48eaf543e47..d30cb5fb769 100644 --- a/packages/twenty-front/src/modules/app/effect-components/GotoHotkeysEffectsProvider.tsx +++ b/packages/twenty-front/src/modules/app/effect-components/GotoHotkeysEffectsProvider.tsx @@ -3,7 +3,7 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilte import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded'; import { navigationDrawerExpandedMemorizedStateV2 } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedStateV2'; import { useGoToHotkeys } from '@/ui/utilities/hotkey/hooks/useGoToHotkeys'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { AppPath, SettingsPath } from 'twenty-shared/types'; import { getAppPath, getSettingsPath } from 'twenty-shared/utils'; @@ -12,6 +12,8 @@ export const GotoHotkeysEffectsProvider = () => { const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); + const store = useStore(); + useGoToHotkeys({ key: 's', location: getSettingsPath(SettingsPath.ProfilePage), @@ -19,9 +21,9 @@ export const GotoHotkeysEffectsProvider = () => { ({ set }) => () => { set(isNavigationDrawerExpandedState, true); - jotaiStore.set(navigationDrawerExpandedMemorizedStateV2.atom, true); + store.set(navigationDrawerExpandedMemorizedStateV2.atom, true); }, - [], + [store], ), }); diff --git a/packages/twenty-front/src/modules/blocknote-editor/components/CustomSlashMenuListItem.tsx b/packages/twenty-front/src/modules/blocknote-editor/components/CustomSlashMenuListItem.tsx index 3e88f2f5428..4ef46e10ec5 100644 --- a/packages/twenty-front/src/modules/blocknote-editor/components/CustomSlashMenuListItem.tsx +++ b/packages/twenty-front/src/modules/blocknote-editor/components/CustomSlashMenuListItem.tsx @@ -2,8 +2,8 @@ import { SLASH_MENU_LIST_ID } from '@/ui/input/constants/SlashMenuListId'; import { type SuggestionItem } from '@/blocknote-editor/types/types'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { MenuItemSuggestion } from 'twenty-ui/navigation'; export type CustomSlashMenuListItemProps = { @@ -15,8 +15,8 @@ export const CustomSlashMenuListItem = ({ }: CustomSlashMenuListItemProps) => { const { resetSelectedItem } = useSelectableList(SLASH_MENU_LIST_ID); - const isSelectedItem = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItem = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, item.title, ); diff --git a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts index 77f99710c7b..20143359478 100644 --- a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts @@ -26,7 +26,7 @@ import { sentryConfigState } from '@/client-config/states/sentryConfigState'; import { supportChatState } from '@/client-config/states/supportChatState'; import { type ClientConfig } from '@/client-config/types/ClientConfig'; import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { isAttachmentPreviewEnabledStateV2 } from '@/client-config/states/isAttachmentPreviewEnabledStateV2'; @@ -130,6 +130,8 @@ export const useClientConfig = (): UseClientConfigResult => { const setAppVersion = useSetRecoilStateV2(appVersionState); + const store = useStore(); + const fetchClientConfig = useCallback(async () => { setClientConfigApiStatus((prev) => ({ ...prev, @@ -191,7 +193,7 @@ export const useClientConfig = (): UseClientConfigResult => { setGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled); setGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled); setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled); - jotaiStore.set( + store.set( isAttachmentPreviewEnabledStateV2.atom, clientConfig?.isAttachmentPreviewEnabled, ); @@ -252,6 +254,7 @@ export const useClientConfig = (): UseClientConfigResult => { setSentryConfig, setSupportChat, setAllowRequestsToTwentyIcons, + store, ]); return { diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx index 282fca12b34..c62755077d9 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx @@ -2,7 +2,7 @@ import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/C import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useEffect } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -16,7 +16,7 @@ export const CommandMenuDefaultSelectionEffect = ({ COMMAND_MENU_LIST_SELECTABLE_LIST_ID, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, COMMAND_MENU_LIST_SELECTABLE_LIST_ID, ); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx index eeac62e970c..9281fb61f37 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx @@ -4,8 +4,8 @@ import { IconArrowUpRight, type IconComponent } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; export type CommandMenuItemProps = { label: string; @@ -44,8 +44,8 @@ export const CommandMenuItem = ({ Icon = IconArrowUpRight; } - const isSelectedItemId = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItemId = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, id, ); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemNumberInput.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemNumberInput.tsx index 1bca45a49d0..e4e24761960 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemNumberInput.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemNumberInput.tsx @@ -4,10 +4,10 @@ import { TextInput } from '@/ui/input/components/TextInput'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import styled from '@emotion/styled'; import { useRef, useState } from 'react'; -import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { type IconComponent } from 'twenty-ui/display'; import { @@ -45,7 +45,7 @@ export const CommandMenuItemNumberInput = ({ const [draftValue, setDraftValue] = useState(value); const [hasError, setHasError] = useState(false); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); const isNumberInputCurrentlyFocused = currentFocusId === focusId; const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemTextInput.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemTextInput.tsx index 0c4fd7e5c0f..33338ce33ef 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemTextInput.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemTextInput.tsx @@ -4,10 +4,10 @@ import { TextInput } from '@/ui/input/components/TextInput'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import styled from '@emotion/styled'; import { useRef, useState } from 'react'; -import { useRecoilValue } from 'recoil'; import { type IconComponent } from 'twenty-ui/display'; type CommandMenuItemTextInputProps = { @@ -37,7 +37,7 @@ export const CommandMenuItemTextInput = ({ const focusId = `${id}-input`; const [draftValue, setDraftValue] = useState(value); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); const isTextInputCurrentlyFocused = currentFocusId === focusId; const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemToggle.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemToggle.tsx index 20d810af705..99821f08435 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemToggle.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemToggle.tsx @@ -1,5 +1,5 @@ -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { MenuItemToggle, type MenuItemToggleProps } from 'twenty-ui/navigation'; export type CommandMenuItemToggleProps = MenuItemToggleProps & { @@ -7,8 +7,8 @@ export type CommandMenuItemToggleProps = MenuItemToggleProps & { }; export const CommandMenuItemToggle = (props: CommandMenuItemToggleProps) => { - const isSelectedItemId = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItemId = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, props.id, ); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx index 9395c49d96f..df9953a6005 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx @@ -11,6 +11,7 @@ import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingC import { PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { useStore } from 'jotai'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; import { WORKFLOW_DIAGRAM_CREATE_STEP_NODE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramCreateStepNodeClickOutsideId'; import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramStepNodeClickOutsideId'; @@ -19,8 +20,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { motion } from 'framer-motion'; -import { useRef } from 'react'; -import { useRecoilCallback } from 'recoil'; +import { useCallback, useRef } from 'react'; import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components'; import { useIsMobile } from 'twenty-ui/utilities'; @@ -58,20 +58,19 @@ export const CommandMenuOpenContainer = ({ isSidePanelAnimatingStateV2, ); - const handleClickOutside = useRecoilCallback( - ({ snapshot }) => - (event: MouseEvent | TouchEvent) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const store = useStore(); - if (currentFocusId === SIDE_PANEL_FOCUS_ID) { - event.stopImmediatePropagation(); - event.preventDefault(); - closeCommandMenu(); - } - }, - [closeCommandMenu], + const handleClickOutside = useCallback( + (event: MouseEvent | TouchEvent) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); + + if (currentFocusId === SIDE_PANEL_FOCUS_ID) { + event.stopImmediatePropagation(); + event.preventDefault(); + closeCommandMenu(); + } + }, + [closeCommandMenu, store], ); useListenClickOutside({ diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts index 7ff1f40f209..caf418c819a 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts @@ -10,7 +10,7 @@ import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/addToNavPayloadRegistryStateV2'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { t } from '@lingui/core/macro'; import { useCallback } from 'react'; @@ -23,6 +23,8 @@ export const useCommandMenu = () => { const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); + const store = useStore(); + const closeCommandMenu = useRecoilCallback( ({ set, snapshot }) => () => { @@ -31,9 +33,9 @@ export const useCommandMenu = () => { .getValue(); if (isCommandMenuOpened) { - jotaiStore.set(addToNavPayloadRegistryStateV2.atom, new Map()); + store.set(addToNavPayloadRegistryStateV2.atom, new Map()); set(isCommandMenuOpenedState, false); - jotaiStore.set(isCommandMenuOpenedStateV2.atom, false); + store.set(isCommandMenuOpenedStateV2.atom, false); set(isCommandMenuClosingState, true); closeAnyOpenDropdown(); removeFocusItemFromFocusStackById({ @@ -41,7 +43,7 @@ export const useCommandMenu = () => { }); } }, - [closeAnyOpenDropdown, removeFocusItemFromFocusStackById], + [closeAnyOpenDropdown, removeFocusItemFromFocusStackById, store], ); const openCommandMenu = useCallback(() => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts index ac8a39e0fe4..8926d4f13b7 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts @@ -26,7 +26,7 @@ import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getSh import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowLogicFunctionTabListComponentId'; import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -39,6 +39,8 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { const { closeDropdown } = useCloseDropdown(); + const store = useStore(); + const commandMenuCloseAnimationCompleteCleanup = useRecoilCallback( ({ snapshot, set }) => () => { @@ -106,7 +108,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { instanceId: '', }); set(isCommandMenuOpenedState, false); - jotaiStore.set(isCommandMenuOpenedStateV2.atom, false); + store.set(isCommandMenuOpenedStateV2.atom, false); set(commandMenuSearchState, ''); set(commandMenuNavigationMorphItemsByPageState, new Map()); set(commandMenuNavigationStackState, []); @@ -115,7 +117,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { emitSidePanelCloseEvent(); set(isCommandMenuClosingState, false); - set( + store.set( activeTabIdComponentState.atomFamily({ instanceId: WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID, }), @@ -125,7 +127,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { for (const [pageId, morphItems] of snapshot .getLoadable(commandMenuNavigationMorphItemsByPageState) .getValue()) { - set( + store.set( activeTabIdComponentState.atomFamily({ instanceId: getShowPageTabListComponentId({ pageId, @@ -136,7 +138,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => { ); } }, - [closeDropdown, resetContextStoreStates, resetSelectedItem], + [closeDropdown, resetContextStoreStates, resetSelectedItem, store], ); return { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHistory.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHistory.ts index a76c39521b3..0abf97e3b1f 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHistory.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHistory.ts @@ -8,12 +8,15 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useStore } from 'jotai'; import { isNonEmptyArray } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; export const useCommandMenuHistory = () => { const { closeCommandMenu } = useCommandMenu(); + const store = useStore(); + const goBackFromCommandMenu = useRecoilCallback( ({ snapshot, set }) => { return () => { @@ -53,7 +56,7 @@ export const useCommandMenuHistory = () => { const morphItems = currentMorphItems.get(removedItem.pageId); if (isNonEmptyArray(morphItems)) { - set( + store.set( activeTabIdComponentState.atomFamily({ instanceId: getShowPageTabListComponentId({ pageId: removedItem.pageId, @@ -69,62 +72,68 @@ export const useCommandMenuHistory = () => { set(hasUserSelectedCommandState, false); }; }, - [closeCommandMenu], + [closeCommandMenu, store], ); - const navigateCommandMenuHistory = useRecoilCallback(({ snapshot, set }) => { - return (pageIndex: number) => { - const currentNavigationStack = snapshot - .getLoadable(commandMenuNavigationStackState) - .getValue(); + const navigateCommandMenuHistory = useRecoilCallback( + ({ snapshot, set }) => { + return (pageIndex: number) => { + const currentNavigationStack = snapshot + .getLoadable(commandMenuNavigationStackState) + .getValue(); - const newNavigationStack = currentNavigationStack.slice(0, pageIndex + 1); - - set(commandMenuNavigationStackState, newNavigationStack); - - const newNavigationStackItem = newNavigationStack.at(-1); - - if (!isDefined(newNavigationStackItem)) { - throw new Error( - `No command menu navigation stack item found for index ${pageIndex}`, + const newNavigationStack = currentNavigationStack.slice( + 0, + pageIndex + 1, ); - } - set(commandMenuPageState, newNavigationStackItem.page); - set(commandMenuPageInfoState, { - title: newNavigationStackItem.pageTitle, - Icon: newNavigationStackItem.pageIcon, - instanceId: newNavigationStackItem.pageId, - }); - const currentMorphItems = snapshot - .getLoadable(commandMenuNavigationMorphItemsByPageState) - .getValue(); + set(commandMenuNavigationStackState, newNavigationStack); - for (const [pageId, morphItems] of currentMorphItems.entries()) { - if (!newNavigationStack.some((item) => item.pageId === pageId)) { - set( - activeTabIdComponentState.atomFamily({ - instanceId: getShowPageTabListComponentId({ - pageId, - targetObjectId: morphItems[0].recordId, - }), - }), - null, + const newNavigationStackItem = newNavigationStack.at(-1); + + if (!isDefined(newNavigationStackItem)) { + throw new Error( + `No command menu navigation stack item found for index ${pageIndex}`, ); } - } - const newMorphItems = new Map( - Array.from(currentMorphItems.entries()).filter(([pageId]) => - newNavigationStack.some((item) => item.pageId === pageId), - ), - ); + set(commandMenuPageState, newNavigationStackItem.page); + set(commandMenuPageInfoState, { + title: newNavigationStackItem.pageTitle, + Icon: newNavigationStackItem.pageIcon, + instanceId: newNavigationStackItem.pageId, + }); + const currentMorphItems = snapshot + .getLoadable(commandMenuNavigationMorphItemsByPageState) + .getValue(); - set(commandMenuNavigationMorphItemsByPageState, newMorphItems); + for (const [pageId, morphItems] of currentMorphItems.entries()) { + if (!newNavigationStack.some((item) => item.pageId === pageId)) { + store.set( + activeTabIdComponentState.atomFamily({ + instanceId: getShowPageTabListComponentId({ + pageId, + targetObjectId: morphItems[0].recordId, + }), + }), + null, + ); + } + } - set(hasUserSelectedCommandState, false); - }; - }, []); + const newMorphItems = new Map( + Array.from(currentMorphItems.entries()).filter(([pageId]) => + newNavigationStack.some((item) => item.pageId === pageId), + ), + ); + + set(commandMenuNavigationMorphItemsByPageState, newMorphItems); + + set(hasUserSelectedCommandState, false); + }; + }, + [store], + ); return { goBackFromCommandMenu, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts index 5bbd8115973..f21e784b1ed 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts @@ -15,7 +15,7 @@ import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { type IconComponent } from 'twenty-ui/display'; import { v4 } from 'uuid'; @@ -36,6 +36,8 @@ export const useNavigateCommandMenu = () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); + const store = useStore(); + const openCommandMenu = useRecoilCallback( ({ snapshot, set }) => () => { @@ -72,13 +74,14 @@ export const useNavigateCommandMenu = () => { }); set(isCommandMenuOpenedState, true); - jotaiStore.set(isCommandMenuOpenedStateV2.atom, true); + store.set(isCommandMenuOpenedStateV2.atom, true); set(hasUserSelectedCommandState, false); }, [ copyContextStoreStates, commandMenuCloseAnimationCompleteCleanup, pushFocusItemToFocusStack, + store, ], ); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts index f4b818164f5..42b0b5c43cc 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts @@ -1,7 +1,7 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { viewableRichTextComponentStateV2 } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentStateV2'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { t } from '@lingui/core/macro'; import { useCallback } from 'react'; import { IconPencil } from 'twenty-ui/display'; @@ -9,6 +9,8 @@ import { IconPencil } from 'twenty-ui/display'; export const useRichTextCommandMenu = () => { const { navigateCommandMenu, openCommandMenu } = useCommandMenu(); + const store = useStore(); + const openRichTextInCommandMenu = useCallback( ({ activityId, @@ -17,7 +19,7 @@ export const useRichTextCommandMenu = () => { activityId: string; activityObjectNameSingular: string; }) => { - jotaiStore.set(viewableRichTextComponentStateV2.atom, { + store.set(viewableRichTextComponentStateV2.atom, { activityId, activityObjectNameSingular, }); @@ -29,7 +31,7 @@ export const useRichTextCommandMenu = () => { pageIcon: IconPencil, }); }, - [navigateCommandMenu, openCommandMenu], + [navigateCommandMenu, openCommandMenu, store], ); const editRichText = useCallback( diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/WidgetSettingsFooter.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/WidgetSettingsFooter.tsx index aa14fb739a3..24d254a51bf 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/WidgetSettingsFooter.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/WidgetSettingsFooter.tsx @@ -7,6 +7,7 @@ import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDraw import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useLingui } from '@lingui/react/macro'; import { useId } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -42,7 +43,7 @@ export const WidgetSettingsFooter = ({ closeDropdown(dropdownId); }; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx index d44777641ba..05da08419b1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx @@ -11,7 +11,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; import { MenuItemSelect } from 'twenty-ui/navigation'; @@ -40,7 +40,7 @@ export const ChartAggregateOperationSelectableListItem = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx index 0d715e082cf..db7659ccb66 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx @@ -10,7 +10,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { AxisNameDisplay } from '~/generated-metadata/graphql'; @@ -34,7 +34,7 @@ export const ChartAxisNameSelectionDropdownContent = () => { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx index d564e847354..6a5412b9cd1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx @@ -13,7 +13,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { capitalize, isDefined } from 'twenty-shared/utils'; @@ -35,7 +35,7 @@ export const ChartColorSelectionDropdownContent = () => { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx index e5292532c5e..233f28fa2b6 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent.tsx @@ -15,7 +15,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { Trans } from '@lingui/react/macro'; import { useState } from 'react'; @@ -44,7 +44,7 @@ export const ChartDataSourceDropdownContent = () => { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx index 541843d282b..9a622123e1c 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx @@ -13,7 +13,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { MenuItemSelect } from 'twenty-ui/navigation'; @@ -107,7 +107,7 @@ export const ChartDateGranularitySelectionDropdownContent = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx index c10ae407120..a61a26684a2 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx @@ -13,7 +13,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { useIcons } from 'twenty-ui/display'; @@ -60,7 +60,7 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView.tsx index a68c979ad98..f984ba3bb69 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView.tsx @@ -11,7 +11,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { IconChevronLeft, useIcons } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; @@ -34,7 +34,7 @@ export const ChartGroupByFieldSelectionCompositeFieldView = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx index 7bf79444ad7..82104436904 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx @@ -19,7 +19,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useMemo, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -72,7 +72,7 @@ export const ChartGroupByFieldSelectionDropdownContentBase = < DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionRelationFieldView.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionRelationFieldView.tsx index 584c4d2e1ef..5328f0396d0 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionRelationFieldView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionRelationFieldView.tsx @@ -14,7 +14,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useMemo, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -46,7 +46,7 @@ export const ChartGroupByFieldSelectionRelationFieldView = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx index 6f0d74701c0..a1c1964b5d9 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx @@ -6,7 +6,7 @@ import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/ import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; import { MenuItemSelect } from 'twenty-ui/navigation'; @@ -24,7 +24,7 @@ export const ChartRatioAggregateOperationSelectableListItem = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx index 13bac6f0ba5..64eaef1d63b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx @@ -7,7 +7,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { IconCheck, IconX } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { AggregateOperations } from '~/generated-metadata/graphql'; @@ -31,7 +31,7 @@ export const ChartRatioOptionBooleanSelectableListItem = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx index 496542f3f41..6512f1eabaf 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx @@ -7,7 +7,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { MenuItemSelectTag } from 'twenty-ui/navigation'; import { type ThemeColor } from 'twenty-ui/theme'; import { AggregateOperations } from '~/generated-metadata/graphql'; @@ -33,7 +33,7 @@ export const ChartRatioOptionSelectSelectableListItem = ({ DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx index 7e0575c3a1b..0d865dba619 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx @@ -18,12 +18,12 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { - type GraphOrderBy, - GraphOrderBy as GraphOrderByEnum, + GraphOrderBy, + type GraphOrderBy as GraphOrderByType, } from '~/generated-metadata/graphql'; export const ChartSortByGroupByFieldDropdownContent = () => { @@ -53,7 +53,7 @@ export const ChartSortByGroupByFieldDropdownContent = () => { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); @@ -74,12 +74,12 @@ export const ChartSortByGroupByFieldDropdownContent = () => { return null; } - const handleSelectSortOption = (orderBy: GraphOrderBy) => { + const handleSelectSortOption = (orderBy: GraphOrderByType) => { const configToUpdate: Record = { secondaryAxisOrderBy: orderBy, }; - if (orderBy === GraphOrderByEnum.MANUAL) { + if (orderBy === GraphOrderBy.MANUAL) { const existingManualSortOrder = configuration.secondaryAxisManualSortOrder; @@ -94,7 +94,7 @@ export const ChartSortByGroupByFieldDropdownContent = () => { return; } - if (configuration.secondaryAxisOrderBy === GraphOrderByEnum.MANUAL) { + if (configuration.secondaryAxisOrderBy === GraphOrderBy.MANUAL) { configToUpdate.secondaryAxisManualSortOrder = null; } @@ -125,7 +125,7 @@ export const ChartSortByGroupByFieldDropdownContent = () => { selectableItemIdArray={availableOptions.map((option) => option.value)} > {availableOptions.map((sortOption) => { - const isManualOption = sortOption.value === GraphOrderByEnum.MANUAL; + const isManualOption = sortOption.value === GraphOrderBy.MANUAL; return ( { { DropdownComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts index 6832ae15be7..f1d50cb1ec4 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts @@ -16,6 +16,7 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab import { updateWidgetMinimumSizeForGraphType } from '@/page-layout/utils/updateWidgetMinimumSizeForGraphType'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { BarChartLayout } from '~/generated-metadata/graphql'; @@ -31,11 +32,6 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - tabListInstanceId, - ); - const currentlyEditingWidgetIdState = useRecoilComponentCallbackState( pageLayoutEditingWidgetIdComponentState, pageLayoutId, @@ -51,6 +47,8 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ pageLayoutId, ); + const store = useStore(); + const getConfigToUpdateAfterGraphTypeChange = useRecoilCallback( ({ set, snapshot }) => (graphType: GraphType) => { @@ -164,7 +162,11 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ }; } - const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); + const activeTabId = store.get( + activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }), + ); if (isDefined(activeTabId) && isDefined(currentlyEditingWidgetId)) { const currentLayouts = snapshot @@ -184,11 +186,12 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ return configToUpdate; }, [ - activeTabIdState, currentlyEditingWidgetIdState, objectMetadataItems, pageLayoutCurrentLayoutsState, pageLayoutDraftState, + store, + tabListInstanceId, widget.objectMetadataId, ], ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx index 431a36fed9d..b79850ea11f 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx @@ -8,6 +8,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps'; import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; @@ -59,7 +60,7 @@ export const CommandMenuWorkflowRunViewStepContent = () => { ); } - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, commandMenuPageComponentInstance.instanceId, ); diff --git a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx index 1f0a8dee96c..88d6bf04810 100644 --- a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx +++ b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx @@ -25,7 +25,6 @@ import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/compo import { currentFavoriteFolderIdStateV2 } from '@/ui/navigation/navigation-drawer/states/currentFavoriteFolderIdStateV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { Droppable } from '@hello-pangea/dnd'; import { useLingui } from '@lingui/react/macro'; @@ -163,7 +162,7 @@ export const CurrentWorkspaceMemberFavorites = ({ /> ); - const isModalOpened = useRecoilComponentValue( + const isModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, modalId, ); diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts index 332a1f28397..8be94deb7be 100644 --- a/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts +++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu.ts @@ -4,7 +4,7 @@ import { isKeyboardShortcutMenuOpenedStateV2 } from '@/keyboard-shortcut-menu/st import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; export const KEYBOARD_SHORTCUT_MENU_INSTANCE_ID = 'keyboard-shortcut-menu'; @@ -13,8 +13,10 @@ export const useKeyboardShortcutMenu = () => { const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); + const store = useStore(); + const openKeyboardShortcutMenu = useCallback(() => { - jotaiStore.set(isKeyboardShortcutMenuOpenedStateV2.atom, true); + store.set(isKeyboardShortcutMenuOpenedStateV2.atom, true); pushFocusItemToFocusStack({ focusId: KEYBOARD_SHORTCUT_MENU_INSTANCE_ID, component: { @@ -26,23 +28,23 @@ export const useKeyboardShortcutMenu = () => { enableGlobalHotkeysWithModifiers: false, }, }); - }, [pushFocusItemToFocusStack]); + }, [pushFocusItemToFocusStack, store]); const closeKeyboardShortcutMenu = useCallback(() => { - const isKeyboardShortcutMenuOpened = jotaiStore.get( + const isKeyboardShortcutMenuOpened = store.get( isKeyboardShortcutMenuOpenedStateV2.atom, ); if (isKeyboardShortcutMenuOpened) { - jotaiStore.set(isKeyboardShortcutMenuOpenedStateV2.atom, false); + store.set(isKeyboardShortcutMenuOpenedStateV2.atom, false); removeFocusItemFromFocusStackById({ focusId: KEYBOARD_SHORTCUT_MENU_INSTANCE_ID, }); } - }, [removeFocusItemFromFocusStackById]); + }, [removeFocusItemFromFocusStackById, store]); const toggleKeyboardShortcutMenu = useCallback(() => { - const isKeyboardShortcutMenuOpened = jotaiStore.get( + const isKeyboardShortcutMenuOpened = store.get( isKeyboardShortcutMenuOpenedStateV2.atom, ); @@ -51,7 +53,7 @@ export const useKeyboardShortcutMenu = () => { } else { closeKeyboardShortcutMenu(); } - }, [closeKeyboardShortcutMenu, openKeyboardShortcutMenu]); + }, [store, closeKeyboardShortcutMenu, openKeyboardShortcutMenu]); return { toggleKeyboardShortcutMenu, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx index 4f6883d4333..b03976e275a 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx @@ -44,7 +44,6 @@ import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/compo import { currentNavigationMenuItemFolderIdStateV2 } from '@/ui/navigation/navigation-drawer/states/currentNavigationMenuItemFolderIdStateV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; @@ -195,7 +194,7 @@ export const CurrentWorkspaceMemberNavigationMenuItems = ({ /> ); - const isModalOpened = useRecoilComponentValue( + const isModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, modalId, ); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx index 3a7945e2a32..1791827d9a2 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx @@ -28,7 +28,7 @@ import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavi import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; @@ -41,6 +41,7 @@ const StyledRightIconsContainer = styled.div` export const WorkspaceNavigationMenuItems = () => { const items = useWorkspaceSectionItems(); + const store = useStore(); const enterEditMode = useRecoilCallback( ({ snapshot }) => () => { @@ -50,13 +51,13 @@ export const WorkspaceNavigationMenuItems = () => { const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems( prefetchNavigationMenuItems, ); - jotaiStore.set( + store.set( navigationMenuItemsDraftStateV2.atom, workspaceNavigationMenuItems, ); - jotaiStore.set(isNavigationMenuInEditModeStateV2.atom, true); + store.set(isNavigationMenuInEditModeStateV2.atom, true); }, - [], + [store], ); const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts index a624e1abc0c..fb9598bf335 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts @@ -20,7 +20,7 @@ import { openNavigationMenuItemFolderIdsStateV2 } from '@/navigation-menu-item/s import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isWorkspaceDroppableId } from '@/navigation-menu-item/utils/isWorkspaceDroppableId'; import { validateAndExtractWorkspaceFolderId } from '@/navigation-menu-item/utils/validateAndExtractWorkspaceFolderId'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; @@ -52,6 +52,8 @@ export const useHandleAddToNavigationDrop = () => { openNavigationMenuItemFolderIdsStateV2, ); + const store = useStore(); + const handleAddToNavigationDrop = useRecoilCallback( () => (result: DropResult, _provided: ResponderProvided) => { const { source, destination, draggableId } = result; @@ -64,8 +66,7 @@ export const useHandleAddToNavigationDrop = () => { } const payload = - jotaiStore.get(addToNavPayloadRegistryStateV2.atom).get(draggableId) ?? - null; + store.get(addToNavPayloadRegistryStateV2.atom).get(draggableId) ?? null; if (!payload) { return; } @@ -202,6 +203,7 @@ export const useHandleAddToNavigationDrop = () => { setOpenNavigationMenuItemFolderIds, setIsNavigationMenuInEditMode, setSelectedNavigationMenuItemInEditMode, + store, workspaceNavigationMenuItems, ], ); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts index 3153b8d7e04..6e61f20373f 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts @@ -12,7 +12,7 @@ import { navigationMenuItemsDraftStateV2 } from '@/navigation-menu-item/states/n import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/utils/filterWorkspaceNavigationMenuItems'; import { isNavigationMenuItemFolder } from '@/navigation-menu-item/utils/isNavigationMenuItemFolder'; import { isNavigationMenuItemLink } from '@/navigation-menu-item/utils/isNavigationMenuItemLink'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState'; export const useSaveNavigationMenuItemsDraft = () => { @@ -23,10 +23,12 @@ export const useSaveNavigationMenuItemsDraft = () => { refetchQueries: ['FindManyNavigationMenuItems'], }); + const store = useStore(); + const saveDraft = useRecoilCallback( ({ snapshot }) => async () => { - const draft = jotaiStore.get(navigationMenuItemsDraftStateV2.atom); + const draft = store.get(navigationMenuItemsDraftStateV2.atom); const prefetch = snapshot .getLoadable(prefetchNavigationMenuItemsState) .getValue(); @@ -190,6 +192,7 @@ export const useSaveNavigationMenuItemsDraft = () => { updateNavigationMenuItem, deleteNavigationMenuItem, createNavigationMenuItemMutation, + store, ], ); diff --git a/packages/twenty-front/src/modules/navigation/components/PageDragDropProvider.tsx b/packages/twenty-front/src/modules/navigation/components/PageDragDropProvider.tsx index c1f3054147e..345e061d1f4 100644 --- a/packages/twenty-front/src/modules/navigation/components/PageDragDropProvider.tsx +++ b/packages/twenty-front/src/modules/navigation/components/PageDragDropProvider.tsx @@ -24,7 +24,7 @@ import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/ad import { getDropTargetIdFromDestination } from '@/navigation-menu-item/utils/getDropTargetIdFromDestination'; import { isWorkspaceDroppableId } from '@/navigation-menu-item/utils/isWorkspaceDroppableId'; import { validateAndExtractWorkspaceFolderId } from '@/navigation-menu-item/utils/validateAndExtractWorkspaceFolderId'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isDefined } from 'twenty-shared/utils'; @@ -53,6 +53,7 @@ export const PageDragDropProvider = ({ setAddToNavigationFallbackDestination, ] = useState<{ droppableId: string; index: number } | null>(null); + const store = useStore(); const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState(); const { handleAddToNavigationDrop } = useHandleAddToNavigationDrop(); const { handleNavigationMenuItemDragAndDrop } = @@ -95,7 +96,7 @@ export const PageDragDropProvider = ({ setActiveDropTargetId(dropTargetId); const payload = - jotaiStore + store .get(addToNavPayloadRegistryStateV2.atom) .get(update.draggableId) ?? null; const folderId = validateAndExtractWorkspaceFolderId( @@ -112,7 +113,7 @@ export const PageDragDropProvider = ({ ); } }) as OnDragUpdateResponder, - [addToNavigationFallbackDestination], + [addToNavigationFallbackDestination, store], ); const handleDragEnd = (result: DropResult, provided: ResponderProvided) => { diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx index 90c2df0c517..d14e53037b6 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx @@ -16,7 +16,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useContext } from 'react'; import { type ViewFilterOperand } from 'twenty-shared/types'; @@ -48,7 +48,7 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({ applyObjectFilterDropdownOperand(operand); }; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx index 622ad5333ad..921506743d5 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu.tsx @@ -1,6 +1,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; @@ -83,7 +84,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({ const { advancedFilterFieldSelectDropdownId } = useAdvancedFilterFieldSelectDropdown(recordFilterId); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, advancedFilterFieldSelectDropdownId, ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx index 25fe7e234e1..e6acae9f2a6 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx @@ -2,8 +2,8 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId'; import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { useIcons } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; @@ -18,8 +18,8 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ }: ObjectFilterDropdownFilterSelectMenuItemProps) => { const { resetSelectedItem } = useSelectableList(FILTER_FIELD_LIST_ID); - const isSelectedItem = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItem = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, fieldMetadataItemToSelect.id, ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx index b94ba55f992..cac7e9dd576 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx @@ -18,6 +18,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { MAX_OPTIONS_TO_DISPLAY } from 'twenty-shared/constants'; @@ -68,7 +69,7 @@ export const ObjectFilterDropdownOptionSelect = ({ const { resetSelectedItem } = useSelectableList(componentInstanceId); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarViewContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarViewContent.tsx index 4275b505f6b..bcb26ce7737 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarViewContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCalendarViewContent.tsx @@ -8,7 +8,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { t } from '@lingui/core/macro'; import { useRecoilValue, useSetRecoilState } from 'recoil'; @@ -32,7 +32,7 @@ export const ObjectOptionsDropdownCalendarViewContent = () => { ); const { updateCurrentView } = useUpdateCurrentView(); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx index a24b3e18c2a..63c5cb3b439 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownCustomView.tsx @@ -13,6 +13,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewKey } from '@/views/types/ViewKey'; import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType'; @@ -104,7 +105,7 @@ export const ObjectOptionsDropdownCustomView = ({ 'Delete view', ]; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx index d0aa5a2613e..d2dd83cda28 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx @@ -9,6 +9,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useOpenCreateViewDropdown } from '@/views/hooks/useOpenCreateViewDropown'; import { useLingui } from '@lingui/react/macro'; @@ -41,7 +42,7 @@ export const ObjectOptionsDropdownDefaultView = () => { 'Create custom view', ]; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx index 779ee3df4f8..a349b08eae9 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutContent.tsx @@ -14,6 +14,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { type GraphQLView } from '@/views/types/GraphQLView'; @@ -124,7 +125,7 @@ export const ObjectOptionsDropdownLayoutContent = () => { ...(currentView?.type !== ViewType.Table ? ['Compact view'] : []), ]; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx index 371b7baa041..46c415a04b7 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownLayoutOpenInContent.tsx @@ -11,7 +11,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; import { t } from '@lingui/core/macro'; @@ -33,7 +33,7 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => { objectMetadataItem.nameSingular, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx index 61a31eecf6f..507b1e5773f 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx @@ -14,6 +14,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { IconChevronLeft, @@ -38,7 +39,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => { setRecordGroupSort(sort); }; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx index b7c1a0b330b..27269109570 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx @@ -19,6 +19,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy'; import { useLingui } from '@lingui/react/macro'; @@ -90,7 +91,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { } }, [hiddenRecordGroupIds, currentContentId, onContentChange]); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx index 4bea9240cef..f8a9a5616e8 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx @@ -10,7 +10,7 @@ import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/Gene import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; @@ -39,7 +39,7 @@ export const ObjectOptionsDropdownVisibilityContent = () => { const hasViewsPermission = useHasPermissionFlag(PermissionFlagType.VIEWS); const { canPersistChanges } = useCanPersistViewChanges(); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_OPTIONS_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions.ts b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions.ts index 0bd4575a821..c22abcb2187 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions.ts +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions.ts @@ -1,7 +1,7 @@ import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { recordIndexOpenRecordInStateV2 } from '@/object-record/record-index/states/recordIndexOpenRecordInStateV2'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { type GraphQLView } from '@/views/types/GraphQLView'; import { type ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -11,6 +11,8 @@ import { useCallback } from 'react'; import { useSetRecoilState } from 'recoil'; export const useUpdateObjectViewOptions = () => { + const store = useStore(); + const setRecordIndexOpenRecordIn = useSetRecoilState( recordIndexOpenRecordInState, ); @@ -29,12 +31,12 @@ export const useUpdateObjectViewOptions = () => { (openRecordIn: ViewOpenRecordInType, view: GraphQLView | undefined) => { if (!view) return; setRecordIndexOpenRecordIn(openRecordIn); - jotaiStore.set(recordIndexOpenRecordInStateV2.atom, openRecordIn); + store.set(recordIndexOpenRecordInStateV2.atom, openRecordIn); updateCurrentView({ openRecordIn, }); }, - [setRecordIndexOpenRecordIn, updateCurrentView], + [setRecordIndexOpenRecordIn, updateCurrentView, store], ); const setAndPersistViewName = useCallback( diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx index 02678235125..b0ce0fa5d74 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx @@ -29,6 +29,7 @@ import { useRecoilComponentState } from '@/ui/utilities/state/component-state/ho import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { Trans, useLingui } from '@lingui/react/macro'; import { useRecoilValue } from 'recoil'; import { findByProperty } from 'twenty-shared/utils'; @@ -151,12 +152,12 @@ export const ObjectSortDropdownButton = () => { ...hiddenFieldMetadataItemsSorted.map((item) => item.id), ]; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, OBJECT_SORT_DROPDOWN_ID, ); - const setSelectedItemId = useSetRecoilComponentState( + const setSelectedItemId = useSetRecoilComponentStateV2( selectedItemIdComponentState, OBJECT_SORT_DROPDOWN_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardContainer.tsx index 00539c80084..dd34d706c76 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardContainer.tsx @@ -15,6 +15,7 @@ import { RECORD_INDEX_REMOVE_SORTING_MODAL_ID } from '@/object-record/record-ind import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isDefined } from 'twenty-shared/utils'; type RecordBoardContainerProps = { @@ -55,7 +56,7 @@ export const RecordBoardContainer = ({ ...args, }); - const isRecordIndexRemoveSortingModalOpened = useRecoilComponentValue( + const isRecordIndexRemoveSortingModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, RECORD_INDEX_REMOVE_SORTING_MODAL_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardStickyHeaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardStickyHeaderEffect.tsx index bdf26ff5571..9ba2a5d52f7 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardStickyHeaderEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardStickyHeaderEffect.tsx @@ -1,9 +1,9 @@ import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useEffect } from 'react'; export const RecordBoardStickyHeaderEffect = () => { - const scrollTop = useRecoilComponentValue( + const scrollTop = useRecoilComponentValueV2( scrollWrapperScrollTopComponentState, ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx index 56a716af8fa..7a9efb55fd8 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/anchored-portal/components/RecordBoardCardInputContextProvider.tsx @@ -11,8 +11,8 @@ import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlin import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; -import { useRecoilCallback } from 'recoil'; type RecordBoardCardInputContextProviderProps = { children: React.ReactNode; @@ -21,6 +21,7 @@ type RecordBoardCardInputContextProviderProps = { export const RecordBoardCardInputContextProvider = ({ children, }: RecordBoardCardInputContextProviderProps) => { + const store = useStore(); const { closeInlineCell } = useInlineCell(); const setRecordBoardCardEditModePosition = useSetRecoilComponentState( recordBoardCardEditModePositionComponentState, @@ -58,29 +59,27 @@ export const RecordBoardCardInputContextProvider = ({ closeInlineCellAndResetEditModePosition(); }; - const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback( - ({ snapshot }) => - ({ newValue, event, skipPersist }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside: FieldInputClickOutsideEvent = useCallback( + ({ newValue, event, skipPersist }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId !== instanceId) { - return; - } - event?.preventDefault(); - event?.stopImmediatePropagation(); + if (currentFocusId !== instanceId) { + return; + } + event?.preventDefault(); + event?.stopImmediatePropagation(); - if (skipPersist !== true) { - persistFieldFromFieldInputContext(newValue); - } + if (skipPersist !== true) { + persistFieldFromFieldInputContext(newValue); + } - closeInlineCellAndResetEditModePosition(); - }, + closeInlineCellAndResetEditModePosition(); + }, [ closeInlineCellAndResetEditModePosition, instanceId, persistFieldFromFieldInputContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardInputContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardInputContextProvider.tsx index 86363307663..300bc31dca1 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardInputContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardInputContextProvider.tsx @@ -11,8 +11,8 @@ import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlin import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; -import { useRecoilCallback } from 'recoil'; type RecordCalendarCardInputContextProviderProps = { children: React.ReactNode; @@ -21,6 +21,7 @@ type RecordCalendarCardInputContextProviderProps = { export const RecordCalendarCardInputContextProvider = ({ children, }: RecordCalendarCardInputContextProviderProps) => { + const store = useStore(); const { closeInlineCell } = useInlineCell(); const setRecordCalendarCardEditModePosition = useSetRecoilComponentState( recordCalendarCardEditModePositionComponentState, @@ -58,29 +59,27 @@ export const RecordCalendarCardInputContextProvider = ({ closeInlineCellAndResetEditModePosition(); }; - const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback( - ({ snapshot }) => - ({ newValue, event, skipPersist }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside: FieldInputClickOutsideEvent = useCallback( + ({ newValue, event, skipPersist }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId !== instanceId) { - return; - } - event?.preventDefault(); - event?.stopImmediatePropagation(); + if (currentFocusId !== instanceId) { + return; + } + event?.preventDefault(); + event?.stopImmediatePropagation(); - if (skipPersist !== true) { - persistFieldFromFieldInputContext(newValue); - } + if (skipPersist !== true) { + persistFieldFromFieldInputContext(newValue); + } - closeInlineCellAndResetEditModePosition(); - }, + closeInlineCellAndResetEditModePosition(); + }, [ closeInlineCellAndResetEditModePosition, instanceId, persistFieldFromFieldInputContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider.tsx index 6fa0c932c5e..ce41f4f8015 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/anchored-portal/components/RecordFieldListInputContextProvider.tsx @@ -14,8 +14,8 @@ import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; -import { useRecoilCallback } from 'recoil'; type RecordFieldListInputContextProviderProps = { children: React.ReactNode; @@ -32,6 +32,7 @@ export const RecordFieldListInputContextProvider = ({ objectMetadataItem, instanceIdPrefix, }: RecordFieldListInputContextProviderProps) => { + const store = useStore(); const instanceId = useAvailableComponentInstanceId( RecordFieldComponentInstanceContext, ); @@ -86,29 +87,27 @@ export const RecordFieldListInputContextProvider = ({ closeInlineCellAndResetEditModePosition(); }; - const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback( - ({ snapshot }) => - ({ newValue, event, skipPersist }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside: FieldInputClickOutsideEvent = useCallback( + ({ newValue, event, skipPersist }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId !== instanceId) { - return; - } - event?.preventDefault(); - event?.stopImmediatePropagation(); + if (currentFocusId !== instanceId) { + return; + } + event?.preventDefault(); + event?.stopImmediatePropagation(); - if (skipPersist !== true) { - persistFieldFromFieldInputContext(newValue); - } + if (skipPersist !== true) { + persistFieldFromFieldInputContext(newValue); + } - closeInlineCellAndResetEditModePosition(); - }, + closeInlineCellAndResetEditModePosition(); + }, [ closeInlineCellAndResetEditModePosition, instanceId, persistFieldFromFieldInputContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx index 7b8edcfa1ba..2e6b8671890 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiItemFieldInput.tsx @@ -17,7 +17,7 @@ import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentTyp import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { CustomError, isDefined } from 'twenty-shared/utils'; import { IconCheck, IconPlus } from 'twenty-ui/display'; import { LightIconButton } from 'twenty-ui/input'; @@ -77,7 +77,7 @@ export const MultiItemFieldInput = ({ RecordFieldComponentInstanceContext, ); - const currentFocusedItem = useRecoilValue(currentFocusedItemSelector); + const currentFocusedItem = useRecoilValueV2(currentFocusedItemSelector); useListenClickOutside({ refs: [containerRef], diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx index 324f772c8f9..187e2eef435 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/hooks/useOpenFilesFieldInput.tsx @@ -5,7 +5,6 @@ import { filesFieldUploadState } from '@/object-record/record-field/ui/states/fi import { filesFieldUploadStateV2 } from '@/object-record/record-field/ui/states/filesFieldUploadStateV2'; import { type FieldFilesValue } from '@/object-record/record-field/ui/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState'; @@ -16,6 +15,7 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush import { useRemoveLastFocusItemFromFocusStackByComponentType } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; +import { useStore } from 'jotai'; import { useLingui } from '@lingui/react/macro'; import { useRecoilCallback } from 'recoil'; import { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from 'twenty-shared/constants'; @@ -34,6 +34,7 @@ export const useOpenFilesFieldInput = () => { ); const { enqueueErrorSnackBar } = useSnackBar(); const { t } = useLingui(); + const store = useStore(); const openFilesFieldInput = useRecoilCallback( ({ snapshot, set }) => @@ -101,7 +102,7 @@ export const useOpenFilesFieldInput = () => { filesFieldUploadState({ recordId, fieldName }), 'UPLOAD_WINDOW_OPEN', ); - jotaiStore.set( + store.set( filesFieldUploadStateV2.atomFamily({ recordId, fieldName }), 'UPLOAD_WINDOW_OPEN', ); @@ -115,7 +116,7 @@ export const useOpenFilesFieldInput = () => { }); set(filesFieldUploadState({ recordId, fieldName }), null); - jotaiStore.set( + store.set( filesFieldUploadStateV2.atomFamily({ recordId, fieldName }), null, ); @@ -141,7 +142,7 @@ export const useOpenFilesFieldInput = () => { filesFieldUploadState({ recordId, fieldName }), 'UPLOADING_FILE', ); - jotaiStore.set( + store.set( filesFieldUploadStateV2.atomFamily({ recordId, fieldName }), 'UPLOADING_FILE', ); @@ -160,7 +161,7 @@ export const useOpenFilesFieldInput = () => { } } finally { set(filesFieldUploadState({ recordId, fieldName }), null); - jotaiStore.set( + store.set( filesFieldUploadStateV2.atomFamily({ recordId, fieldName }), null, ); @@ -183,7 +184,7 @@ export const useOpenFilesFieldInput = () => { }, onCancel: () => { set(filesFieldUploadState({ recordId, fieldName }), null); - jotaiStore.set( + store.set( filesFieldUploadStateV2.atomFamily({ recordId, fieldName }), null, ); @@ -214,6 +215,7 @@ export const useOpenFilesFieldInput = () => { removeLastFocusItemFromFocusStackByComponentType, enqueueErrorSnackBar, t, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx index 9ec4bd83a3f..2019578b8da 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx @@ -5,7 +5,7 @@ import { RECORD_INDEX_REMOVE_SORTING_MODAL_ID } from '@/object-record/record-ind import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; type RecordIndexTableContainerProps = { recordTableId: string; @@ -17,7 +17,7 @@ export const RecordIndexTableContainer = ({ const { objectNameSingular, viewBarInstanceId } = useRecordIndexContextOrThrow(); - const isRecordIndexRemoveSortingModalOpened = useRecoilComponentValue( + const isRecordIndexRemoveSortingModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, RECORD_INDEX_REMOVE_SORTING_MODAL_ID, ); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts index a8717f4b396..0f3aa5d453f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts @@ -15,7 +15,6 @@ import { recordIndexGroupAggregateFieldMetadataItemComponentState } from '@/obje import { recordIndexGroupAggregateOperationComponentState } from '@/object-record/record-index/states/recordIndexGroupAggregateOperationComponentState'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { recordIndexOpenRecordInStateV2 } from '@/object-record/record-index/states/recordIndexOpenRecordInStateV2'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; import { recordIndexShouldHideEmptyRecordGroupsComponentState } from '@/object-record/record-index/states/recordIndexShouldHideEmptyRecordGroupsComponentState'; import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState'; import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState'; @@ -27,11 +26,14 @@ import { type View } from '@/views/types/View'; import { type ViewField } from '@/views/types/ViewField'; import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; +import { useStore } from 'jotai'; import { useRecoilCallback, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; export const useLoadRecordIndexStates = () => { + const store = useStore(); + const setContextStoreTargetedRecordsRuleComponentState = useSetRecoilComponentState(contextStoreTargetedRecordsRuleComponentState); @@ -203,7 +205,7 @@ export const useLoadRecordIndexStates = () => { setRecordIndexViewType(view.type); setRecordIndexOpenRecordIn(view.openRecordIn); - jotaiStore.set(recordIndexOpenRecordInStateV2.atom, view.openRecordIn); + store.set(recordIndexOpenRecordInStateV2.atom, view.openRecordIn); setRecordIndexCalendarFieldMetadataIdState( view.calendarFieldMetadataId ?? null, @@ -259,6 +261,7 @@ export const useLoadRecordIndexStates = () => { setRecordIndexGroupAggregateOperation, getFieldMetadataItemByIdOrThrow, setRecordIndexGroupFieldMetadataItem, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx index 79f0583c660..8b45437e51a 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx @@ -19,7 +19,7 @@ import { usePersistFieldFromFieldInputContext } from '@/object-record/record-fie import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useIcons } from 'twenty-ui/display'; import { RecordInlineCellContainer } from './RecordInlineCellContainer'; import { @@ -46,6 +46,7 @@ export const RecordInlineCell = ({ isRecordFieldReadOnly: isReadOnly, } = useContext(FieldContext); const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); + const store = useStore(); const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode(); @@ -139,9 +140,7 @@ export const RecordInlineCell = ({ newValue, skipPersist, }: Parameters[0]) => { - const currentDropdownFocusId = jotaiStore.get( - activeDropdownFocusIdState.atom, - ); + const currentDropdownFocusId = store.get(activeDropdownFocusIdState.atom); const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, @@ -169,6 +168,7 @@ export const RecordInlineCell = ({ scopeInstanceId, closeInlineCell, persistFieldFromFieldInputContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-merge/components/MergeRecordsContainer.tsx b/packages/twenty-front/src/modules/object-record/record-merge/components/MergeRecordsContainer.tsx index f6e6dd5aec6..e8941df10de 100644 --- a/packages/twenty-front/src/modules/object-record/record-merge/components/MergeRecordsContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-merge/components/MergeRecordsContainer.tsx @@ -5,7 +5,7 @@ import { RightDrawerProvider } from '@/ui/layout/right-drawer/contexts/RightDraw import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; import { useMergeRecordsSelectedRecords } from '@/object-record/record-merge/hooks/useMergeRecordsSelectedRecords'; @@ -52,7 +52,7 @@ export const MergeRecordsContainer = ({ const instanceId = useAvailableComponentInstanceIdOrThrow( CommandMenuPageComponentInstanceContext, ); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, instanceId, ); diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx index 69898e0c3ef..67cefb5aa0e 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx @@ -8,10 +8,11 @@ import { multipleRecordPickerIsSelectedComponentFamilySelector } from '@/object- import { getMultipleRecordPickerSelectableListId } from '@/object-record/record-picker/multiple-record-picker/utils/getMultipleRecordPickerSelectableListId'; import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { capitalize } from 'twenty-shared/utils'; import { Avatar } from 'twenty-ui/display'; import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation'; @@ -42,8 +43,8 @@ export const MultipleRecordPickerMenuItemContent = ({ const selectableListComponentInstanceId = getMultipleRecordPickerSelectableListId(componentInstanceId); - const isSelectedByKeyboard = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedByKeyboard = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, searchRecord.recordId, selectableListComponentInstanceId, ); diff --git a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx index c9df5a07cb7..54518d7a68b 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem.tsx @@ -7,10 +7,10 @@ import { singleRecordPickerSearchableObjectMetadataItemsComponentState } from '@ import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId'; import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilValue } from 'recoil'; import { capitalize, isDefined } from 'twenty-shared/utils'; import { Avatar } from 'twenty-ui/display'; @@ -39,8 +39,8 @@ export const SingleRecordPickerMenuItem = ({ const selectableListComponentInstanceId = getSingleRecordPickerSelectableListId(recordPickerComponentInstanceId); - const isSelectedByKeyboard = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedByKeyboard = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, morphItem.recordId, selectableListComponentInstanceId, ); diff --git a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItems.tsx b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItems.tsx index f4e54ab3b3b..8de7ecf255e 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItems.tsx @@ -15,12 +15,12 @@ import { singleRecordPickerShouldShowSkeletonComponentState } from '@/object-rec import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId'; import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { type IconComponent } from 'twenty-ui/display'; import { MenuItemSelect } from 'twenty-ui/navigation'; @@ -53,8 +53,8 @@ export const SingleRecordPickerMenuItems = ({ selectableListComponentInstanceId, ); - const isSelectedSelectNoneButton = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedSelectNoneButton = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, selectableListComponentInstanceId, 'select-none', ); diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowEffect.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowEffect.tsx index 3c46c802e63..1f00b24e100 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowEffect.tsx @@ -5,7 +5,7 @@ import { buildFindOneRecordForShowPageOperationSignature } from '@/object-record import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyStateV2 } from '@/object-record/record-store/states/recordStoreFamilyStateV2'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useEffect } from 'react'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -28,6 +28,8 @@ export const RecordShowEffect = ({ objectMetadataItems, }); + const store = useStore(); + const { record, loading } = useFindOneRecord({ objectRecordId: recordId, objectNameSingular, @@ -44,13 +46,10 @@ export const RecordShowEffect = ({ if (JSON.stringify(previousRecordValue) !== JSON.stringify(newRecord)) { set(recordStoreFamilyState(recordId), newRecord); - jotaiStore.set( - recordStoreFamilyStateV2.atomFamily(recordId), - newRecord, - ); + store.set(recordStoreFamilyStateV2.atomFamily(recordId), newRecord); } }, - [recordId], + [recordId, store], ); useEffect(() => { diff --git a/packages/twenty-front/src/modules/object-record/record-store/hooks/useUpsertRecordsInStore.ts b/packages/twenty-front/src/modules/object-record/record-store/hooks/useUpsertRecordsInStore.ts index e0c5bfaad72..b4e3016377f 100644 --- a/packages/twenty-front/src/modules/object-record/record-store/hooks/useUpsertRecordsInStore.ts +++ b/packages/twenty-front/src/modules/object-record/record-store/hooks/useUpsertRecordsInStore.ts @@ -5,7 +5,7 @@ import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/ import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyStateV2 } from '@/object-record/record-store/states/recordStoreFamilyStateV2'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; @@ -15,6 +15,8 @@ type UpsertRecordsInStoreProps = { }; export const useUpsertRecordsInStore = () => { + const store = useStore(); + const upsertRecordsInStore = useRecoilCallback( ({ set, snapshot }) => ({ partialRecords, recordGqlFields }: UpsertRecordsInStoreProps) => { @@ -37,7 +39,7 @@ export const useUpsertRecordsInStore = () => { ...filteredPartialRecord, }; set(recordStoreFamilyState(partialRecord.id), newRecord); - jotaiStore.set( + store.set( recordStoreFamilyStateV2.atomFamily(partialRecord.id), newRecord, ); @@ -57,14 +59,14 @@ export const useUpsertRecordsInStore = () => { ...filteredPartialRecord, }; set(recordStoreFamilyState(partialRecord.id), updatedRecord); - jotaiStore.set( + store.set( recordStoreFamilyStateV2.atomFamily(partialRecord.id), updatedRecord, ); } } }, - [], + [store], ); return { diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index 9fc84153896..4bc1824d59d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -17,7 +17,7 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { isDefined } from 'twenty-shared/utils'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; @@ -69,6 +69,7 @@ export const useSetRecordTableData = ({ const { unfocusRecordTableCell } = useUnfocusRecordTableCell(recordTableId); const { unfocusRecordTableRow } = useFocusedRecordTableRow(recordTableId); + const store = useStore(); return useRecoilCallback( ({ set, snapshot }) => @@ -92,7 +93,7 @@ export const useSetRecordTableData = ({ }; set(recordStoreFamilyState(record.id), newRecord); - jotaiStore.set( + store.set( recordStoreFamilyStateV2.atomFamily(record.id), newRecord, ); @@ -162,6 +163,7 @@ export const useSetRecordTableData = ({ isRowSelectedFamilyState, recordIdByRealIndexCallbackSelector, isRecordTableInitialLoadingCallbackState, + store, ], ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx index 31a602504cc..579bb1415e1 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyFocusClickOutsideEffect.tsx @@ -8,7 +8,7 @@ import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constan import { currentFocusedItemSelector } from '@/ui/utilities/focus/states/currentFocusedItemSelector'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; type RecordTableBodyFocusClickOutsideEffectProps = { tableBodyRef: React.RefObject; }; @@ -20,7 +20,7 @@ export const RecordTableBodyFocusClickOutsideEffect = ({ const leaveTableFocus = useLeaveTableFocus(recordTableId); - const currentFocusedItem = useRecoilValue(currentFocusedItemSelector); + const currentFocusedItem = useRecoilValueV2(currentFocusedItemSelector); const componentType = currentFocusedItem?.componentInstance.componentType; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput.tsx index 4023e4b0ac8..79ddbe0dde7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellFieldInput.tsx @@ -11,10 +11,12 @@ import { import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; export const RecordTableCellFieldInput = () => { const { onMoveFocus, onCloseTableCell } = useRecordTableBodyContextOrThrow(); + const store = useStore(); const instanceId = useAvailableComponentInstanceId( RecordFieldComponentInstanceContext, @@ -44,26 +46,23 @@ export const RecordTableCellFieldInput = () => { onCloseTableCell(); }; - const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback( - ({ snapshot }) => - ({ newValue, event, skipPersist }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside: FieldInputClickOutsideEvent = useCallback( + ({ newValue, event, skipPersist }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId !== instanceId) { - return; - } - event?.preventDefault(); - event?.stopImmediatePropagation(); + if (currentFocusId !== instanceId) { + return; + } + event?.preventDefault(); + event?.stopImmediatePropagation(); - if (skipPersist !== true) { - persistFieldFromFieldInputContext(newValue); - } + if (skipPersist !== true) { + persistFieldFromFieldInputContext(newValue); + } - onCloseTableCell(); - }, - [onCloseTableCell, instanceId, persistFieldFromFieldInputContext], + onCloseTableCell(); + }, + [onCloseTableCell, instanceId, persistFieldFromFieldInputContext, store], ); const handleEscape: FieldInputEvent = ({ newValue, skipPersist }) => { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts index 37d44663b9a..a99a6c02bd2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellInGroup.ts @@ -1,5 +1,3 @@ -import { useRecoilCallback } from 'recoil'; - import { FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/FocusClickOutsideListenerId'; import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; @@ -8,10 +6,12 @@ import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useC import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; export const useCloseRecordTableCellInGroup = () => { const { recordTableId } = useRecordTableContextOrThrow(); + const store = useStore(); const { setDragSelectionStartEnabled } = useDragSelect(); @@ -22,26 +22,22 @@ export const useCloseRecordTableCellInGroup = () => { const closeCurrentTableCellInEditMode = useCloseCurrentTableCellInEditMode(recordTableId); - const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( - clickOutsideListenerIsActivatedComponentState, - RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, - ); - - const closeTableCellInGroup = useRecoilCallback( - ({ set }) => - () => { - toggleClickOutside(true); - setDragSelectionStartEnabled(true); - closeCurrentTableCellInEditMode(); - set(clickOutsideListenerIsActivatedState, true); - }, - [ - clickOutsideListenerIsActivatedState, - closeCurrentTableCellInEditMode, - setDragSelectionStartEnabled, - toggleClickOutside, - ], - ); + const closeTableCellInGroup = useCallback(() => { + toggleClickOutside(true); + setDragSelectionStartEnabled(true); + closeCurrentTableCellInEditMode(); + store.set( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId: RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, + }), + true, + ); + }, [ + closeCurrentTableCellInEditMode, + setDragSelectionStartEnabled, + toggleClickOutside, + store, + ]); return { closeTableCellInGroup, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts index b1f78b31c80..16ff90b33d3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/useCloseRecordTableCellNoGroup.ts @@ -6,11 +6,12 @@ import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-t import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; export const useCloseRecordTableCellNoGroup = () => { const { recordTableId } = useRecordTableContextOrThrow(); + const store = useStore(); const { setDragSelectionStartEnabled } = useDragSelect(); @@ -21,26 +22,22 @@ export const useCloseRecordTableCellNoGroup = () => { const closeCurrentTableCellInEditMode = useCloseCurrentTableCellInEditMode(recordTableId); - const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( - clickOutsideListenerIsActivatedComponentState, - RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, - ); - - const closeTableCellNoGroup = useRecoilCallback( - ({ set }) => - () => { - toggleClickOutside(true); - setDragSelectionStartEnabled(true); - closeCurrentTableCellInEditMode(); - set(clickOutsideListenerIsActivatedState, true); - }, - [ - clickOutsideListenerIsActivatedState, - closeCurrentTableCellInEditMode, - setDragSelectionStartEnabled, - toggleClickOutside, - ], - ); + const closeTableCellNoGroup = useCallback(() => { + toggleClickOutside(true); + setDragSelectionStartEnabled(true); + closeCurrentTableCellInEditMode(); + store.set( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId: RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, + }), + true, + ); + }, [ + closeCurrentTableCellInEditMode, + setDragSelectionStartEnabled, + toggleClickOutside, + store, + ]); return { closeTableCellNoGroup, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts index 4347a40944e..901a44a75a2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts @@ -28,8 +28,8 @@ import { useFocusedRecordTableRow } from '@/object-record/record-table/hooks/use import { useFocusRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useFocusRecordTableCell'; import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useStore } from 'jotai'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; export type OpenTableCellArgs = { @@ -43,11 +43,8 @@ export type OpenTableCellArgs = { export const useOpenRecordTableCell = (recordTableId: string) => { const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); + const store = useStore(); - const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( - clickOutsideListenerIsActivatedComponentState, - RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, - ); const setCurrentTableCellInEditModePosition = useSetRecoilComponentState( recordTableCellEditModePositionComponentState, recordTableId, @@ -82,7 +79,7 @@ export const useOpenRecordTableCell = (recordTableId: string) => { const { openRecordFromIndexView } = useOpenRecordFromIndexView(); const openTableCell = useRecoilCallback( - ({ snapshot, set }) => + ({ snapshot }) => ({ initialValue, cellPosition, @@ -91,7 +88,12 @@ export const useOpenRecordTableCell = (recordTableId: string) => { recordId, isNavigating, }: OpenTableCellArgs) => { - set(clickOutsideListenerIsActivatedState, false); + store.set( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId: RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, + }), + false, + ); const isFirstColumnCell = cellPosition.column === 0; @@ -169,7 +171,6 @@ export const useOpenRecordTableCell = (recordTableId: string) => { ); }, [ - clickOutsideListenerIsActivatedState, deactivateRecordTableRow, focusRecordTableCell, setIsRowFocusActive, @@ -184,6 +185,7 @@ export const useOpenRecordTableCell = (recordTableId: string) => { openRecordFromIndexView, activateRecordTableRow, unfocusRecordTableRow, + store, ], ); diff --git a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx index 7cf672da876..3acf67a49d1 100644 --- a/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/select/components/MultipleSelectDropdown.tsx @@ -9,7 +9,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { Avatar } from 'twenty-ui/display'; import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation'; @@ -39,7 +39,7 @@ export const MultipleSelectDropdown = ({ const { resetSelectedItem } = useSelectableList(selectableListId); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, selectableListId, ); diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx index 02cd836b407..8b07e733102 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.tsx @@ -9,6 +9,7 @@ import { calculateTotalGridRows } from '@/page-layout/utils/calculateTotalGridRo import { generateCellId } from '@/page-layout/utils/generateCellId'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { useMemo } from 'react'; @@ -60,7 +61,7 @@ export const PageLayoutGridOverlay = () => { pageLayoutCurrentLayoutsComponentState, ); - const activeTabId = useRecoilComponentValue(activeTabIdComponentState); + const activeTabId = useRecoilComponentValueV2(activeTabIdComponentState); const { createWidgetFromClick } = useCreateWidgetFromClick(); diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx index a0022bfef0d..8a82f5bf99b 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx @@ -21,6 +21,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { isDefined } from 'twenty-shared/utils'; @@ -59,7 +60,7 @@ export const PageLayoutRendererContent = () => { isPageLayoutInEditModeComponentState, ); - const activeTabId = useRecoilComponentValue(activeTabIdComponentState); + const activeTabId = useRecoilComponentValueV2(activeTabIdComponentState); const { createPageLayoutTab } = useCreatePageLayoutTab(currentPageLayout?.id); const { reorderTabs } = useReorderPageLayoutTabs(currentPageLayout?.id ?? ''); diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx index a6fc8a65324..d688940f121 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx @@ -24,8 +24,8 @@ import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/con import { type TabListProps } from '@/ui/layout/tab-list/types/TabListProps'; import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2'; import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; @@ -115,7 +115,7 @@ export const PageLayoutTabList = ({ const navigate = useNavigate(); - const [activeTabId, setActiveTabId] = useRecoilComponentState( + const [activeTabId, setActiveTabId] = useRecoilComponentStateV2( activeTabIdComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListEffect.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListEffect.tsx index f40f317ea2e..961c76520d8 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListEffect.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListEffect.tsx @@ -4,7 +4,7 @@ import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingC import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { type TabListProps } from '@/ui/layout/tab-list/types/TabListProps'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2'; import { useEffect } from 'react'; type PageLayoutTabListEffectProps = Pick< @@ -21,7 +21,7 @@ export const PageLayoutTabListEffect = ({ componentInstanceId, defaultTabToFocusOnMobileAndSidePanelId, }: PageLayoutTabListEffectProps) => { - const [activeTabId, setActiveTabId] = useRecoilComponentState( + const [activeTabId, setActiveTabId] = useRecoilComponentStateV2( activeTabIdComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/PageLayoutTestWrapper.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/PageLayoutTestWrapper.tsx index 1fdeb04083c..6015d1d3b05 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/PageLayoutTestWrapper.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/PageLayoutTestWrapper.tsx @@ -1,7 +1,12 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId'; import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext'; -import { type ReactNode } from 'react'; +import { + createStore, + type getDefaultStore, + Provider as JotaiProvider, +} from 'jotai'; +import { type ReactNode, useState } from 'react'; import { RecoilRoot, type MutableSnapshot } from 'recoil'; export const PAGE_LAYOUT_TEST_INSTANCE_ID = @@ -11,22 +16,28 @@ export const PageLayoutTestWrapper = ({ children, initializeState, instanceId: instanceIdFromProps, + store: storeFromProps, }: { children: ReactNode; initializeState?: (snapshot: MutableSnapshot) => void; instanceId?: string; + store?: ReturnType; }) => { const instanceId = instanceIdFromProps ?? PAGE_LAYOUT_TEST_INSTANCE_ID; + const [defaultStore] = useState(() => createStore()); + const store = storeFromProps ?? defaultStore; return ( - - - {children} - - + + + + {children} + + + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx index c3fbb3b36d8..0bd8dfbd4c6 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx @@ -6,7 +6,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { act, renderHook } from '@testing-library/react'; -import { useSetRecoilState } from 'recoil'; +import { useSetAtom } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; import { PageLayoutType, @@ -30,7 +30,7 @@ describe('useCreatePageLayoutGraphWidget', () => { it('should create widget in the correct tab with isolated layouts', () => { const { result } = renderHook( () => { - const setActiveTabId = useSetRecoilState( + const setActiveTabId = useSetAtom( activeTabIdComponentState.atomFamily({ instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, }), @@ -113,7 +113,7 @@ describe('useCreatePageLayoutGraphWidget', () => { pageLayoutDraftComponentState, PAGE_LAYOUT_TEST_INSTANCE_ID, ); - const setActiveTabId = useSetRecoilState( + const setActiveTabId = useSetAtom( activeTabIdComponentState.atomFamily({ instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, }), diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutTab.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutTab.test.tsx index ca4ad4a7d9c..8dd37dc6ed3 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutTab.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutTab.test.tsx @@ -3,9 +3,10 @@ import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pag import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { act, renderHook } from '@testing-library/react'; -import { useSetRecoilState } from 'recoil'; +import { useSetAtom } from 'jotai'; import { PageLayoutType } from '~/generated-metadata/graphql'; import { PAGE_LAYOUT_TEST_INSTANCE_ID, @@ -36,7 +37,7 @@ describe('useCreatePageLayoutTab', () => { pageLayoutCurrentLayoutsComponentState, PAGE_LAYOUT_TEST_INSTANCE_ID, ), - activeTabId: useSetRecoilState( + activeTabId: useSetAtom( activeTabIdComponentState.atomFamily({ instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, }), @@ -174,7 +175,7 @@ describe('useCreatePageLayoutTab', () => { const { result } = renderHook( () => { - const getActiveTabId = useRecoilComponentValue( + const getActiveTabId = useRecoilComponentValueV2( activeTabIdComponentState, `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutHandleLayoutChange.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutHandleLayoutChange.test.tsx index 032d491f840..011bc787503 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutHandleLayoutChange.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutHandleLayoutChange.test.tsx @@ -1,6 +1,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { act, renderHook } from '@testing-library/react'; +import { useSetAtom } from 'jotai'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { usePageLayoutHandleLayoutChange } from '@/page-layout/hooks/usePageLayoutHandleLayoutChange'; import { @@ -17,25 +18,21 @@ describe('usePageLayoutHandleLayoutChange', () => { pageLayoutCurrentLayoutsComponentState, PAGE_LAYOUT_TEST_INSTANCE_ID, ), + setActiveTabId: useSetAtom( + activeTabIdComponentState.atomFamily({ + instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, + }), + ), }), { - wrapper: ({ children }) => ( - { - set( - activeTabIdComponentState.atomFamily({ - instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, - }), - 'tab-1', - ); - }} - > - {children} - - ), + wrapper: PageLayoutTestWrapper, }, ); + act(() => { + result.current.setActiveTabId('tab-1'); + }); + const newLayouts = { desktop: [ { i: 'widget-1', x: 2, y: 3, w: 4, h: 5 }, @@ -52,7 +49,7 @@ describe('usePageLayoutHandleLayoutChange', () => { }); expect(result.current.layouts['tab-1']).toEqual(newLayouts); - expect(result.current.layouts['tab-1']).not.toBe(newLayouts); // Ensure a clone was set, not the same reference + expect(result.current.layouts['tab-1']).not.toBe(newLayouts); expect(result.current.layouts['tab-2']).toBeUndefined(); }); @@ -64,25 +61,21 @@ describe('usePageLayoutHandleLayoutChange', () => { pageLayoutCurrentLayoutsComponentState, PAGE_LAYOUT_TEST_INSTANCE_ID, ), + setActiveTabId: useSetAtom( + activeTabIdComponentState.atomFamily({ + instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, + }), + ), }), { - wrapper: ({ children }) => ( - { - set( - activeTabIdComponentState.atomFamily({ - instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`, - }), - 'tab-1', - ); - }} - > - {children} - - ), + wrapper: PageLayoutTestWrapper, }, ); + act(() => { + result.current.setActiveTabId('tab-1'); + }); + const tab1Layouts = { desktop: [{ i: 'widget-1', x: 0, y: 0, w: 2, h: 2 }], mobile: [{ i: 'widget-1', x: 0, y: 0, w: 1, h: 2 }], @@ -92,8 +85,6 @@ describe('usePageLayoutHandleLayoutChange', () => { result.current.handler.handleLayoutChange([], tab1Layouts); }); - // Unfortunately we can't properly test tab switching with the current renderHook API - // since we can't change the activeTabId after initialization in a simple way expect(result.current.layouts['tab-1']).toEqual(tab1Layouts); }); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutFrontComponentWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutFrontComponentWidget.ts index 53242c3cf65..989c0b986df 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutFrontComponentWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutFrontComponentWidget.ts @@ -12,7 +12,7 @@ import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -26,7 +26,7 @@ export const useCreatePageLayoutFrontComponentWidget = ( pageLayoutIdFromProps, ); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, getTabListInstanceIdFromPageLayoutId(pageLayoutId), ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts index 9589441d7e7..a50ec45aa41 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts @@ -16,6 +16,7 @@ import { getWidgetTitle } from '@/page-layout/utils/getWidgetTitle'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -33,15 +34,11 @@ export const useCreatePageLayoutGraphWidget = ( pageLayoutIdFromProps, ); + const store = useStore(); const { timeZone, calendarStartDay } = useDateTimeFormat(); const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - tabListInstanceId, - ); - const pageLayoutDraftState = useRecoilComponentCallbackState( pageLayoutDraftComponentState, pageLayoutId, @@ -64,7 +61,11 @@ export const useCreatePageLayoutGraphWidget = ( }: { fieldSelection?: GraphWidgetFieldSelection; }): PageLayoutWidget => { - const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); + const activeTabId = store.get( + activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }), + ); if (!isDefined(activeTabId)) { throw new Error( @@ -166,12 +167,13 @@ export const useCreatePageLayoutGraphWidget = ( return newWidget; }, [ - activeTabIdState, + tabListInstanceId, pageLayoutCurrentLayoutsState, pageLayoutDraftState, pageLayoutDraggedAreaState, timeZone, calendarStartDay, + store, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutIframeWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutIframeWidget.ts index b1e0c3d7a0d..c76c10c18e7 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutIframeWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutIframeWidget.ts @@ -12,7 +12,7 @@ import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -26,7 +26,7 @@ export const useCreatePageLayoutIframeWidget = ( pageLayoutIdFromProps, ); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, getTabListInstanceIdFromPageLayoutId(pageLayoutId), ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutStandaloneRichTextWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutStandaloneRichTextWidget.ts index 7e5ba4939aa..1ef3cd26e75 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutStandaloneRichTextWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutStandaloneRichTextWidget.ts @@ -11,6 +11,7 @@ import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -28,10 +29,8 @@ export const useCreatePageLayoutStandaloneRichTextWidget = ( pageLayoutIdFromProps, ); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - getTabListInstanceIdFromPageLayoutId(pageLayoutId), - ); + const store = useStore(); + const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState( pageLayoutCurrentLayoutsComponentState, @@ -51,7 +50,11 @@ export const useCreatePageLayoutStandaloneRichTextWidget = ( const createPageLayoutStandaloneRichTextWidget = useRecoilCallback( ({ snapshot, set }) => (body: RichTextV2Body): PageLayoutWidget => { - const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); + const activeTabId = store.get( + activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }), + ); if (!isDefined(activeTabId)) { throw new Error( @@ -118,10 +121,11 @@ export const useCreatePageLayoutStandaloneRichTextWidget = ( return newWidget; }, [ - activeTabIdState, + tabListInstanceId, pageLayoutCurrentLayoutsState, pageLayoutDraftState, pageLayoutDraggedAreaState, + store, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutTab.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutTab.ts index fd9babe43a9..5adcab7aed0 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutTab.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutTab.ts @@ -7,7 +7,7 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { useRecoilCallback } from 'recoil'; import { v4 as uuidv4 } from 'uuid'; @@ -28,7 +28,7 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => { ); const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, tabListInstanceId, ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useDeletePageLayoutTab.ts b/packages/twenty-front/src/modules/page-layout/hooks/useDeletePageLayoutTab.ts index 392f7eb26c8..8f6f65727ad 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useDeletePageLayoutTab.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useDeletePageLayoutTab.ts @@ -7,6 +7,7 @@ import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => { @@ -25,11 +26,12 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => { pageLayoutId, ); + const store = useStore(); const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - tabListInstanceId, - ); + + const activeTabIdAtom = activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }); const deleteTab = useRecoilCallback( ({ set, snapshot }) => @@ -42,7 +44,7 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => { const sorted = sortTabsByPosition(draft.tabs); const index = sorted.findIndex((t) => t.id === tabId); - const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); + const activeTabId = store.get(activeTabIdAtom); const allLayouts = snapshot .getLoadable(pageLayoutCurrentLayoutsState) @@ -58,10 +60,15 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => { if (activeTabId === tabId) { const neighbor = index > 0 ? sorted[index - 1] : sorted[index + 1]; const nextActiveId = neighbor?.id ?? null; - set(activeTabIdState, nextActiveId); + store.set(activeTabIdAtom, nextActiveId); } }, - [pageLayoutCurrentLayoutsState, pageLayoutDraftState, activeTabIdState], + [ + pageLayoutCurrentLayoutsState, + pageLayoutDraftState, + activeTabIdAtom, + store, + ], ); return { deleteTab }; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts b/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts index b1bf105486d..1e47cc6e735 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts @@ -14,6 +14,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { useRecoilCallback } from 'recoil'; import { appendCopySuffix, isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -35,7 +36,7 @@ export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => { ); const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, tabListInstanceId, ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutHandleLayoutChange.ts b/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutHandleLayoutChange.ts index bdb34b2a968..7bf6b817fdc 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutHandleLayoutChange.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutHandleLayoutChange.ts @@ -3,6 +3,7 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { type Layout, type Layouts } from 'react-grid-layout'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -18,13 +19,9 @@ export const usePageLayoutHandleLayoutChange = ( pageLayoutIdFromProps, ); + const store = useStore(); const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - tabListInstanceId, - ); - const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState( pageLayoutCurrentLayoutsComponentState, pageLayoutId, @@ -38,7 +35,11 @@ export const usePageLayoutHandleLayoutChange = ( const handleLayoutChange = useRecoilCallback( ({ snapshot, set }) => (_: Layout[], allLayouts: Layouts) => { - const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); + const activeTabId = store.get( + activeTabIdComponentState.atomFamily({ + instanceId: tabListInstanceId, + }), + ); if (!isDefined(activeTabId)) return; @@ -84,7 +85,12 @@ export const usePageLayoutHandleLayoutChange = ( })); } }, - [activeTabIdState, pageLayoutCurrentLayoutsState, pageLayoutDraftState], + [ + tabListInstanceId, + pageLayoutCurrentLayoutsState, + pageLayoutDraftState, + store, + ], ); return { handleLayoutChange }; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts b/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts index 6126fe239a7..1edb8a96d8f 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts @@ -7,6 +7,7 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -18,6 +19,7 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ( pageLayoutIdFromProps, ); + const store = useStore(); const tabListComponentInstanceId = getTabListInstanceIdFromPageLayoutId(componentInstanceId); @@ -36,10 +38,9 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ( componentInstanceId, ); - const activeTabIdState = useRecoilComponentCallbackState( - activeTabIdComponentState, - tabListComponentInstanceId, - ); + const activeTabIdAtom = activeTabIdComponentState.atomFamily({ + instanceId: tabListComponentInstanceId, + }); const resetDraftPageLayoutToPersistedPageLayout = useRecoilCallback( ({ set, snapshot }) => @@ -49,9 +50,7 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ( .getValue(); if (isDefined(pageLayoutPersisted)) { - const currentActiveTabId = snapshot - .getLoadable(activeTabIdState) - .getValue(); + const currentActiveTabId = store.get(activeTabIdAtom); const persistedTabIds = pageLayoutPersisted.tabs.map((tab) => tab.id); const isActiveTabInPersistedTabs = @@ -61,7 +60,7 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ( !isActiveTabInPersistedTabs && pageLayoutPersisted.tabs.length > 0 ) { - set(activeTabIdState, pageLayoutPersisted.tabs[0].id); + store.set(activeTabIdAtom, pageLayoutPersisted.tabs[0].id); } set(pageLayoutDraftState, { @@ -80,7 +79,8 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ( pageLayoutDraftState, pageLayoutPersistedState, pageLayoutCurrentLayoutsState, - activeTabIdState, + activeTabIdAtom, + store, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx index 7175c0e1b17..cf3af62fa7a 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx @@ -24,7 +24,7 @@ import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropd import { FieldWidgetInlineCellContainer } from '@/page-layout/widgets/field/components/FieldWidgetInlineCellContainer'; import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; type FieldWidgetInlineCellProps = { loading?: boolean; @@ -36,6 +36,7 @@ export const FieldWidgetInlineCell = ({ instanceIdPrefix, }: FieldWidgetInlineCellProps) => { const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); + const store = useStore(); const { fieldDefinition, recordId, @@ -138,9 +139,7 @@ export const FieldWidgetInlineCell = ({ newValue, skipPersist, }: Parameters[0]) => { - const currentDropdownFocusId = jotaiStore.get( - activeDropdownFocusIdState.atom, - ); + const currentDropdownFocusId = store.get(activeDropdownFocusIdState.atom); const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, @@ -168,6 +167,7 @@ export const FieldWidgetInlineCell = ({ fieldDefinition.fieldMetadataId, persistFieldFromFieldInputContext, scopeInstanceId, + store, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInputContextProvider.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInputContextProvider.tsx index 7616f95d772..4678a3ffa21 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInputContextProvider.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInputContextProvider.tsx @@ -8,7 +8,8 @@ import { RecordFieldComponentInstanceContext } from '@/object-record/record-fiel import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; type FieldWidgetInputContextProviderProps = { children: React.ReactNode; @@ -17,6 +18,7 @@ type FieldWidgetInputContextProviderProps = { export const FieldWidgetInputContextProvider = ({ children, }: FieldWidgetInputContextProviderProps) => { + const store = useStore(); const { closeInlineCell } = useInlineCell(); const instanceId = useAvailableComponentInstanceId( @@ -46,26 +48,23 @@ export const FieldWidgetInputContextProvider = ({ closeInlineCell(); }; - const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback( - ({ snapshot }) => - ({ newValue, event, skipPersist }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside: FieldInputClickOutsideEvent = useCallback( + ({ newValue, event, skipPersist }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId !== instanceId) { - return; - } - event?.preventDefault(); - event?.stopImmediatePropagation(); + if (currentFocusId !== instanceId) { + return; + } + event?.preventDefault(); + event?.stopImmediatePropagation(); - if (skipPersist !== true) { - persistFieldFromFieldInputContext(newValue); - } + if (skipPersist !== true) { + persistFieldFromFieldInputContext(newValue); + } - closeInlineCell(); - }, - [closeInlineCell, instanceId, persistFieldFromFieldInputContext], + closeInlineCell(); + }, + [closeInlineCell, instanceId, persistFieldFromFieldInputContext, store], ); const handleEscape: FieldInputEvent = ({ newValue, skipPersist }) => { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx index b1f8770e20e..46eefe9de8c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx @@ -390,7 +390,10 @@ export const TextFieldWidget: Story = {
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + { const recordFieldInstanceId = useAvailableComponentInstanceIdOrThrow( RecordFieldComponentInstanceContext, ); - const focusStack = useRecoilValue(focusStackState); + const focusStack = useRecoilValueV2(focusStackState); const isEditing = focusStack.some( (item) => diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx index ae2b592f8b9..a40ec5c7d76 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelsContainer.tsx @@ -14,7 +14,7 @@ import { SettingsNewAccountSection } from '@/settings/accounts/components/Settin import { SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID } from '@/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import React from 'react'; const StyledCalenderContainer = styled.div` @@ -22,7 +22,7 @@ const StyledCalenderContainer = styled.div` `; export const SettingsAccountsCalendarChannelsContainer = () => { - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID, ); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx index 2c3debf3f6e..45c74e21bf2 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx @@ -17,7 +17,7 @@ import { settingsAccountsSelectedMessageChannelStateV2 } from '@/settings/accoun import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import React, { useCallback } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -26,7 +26,7 @@ const StyledMessageContainer = styled.div` `; export const SettingsAccountsMessageChannelsContainer = () => { - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_ACCOUNT_MESSAGE_CHANNELS_TAB_LIST_COMPONENT_ID, ); diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx index e3858081902..68e3c93cb7d 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx @@ -19,7 +19,7 @@ import { SettingsAdminTableCard } from '@/settings/admin-panel/components/Settin import { SettingsAdminVersionContainer } from '@/settings/admin-panel/components/SettingsAdminVersionContainer'; import { SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminUserLookupWorkspaceTabsId'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2'; import { getImageAbsoluteURI, isDefined } from 'twenty-shared/utils'; import { H2Title, @@ -42,7 +42,7 @@ export const SettingsAdminGeneral = () => { const [userIdentifier, setUserIdentifier] = useState(''); const { enqueueErrorSnackBar } = useSnackBar(); - const [activeTabId, setActiveTabId] = useRecoilComponentState( + const [activeTabId, setActiveTabId] = useRecoilComponentStateV2( activeTabIdComponentState, SETTINGS_ADMIN_USER_LOOKUP_WORKSPACE_TABS_ID, ); diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminTabContent.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminTabContent.tsx index e60d5492453..e0034b3ecc1 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminTabContent.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminTabContent.tsx @@ -4,10 +4,10 @@ import { SETTINGS_ADMIN_TABS } from '@/settings/admin-panel/constants/SettingsAd import { SETTINGS_ADMIN_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminTabsId'; import { SettingsAdminHealthStatus } from '@/settings/admin-panel/health-status/components/SettingsAdminHealthStatus'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; export const SettingsAdminTabContent = () => { - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_ADMIN_TABS_ID, ); diff --git a/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx index ceb89c27a87..2af72fb6112 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx @@ -18,7 +18,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { IconBox, useIcons, type IconComponent } from 'twenty-ui/display'; @@ -144,7 +144,7 @@ export const SettingsMorphRelationMultiSelect = ({ const selectableItemIdArray = filteredOptions.map((option) => option.label); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx b/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx index 45491984a03..d9d3353bfef 100644 --- a/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx +++ b/packages/twenty-front/src/modules/settings/developers/components/WebhookEntitySelect.tsx @@ -10,7 +10,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; @@ -86,7 +86,7 @@ export const WebhookEntitySelect = ({ const { getIcon } = useIcons(); const { closeDropdown } = useCloseDropdown(); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx b/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx index 18e09f939e5..9acfe2f05a7 100644 --- a/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx +++ b/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionCodeEditorTab.tsx @@ -5,7 +5,7 @@ import { import { SETTINGS_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/settings/logic-functions/constants/SettingsLogicFunctionTabListComponentId'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { H2Title, IconPlayerPlay } from 'twenty-ui/display'; @@ -27,7 +27,7 @@ export const SettingsLogicFunctionCodeEditorTab = ({ onChange: (value: string) => void; isTesting?: boolean; }) => { - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID, ); diff --git a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesQueryEffect.tsx b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesQueryEffect.tsx index ef25d6d3084..8861277de7d 100644 --- a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesQueryEffect.tsx +++ b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesQueryEffect.tsx @@ -7,7 +7,7 @@ import { type RoleWithPartialMembers } from '@/settings/roles/types/RoleWithPart import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useEffect } from 'react'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { useGetRolesQuery } from '~/generated-metadata/graphql'; @@ -22,12 +22,14 @@ export const SettingsRolesQueryEffect = () => { settingsRolesIsLoadingStateV2, ); + const store = useStore(); + const populateRoles = useRecoilCallback( ({ set, snapshot }) => (roles: RoleWithPartialMembers[]) => { const roleIds = roles.map((role) => role.id); set(settingsRoleIdsState, roleIds); - jotaiStore.set(settingsRoleIdsStateV2.atom, roleIds); + store.set(settingsRoleIdsStateV2.atom, roleIds); roles.forEach((role) => { const persistedRole = getSnapshotValue( snapshot, @@ -50,7 +52,7 @@ export const SettingsRolesQueryEffect = () => { } }); }, - [], + [store], ); useEffect(() => { diff --git a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx index fa0eefddb5a..2372fb46deb 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/SettingsRoleAssignment.tsx @@ -10,7 +10,7 @@ import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRol import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useState } from 'react'; import { useRecoilValue } from 'recoil'; @@ -85,7 +85,7 @@ export const SettingsRoleAssignment = ({ setSelectRoleTarget(null); }; - const isModalOpened = useRecoilComponentValue( + const isModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, ROLE_ASSIGNMENT_CONFIRMATION_MODAL_ID, ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx index 48daf309a55..5ca85d1f767 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable.tsx @@ -14,6 +14,7 @@ import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableHeaderText } from '@/ui/layout/table/components/TableHeaderText'; import { sortedFieldByTableFamilyState } from '@/ui/layout/table/states/sortedFieldByTableFamilyState'; +import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useState } from 'react'; @@ -45,10 +46,11 @@ export const SettingsRolePermissionsObjectLevelObjectFieldPermissionTable = ({ const { t } = useLingui(); const [searchTerm, setSearchTerm] = useState(''); - const sortedFieldByTable = useRecoilValue( - sortedFieldByTableFamilyState({ + const sortedFieldByTable = useFamilyRecoilValueV2( + sortedFieldByTableFamilyState, + { tableId: SETTINGS_ROLE_PERMISSION_OBJECT_LEVEL_FIELD_PERMISSION_TABLE_ID, - }), + }, ); const searchedFields = objectMetadataItem.fields.filter((fieldMetadataItem) => diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectSubFieldMenu.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectSubFieldMenu.tsx index 5818bd0fc49..76c143d8dc8 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectSubFieldMenu.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectSubFieldMenu.tsx @@ -29,6 +29,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; type SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectSubFieldMenuProps = { @@ -88,7 +89,7 @@ export const SettingsRolePermissionsObjectLevelRecordLevelPermissionFieldSelectS const { advancedFilterFieldSelectDropdownId } = useAdvancedFilterFieldSelectDropdown(recordFilterId); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, advancedFilterFieldSelectDropdownId, ); diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx index 9fcb29fa391..31bec0e3467 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRole.tsx @@ -14,7 +14,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser'; import { t } from '@lingui/core/macro'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; @@ -35,7 +35,7 @@ type SettingsRoleProps = { export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => { const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems(); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx index 1e3fe34aa22..106df004abb 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleCreateEffect.tsx @@ -2,7 +2,7 @@ import { SETTINGS_ROLE_DETAIL_TABS } from '@/settings/roles/role/constants/Setti import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { t } from '@lingui/core/macro'; import { useEffect, useState } from 'react'; import { useSetRecoilState } from 'recoil'; @@ -22,7 +22,7 @@ export const SettingsRoleCreateEffect = ({ settingsPersistedRoleFamilyState(roleId), ); - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); diff --git a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx index 6a9e6eb8d1f..c77a0c4bb39 100644 --- a/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role/components/SettingsRoleEditEffect.tsx @@ -3,7 +3,7 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState'; import { type RoleWithPartialMembers } from '@/settings/roles/types/RoleWithPartialMembers'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useEffect, useState } from 'react'; import { useRecoilCallback, useRecoilValue } from 'recoil'; @@ -20,7 +20,7 @@ export const SettingsRoleEditEffect = ({ const [isInitialized, setIsInitialized] = useState(false); const role = useRecoilValue(settingsPersistedRoleFamilyState(roleId)); - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, ); diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectHeader.stories.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectHeader.stories.tsx index b4c2665335a..452707399e7 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectHeader.stories.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectHeader.stories.tsx @@ -10,6 +10,8 @@ import { SelectHeaderStep } from '@/spreadsheet-import/steps/components/SelectHe import { SpreadsheetImportStepType } from '@/spreadsheet-import/steps/types/SpreadsheetImportStepType'; import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { Provider as JotaiProvider } from 'jotai'; import { RecoilRoot } from 'recoil'; const meta: Meta = { @@ -19,20 +21,21 @@ const meta: Meta = { layout: 'fullscreen', }, decorators: [ - (Story) => ( - { - set( - isModalOpenedComponentState.atomFamily({ - instanceId: 'select-header-step', - }), - true, - ); - }} - > - - - ), + (Story) => { + jotaiStore.set( + isModalOpenedComponentState.atomFamily({ + instanceId: 'select-header-step', + }), + true, + ); + return ( + + + + + + ); + }, ], }; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectSheet.stories.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectSheet.stories.tsx index 620702b69c1..e7266f70e27 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectSheet.stories.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/SelectSheet.stories.tsx @@ -7,6 +7,8 @@ import { SelectSheetStep } from '@/spreadsheet-import/steps/components/SelectShe import { SpreadsheetImportStepType } from '@/spreadsheet-import/steps/types/SpreadsheetImportStepType'; import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { Provider as JotaiProvider } from 'jotai'; import { RecoilRoot } from 'recoil'; const meta: Meta = { @@ -16,20 +18,21 @@ const meta: Meta = { layout: 'fullscreen', }, decorators: [ - (Story) => ( - { - set( - isModalOpenedComponentState.atomFamily({ - instanceId: 'select-sheet-step', - }), - true, - ); - }} - > - - - ), + (Story) => { + jotaiStore.set( + isModalOpenedComponentState.atomFamily({ + instanceId: 'select-sheet-step', + }), + true, + ); + return ( + + + + + + ); + }, ], }; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Upload.stories.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Upload.stories.tsx index 921e6e95d68..b367e7f5d16 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Upload.stories.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Upload.stories.tsx @@ -7,6 +7,8 @@ import { UploadStep } from '@/spreadsheet-import/steps/components/UploadStep/Upl import { SpreadsheetImportStepType } from '@/spreadsheet-import/steps/types/SpreadsheetImportStepType'; import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { Provider as JotaiProvider } from 'jotai'; import { RecoilRoot } from 'recoil'; import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; @@ -21,20 +23,21 @@ const meta: Meta = { decorators: [ ObjectMetadataItemsDecorator, ContextStoreDecorator, - (Story) => ( - { - set( - isModalOpenedComponentState.atomFamily({ - instanceId: 'upload-step', - }), - true, - ); - }} - > - - - ), + (Story) => { + jotaiStore.set( + isModalOpenedComponentState.atomFamily({ + instanceId: 'upload-step', + }), + true, + ); + return ( + + + + + + ); + }, SnackBarDecorator, ], }; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Validation.stories.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Validation.stories.tsx index da75cda2adb..c4fcd89a4c5 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Validation.stories.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/__stories__/Validation.stories.tsx @@ -10,6 +10,8 @@ import { SpreadSheetImportModalWrapper } from '@/spreadsheet-import/components/S import { ValidationStep } from '@/spreadsheet-import/steps/components/ValidationStep/ValidationStep'; import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { Provider as JotaiProvider } from 'jotai'; import { RecoilRoot } from 'recoil'; const meta: Meta = { @@ -19,20 +21,21 @@ const meta: Meta = { layout: 'fullscreen', }, decorators: [ - (Story) => ( - { - set( - isModalOpenedComponentState.atomFamily({ - instanceId: 'validation-step', - }), - true, - ); - }} - > - - - ), + (Story) => { + jotaiStore.set( + isModalOpenedComponentState.atomFamily({ + instanceId: 'validation-step', + }), + true, + ); + return ( + + + + + + ); + }, ], }; diff --git a/packages/twenty-front/src/modules/sse-db-event/components/ListenRecordUpdatesEffect.tsx b/packages/twenty-front/src/modules/sse-db-event/components/ListenRecordUpdatesEffect.tsx index 79368ac2188..de56ae6e6e0 100644 --- a/packages/twenty-front/src/modules/sse-db-event/components/ListenRecordUpdatesEffect.tsx +++ b/packages/twenty-front/src/modules/sse-db-event/components/ListenRecordUpdatesEffect.tsx @@ -12,7 +12,7 @@ import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useU import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyStateV2 } from '@/object-record/record-store/states/recordStoreFamilyStateV2'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useOnDbEvent } from '@/sse-db-event/hooks/useOnDbEvent'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -46,13 +46,15 @@ export const ListenRecordUpdatesEffect = ({ objectNameSingular, }); + const store = useStore(); + const setRecordInStore = useRecoilCallback( ({ set }) => (record: ObjectRecord) => { set(recordStoreFamilyState(record.id), record); - jotaiStore.set(recordStoreFamilyStateV2.atomFamily(record.id), record); + store.set(recordStoreFamilyStateV2.atomFamily(record.id), record); }, - [], + [store], ); const { upsertRecordsInStore } = useUpsertRecordsInStore(); diff --git a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx index e37308e7dcc..e787045d56f 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/DateInput.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents'; import { DatePicker } from '@/ui/input/components/internal/date/components/DatePicker'; @@ -8,7 +8,7 @@ import { } from '@/ui/input/components/internal/date/components/DateTimePicker'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { type Nullable } from 'twenty-ui/utilities'; export type DateInputProps = { @@ -39,6 +39,8 @@ export const DateInput = ({ onSubmit, hideHeaderInput, }: DateInputProps) => { + const store = useStore(); + const [internalValue, setInternalValue] = useState(value); const wrapperRef = useRef(null); @@ -75,25 +77,23 @@ export const DateInput = ({ onEscape(internalValue); }; - const handleClickOutside = useRecoilCallback( - ({ snapshot }) => - (event: MouseEvent | TouchEvent) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside = useCallback( + (event: MouseEvent | TouchEvent) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId === instanceId) { - closeDropdownYearSelect(MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID); - closeDropdownMonthSelect(MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID); - onClickOutside(event, internalValue); - } - }, + if (currentFocusId === instanceId) { + closeDropdownYearSelect(MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID); + closeDropdownMonthSelect(MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID); + onClickOutside(event, internalValue); + } + }, [ instanceId, closeDropdownYearSelect, closeDropdownMonthSelect, onClickOutside, internalValue, + store, ], ); diff --git a/packages/twenty-front/src/modules/ui/field/input/components/DateTimeInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/DateTimeInput.tsx index c66f662d0f7..41357b80f27 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/DateTimeInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/DateTimeInput.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents'; import { @@ -9,7 +9,7 @@ import { import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { type Temporal } from 'temporal-polyfill'; import { type Nullable } from 'twenty-ui/utilities'; @@ -41,6 +41,8 @@ export const DateTimeInput = ({ onSubmit, hideHeaderInput, }: DateTimeInputProps) => { + const store = useStore(); + const [internalValue, setInternalValue] = useState(value); const { userTimezone } = useUserTimezone(); @@ -78,25 +80,23 @@ export const DateTimeInput = ({ onEscape(internalValue); }; - const handleClickOutside = useRecoilCallback( - ({ snapshot }) => - (event: MouseEvent | TouchEvent) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); + const handleClickOutside = useCallback( + (event: MouseEvent | TouchEvent) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); - if (currentFocusId === instanceId) { - closeDropdownYearSelect(MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID); - closeDropdownMonthSelect(MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID); - onClickOutside(event, internalValue); - } - }, + if (currentFocusId === instanceId) { + closeDropdownYearSelect(MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID); + closeDropdownMonthSelect(MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID); + onClickOutside(event, internalValue); + } + }, [ instanceId, closeDropdownYearSelect, closeDropdownMonthSelect, onClickOutside, internalValue, + store, ], ); diff --git a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx index 3d601681bfa..bc22a9eb192 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/MultiSelectInput.tsx @@ -14,7 +14,7 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { isDefined } from 'twenty-shared/utils'; import { type SelectOption } from 'twenty-ui/input'; @@ -47,7 +47,7 @@ export const MultiSelectInput = ({ selectableListComponentInstanceId, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, selectableListComponentInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx b/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx index 408c00f846e..8eeb74bd6fd 100644 --- a/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx @@ -17,7 +17,7 @@ import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useRecoilValue, useResetRecoilState } from 'recoil'; import { IconApps, type IconComponent, useIcons } from 'twenty-ui/display'; @@ -99,7 +99,7 @@ const IconPickerIcon = ({ Icon, focusedIconKey, }: IconPickerIconProps) => { - const isSelectedItemId = useRecoilComponentValue( + const isSelectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, iconKey, ); @@ -242,7 +242,7 @@ export const IconPicker = ({ const selectableListInstanceId = 'icon-list'; const focusedIconKey = - useRecoilComponentValue( + useRecoilComponentValueV2( selectedItemIdComponentState, selectableListInstanceId, ) ?? undefined; diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index 79b3c8c2742..482d5a0f69c 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -16,7 +16,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { type IconComponent } from 'twenty-ui/display'; @@ -140,7 +140,7 @@ export const Select = ({ const selectableItemIdArray = filteredOptions.map((option) => option.label); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx index 552e619abc1..198ae0e5e45 100644 --- a/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/SelectInput.tsx @@ -8,7 +8,7 @@ import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-l import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useEffect, useMemo, useRef, useState } from 'react'; import { t } from '@lingui/core/macro'; import { isDefined } from 'twenty-shared/utils'; @@ -46,7 +46,7 @@ export const SelectInput = ({ SelectableListComponentInstanceContext, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, selectableListInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown.ts index f0e020db1e2..ce42ccbfaef 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown.ts @@ -4,7 +4,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; export const useCloseAnyOpenDropdown = () => { @@ -13,14 +13,14 @@ export const useCloseAnyOpenDropdown = () => { const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); + const store = useStore(); + const closeAnyOpenDropdown = useCallback(() => { - const previousDropdownFocusId = jotaiStore.get( + const previousDropdownFocusId = store.get( previousDropdownFocusIdState.atom, ); - const activeDropdownFocusId = jotaiStore.get( - activeDropdownFocusIdState.atom, - ); + const activeDropdownFocusId = store.get(activeDropdownFocusIdState.atom); const thereIsNoDropdownOpen = !isDefined(activeDropdownFocusId) && !isDefined(previousDropdownFocusId); @@ -45,9 +45,9 @@ export const useCloseAnyOpenDropdown = () => { }); } - jotaiStore.set(previousDropdownFocusIdState.atom, null); - jotaiStore.set(activeDropdownFocusIdState.atom, null); - }, [closeDropdown, removeFocusItemFromFocusStackById]); + store.set(previousDropdownFocusIdState.atom, null); + store.set(activeDropdownFocusIdState.atom, null); + }, [closeDropdown, removeFocusItemFromFocusStackById, store]); return { closeAnyOpenDropdown }; }; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseDropdown.ts index d002b6b3948..c37d5bf1b57 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useCloseDropdown.ts @@ -6,7 +6,7 @@ import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDrop import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; export const useCloseDropdown = () => { @@ -16,6 +16,8 @@ export const useCloseDropdown = () => { const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); + const store = useStore(); + const dropdownComponentInstanceIdFromContext = useAvailableComponentInstanceId(DropdownComponentInstanceContext); @@ -29,7 +31,7 @@ export const useCloseDropdown = () => { throw new Error('Dropdown component instance ID is not defined'); } - const isDropdownOpen = jotaiStore.get( + const isDropdownOpen = store.get( isDropdownOpenComponentState.atomFamily({ instanceId: dropdownComponentInstanceId, }), @@ -42,7 +44,7 @@ export const useCloseDropdown = () => { goBackToPreviousDropdownFocusId(); - jotaiStore.set( + store.set( isDropdownOpenComponentState.atomFamily({ instanceId: dropdownComponentInstanceId, }), @@ -54,6 +56,7 @@ export const useCloseDropdown = () => { removeFocusItemFromFocusStackById, goBackToPreviousDropdownFocusId, dropdownComponentInstanceIdFromContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId.ts index bb82d2689b5..2dde02bdcdc 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId.ts @@ -2,21 +2,20 @@ import { useCallback } from 'react'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; // TODO: this won't work for more than 1 nested dropdown export const useGoBackToPreviousDropdownFocusId = () => { + const store = useStore(); + const goBackToPreviousDropdownFocusId = useCallback(() => { - const previouslyFocusedDropdownId = jotaiStore.get( + const previouslyFocusedDropdownId = store.get( previousDropdownFocusIdState.atom, ); - jotaiStore.set( - activeDropdownFocusIdState.atom, - previouslyFocusedDropdownId, - ); - jotaiStore.set(previousDropdownFocusIdState.atom, null); - }, []); + store.set(activeDropdownFocusIdState.atom, previouslyFocusedDropdownId); + store.set(previousDropdownFocusIdState.atom, null); + }, [store]); return { goBackToPreviousDropdownFocusId, diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useOpenDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useOpenDropdown.ts index 2631c4ce86f..6f56194baa3 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useOpenDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useOpenDropdown.ts @@ -8,7 +8,7 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; type OpenDropdownArgs = { @@ -21,6 +21,8 @@ export const useOpenDropdown = () => { const { setActiveDropdownFocusIdAndMemorizePrevious } = useSetActiveDropdownFocusIdAndMemorizePrevious(); + const store = useStore(); + const dropdownComponentInstanceIdFromContext = useAvailableComponentInstanceId(DropdownComponentInstanceContext); @@ -34,7 +36,7 @@ export const useOpenDropdown = () => { throw new Error('Dropdown component instance ID is not defined'); } - jotaiStore.set( + store.set( isDropdownOpenComponentState.atomFamily({ instanceId: dropdownComponentInstanceId, }), @@ -63,6 +65,7 @@ export const useOpenDropdown = () => { pushFocusItemToFocusStack, setActiveDropdownFocusIdAndMemorizePrevious, dropdownComponentInstanceIdFromContext, + store, ], ); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious.ts index 53b000deff5..15da4c07d90 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious.ts @@ -2,23 +2,23 @@ import { useCallback } from 'react'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; export const useSetActiveDropdownFocusIdAndMemorizePrevious = () => { + const store = useStore(); + const setActiveDropdownFocusIdAndMemorizePrevious = useCallback( (dropdownId: string | null) => { - const activeDropdownFocusId = jotaiStore.get( - activeDropdownFocusIdState.atom, - ); + const activeDropdownFocusId = store.get(activeDropdownFocusIdState.atom); if (activeDropdownFocusId === dropdownId) { return; } - jotaiStore.set(previousDropdownFocusIdState.atom, activeDropdownFocusId); - jotaiStore.set(activeDropdownFocusIdState.atom, dropdownId); + store.set(previousDropdownFocusIdState.atom, activeDropdownFocusId); + store.set(activeDropdownFocusIdState.atom, dropdownId); }, - [], + [store], ); return { diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useToggleDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useToggleDropdown.ts index d58b5cb58d7..ae1a9fd5b61 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useToggleDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useToggleDropdown.ts @@ -6,7 +6,7 @@ import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig'; import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; type ToggleDropdownArgs = { @@ -21,6 +21,8 @@ export const useToggleDropdown = () => { const { openDropdown } = useOpenDropdown(); const { closeDropdown } = useCloseDropdown(); + const store = useStore(); + const toggleDropdown = useCallback( (args?: ToggleDropdownArgs | null | undefined) => { const dropdownComponentInstanceId = @@ -31,7 +33,7 @@ export const useToggleDropdown = () => { throw new Error('Dropdown component instance ID is not defined'); } - const isDropdownOpen = jotaiStore.get( + const isDropdownOpen = store.get( isDropdownOpenComponentState.atomFamily({ instanceId: dropdownComponentInstanceId, }), @@ -46,7 +48,12 @@ export const useToggleDropdown = () => { }); } }, - [closeDropdown, openDropdown, dropdownComponentInstanceIdFromContext], + [ + closeDropdown, + openDropdown, + dropdownComponentInstanceIdFromContext, + store, + ], ); return { diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx index 8877d4ab0ea..65cfc6869a1 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx @@ -9,7 +9,7 @@ import { MODAL_CLICK_OUTSIDE_LISTENER_EXCLUDED_ID } from '@/ui/layout/modal/cons import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { AnimatePresence, motion } from 'framer-motion'; @@ -243,7 +243,7 @@ export const Modal = ({ e.stopPropagation(); }; - const isModalOpened = useRecoilComponentValue( + const isModalOpened = useRecoilComponentValueV2( isModalOpenedComponentState, modalId, ); diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx index 99f569e0082..7e9476aeefc 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx @@ -1,24 +1,27 @@ -import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { + type Decorator, + type Meta, + type StoryObj, +} from '@storybook/react-vite'; import { expect, fn, userEvent, waitFor, within } from 'storybook/test'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { type SetRecoilState } from 'recoil'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; import { ComponentDecorator } from 'twenty-ui/testing'; import { RootDecorator } from '~/testing/decorators/RootDecorator'; import { sleep } from '~/utils/sleep'; -const initializeState = ({ set }: { set: SetRecoilState }) => { - set( +const JotaiInitDecorator: Decorator = (Story) => { + jotaiStore.set( isModalOpenedComponentState.atomFamily({ instanceId: 'confirmation-modal', }), true, ); - - set(focusStackState, [ + jotaiStore.set(focusStackState.atom, [ { focusId: 'confirmation-modal', componentInstance: { @@ -31,14 +34,14 @@ const initializeState = ({ set }: { set: SetRecoilState }) => { }, }, ]); + return ; }; const meta: Meta = { title: 'UI/Layout/Modal/ConfirmationModal', component: ConfirmationModal, - decorators: [RootDecorator, ComponentDecorator], + decorators: [JotaiInitDecorator, RootDecorator, ComponentDecorator], parameters: { - initializeState, disableHotkeyInitialization: true, }, }; diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx index 378d8aad7f4..6257e3876a0 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx @@ -1,24 +1,27 @@ -import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { + type Decorator, + type Meta, + type StoryObj, +} from '@storybook/react-vite'; import { expect, fn, userEvent, waitFor, within } from 'storybook/test'; import { Modal } from '@/ui/layout/modal/components/Modal'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { type SetRecoilState } from 'recoil'; +import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; import { ComponentDecorator } from 'twenty-ui/testing'; import { RootDecorator } from '~/testing/decorators/RootDecorator'; import { sleep } from '~/utils/sleep'; -const initializeState = ({ set }: { set: SetRecoilState }) => { - set( +const JotaiInitDecorator: Decorator = (Story) => { + jotaiStore.set( isModalOpenedComponentState.atomFamily({ instanceId: 'modal-id', }), true, ); - - set(focusStackState, [ + jotaiStore.set(focusStackState.atom, [ { focusId: 'modal-id', componentInstance: { @@ -31,14 +34,14 @@ const initializeState = ({ set }: { set: SetRecoilState }) => { }, }, ]); + return ; }; const meta: Meta = { title: 'UI/Layout/Modal/Modal', component: Modal, - decorators: [RootDecorator, ComponentDecorator], + decorators: [JotaiInitDecorator, RootDecorator, ComponentDecorator], parameters: { - initializeState, disableHotkeyInitialization: true, }, }; diff --git a/packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.ts b/packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.tsx similarity index 64% rename from packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.ts rename to packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.tsx index dc8aa69cf56..555bb6521ba 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.ts +++ b/packages/twenty-front/src/modules/ui/layout/modal/hooks/__tests__/useModal.spec.tsx @@ -1,28 +1,39 @@ import { renderHook } from '@testing-library/react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; +import { createStore, Provider } from 'jotai'; +import { type ReactNode, act } from 'react'; +import { RecoilRoot } from 'recoil'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; -import { act } from 'react'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; const modalId = 'test-modal-id'; -describe('useModal', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); +const createTestWrapper = () => { + const store = createStore(); + const TestWrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ); + + return TestWrapper; +}; + +describe('useModal', () => { it('should open a modal', () => { const { result } = renderHook( () => { const modal = useModal(); - const isModalOpened = useRecoilValue( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + const isModalOpened = useRecoilComponentValueV2( + isModalOpenedComponentState, + modalId, ); return { modal, isModalOpened }; }, { - wrapper: RecoilRoot, + wrapper: createTestWrapper(), }, ); @@ -37,13 +48,14 @@ describe('useModal', () => { const { result } = renderHook( () => { const modal = useModal(); - const isModalOpened = useRecoilValue( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + const isModalOpened = useRecoilComponentValueV2( + isModalOpenedComponentState, + modalId, ); return { modal, isModalOpened }; }, { - wrapper: RecoilRoot, + wrapper: createTestWrapper(), }, ); @@ -63,13 +75,14 @@ describe('useModal', () => { const { result } = renderHook( () => { const modal = useModal(); - const isModalOpened = useRecoilValue( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + const isModalOpened = useRecoilComponentValueV2( + isModalOpenedComponentState, + modalId, ); return { modal, isModalOpened }; }, { - wrapper: RecoilRoot, + wrapper: createTestWrapper(), }, ); @@ -86,13 +99,14 @@ describe('useModal', () => { const { result } = renderHook( () => { const modal = useModal(); - const isModalOpened = useRecoilValue( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + const isModalOpened = useRecoilComponentValueV2( + isModalOpenedComponentState, + modalId, ); return { modal, isModalOpened }; }, { - wrapper: RecoilRoot, + wrapper: createTestWrapper(), }, ); diff --git a/packages/twenty-front/src/modules/ui/layout/modal/hooks/useModal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/hooks/useModal.tsx index 767e86e47d6..c53dc77f20f 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/hooks/useModal.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/hooks/useModal.tsx @@ -1,88 +1,83 @@ +import { useCallback } from 'react'; +import { useStore } from 'jotai'; + import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { useRecoilCallback } from 'recoil'; export const useModal = () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); - const closeModal = useRecoilCallback( - ({ set, snapshot }) => - (modalId: string) => { - const isModalOpen = snapshot - .getLoadable( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), - ) - .getValue(); + const store = useStore(); - if (!isModalOpen) { - return; - } + const closeModal = useCallback( + (modalId: string) => { + const isModalOpen = store.get( + isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + ); - removeFocusItemFromFocusStackById({ - focusId: modalId, - }); + if (!isModalOpen) { + return; + } - set( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), - false, - ); - }, - [removeFocusItemFromFocusStackById], + removeFocusItemFromFocusStackById({ + focusId: modalId, + }); + + store.set( + isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + false, + ); + }, + [store, removeFocusItemFromFocusStackById], ); - const openModal = useRecoilCallback( - ({ set, snapshot }) => - (modalId: string) => { - const isModalOpened = snapshot - .getLoadable( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), - ) - .getValue(); + const openModal = useCallback( + (modalId: string) => { + const isModalOpened = store.get( + isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + ); - if (isModalOpened) { - return; - } + if (isModalOpened) { + return; + } - set( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), - true, - ); + store.set( + isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + true, + ); - pushFocusItemToFocusStack({ - focusId: modalId, - component: { - type: FocusComponentType.MODAL, - instanceId: modalId, - }, - globalHotkeysConfig: { - enableGlobalHotkeysWithModifiers: false, - enableGlobalHotkeysConflictingWithKeyboard: false, - }, - }); - }, - [pushFocusItemToFocusStack], + pushFocusItemToFocusStack({ + focusId: modalId, + component: { + type: FocusComponentType.MODAL, + instanceId: modalId, + }, + globalHotkeysConfig: { + enableGlobalHotkeysWithModifiers: false, + enableGlobalHotkeysConflictingWithKeyboard: false, + }, + }); + }, + [store, pushFocusItemToFocusStack], ); - const toggleModal = useRecoilCallback( - ({ snapshot }) => - (modalId: string) => { - const isModalOpen = snapshot - .getLoadable( - isModalOpenedComponentState.atomFamily({ instanceId: modalId }), - ) - .getValue(); + const toggleModal = useCallback( + (modalId: string) => { + const isModalOpen = store.get( + isModalOpenedComponentState.atomFamily({ instanceId: modalId }), + ); - if (isModalOpen) { - closeModal(modalId); - } else { - openModal(modalId); - } - }, - [closeModal, openModal], + if (isModalOpen) { + closeModal(modalId); + } else { + openModal(modalId); + } + }, + [store, closeModal, openModal], ); return { diff --git a/packages/twenty-front/src/modules/ui/layout/modal/states/isModalOpenedComponentState.ts b/packages/twenty-front/src/modules/ui/layout/modal/states/isModalOpenedComponentState.ts index d9ccb1056e4..3bd62e07619 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/states/isModalOpenedComponentState.ts +++ b/packages/twenty-front/src/modules/ui/layout/modal/states/isModalOpenedComponentState.ts @@ -1,7 +1,7 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; import { ModalComponentInstanceContext } from '@/ui/layout/modal/contexts/ModalComponentInstanceContext'; -export const isModalOpenedComponentState = createComponentState({ +export const isModalOpenedComponentState = createComponentStateV2({ key: 'isModalOpenedComponentState', defaultValue: false, componentInstanceContext: ModalComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx index 3354138f4e1..63f3e860cf3 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableList.tsx @@ -4,7 +4,7 @@ import { useSelectableListHotKeys } from '@/ui/layout/selectable-list/hooks/inte import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; import { SelectableListContextProvider } from '@/ui/layout/selectable-list/states/contexts/SelectableListContext'; import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { isDefined } from 'twenty-shared/utils'; import { arrayToChunks } from '~/utils/array/arrayToChunks'; @@ -27,7 +27,7 @@ export const SelectableList = ({ }: SelectableListProps) => { useSelectableListHotKeys(selectableListInstanceId, focusId, onSelect); - const setSelectableItemIds = useSetRecoilComponentState( + const setSelectableItemIds = useSetRecoilComponentStateV2( selectableItemIdsComponentState, selectableListInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx index d0ad445fac4..84ed1e0d67e 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/components/SelectableListItem.tsx @@ -1,8 +1,8 @@ import { type ReactNode, useEffect, useRef } from 'react'; import { SelectableListItemHotkeyEffect } from '@/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import styled from '@emotion/styled'; import { isDefined } from 'twenty-shared/utils'; @@ -25,8 +25,8 @@ export const SelectableListItem = ({ children, onEnter, }: SelectableListItemProps) => { - const isSelectedItemId = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItemId = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, itemId, ); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/__tests__/useSelectableList.test.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/__tests__/useSelectableList.test.ts index 0c42834a2b0..dce72fc2423 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/__tests__/useSelectableList.test.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/__tests__/useSelectableList.test.ts @@ -1,12 +1,12 @@ import { renderHook } from '@testing-library/react'; import { act } from 'react'; -import { RecoilRoot } from 'recoil'; +import { Provider } from 'jotai'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; const selectableListComponentInstanceId = 'testId'; const testArr = [['1'], ['2'], ['3']]; @@ -16,12 +16,12 @@ describe('useSelectableList', () => { it('Should setSelectableItemIds', async () => { const { result } = renderHook( () => { - const setSelectableItemIds = useSetRecoilComponentState( + const setSelectableItemIds = useSetRecoilComponentStateV2( selectableItemIdsComponentState, selectableListComponentInstanceId, ); - const selectableItemIds = useRecoilComponentValue( + const selectableItemIds = useRecoilComponentValueV2( selectableItemIdsComponentState, selectableListComponentInstanceId, ); @@ -32,7 +32,7 @@ describe('useSelectableList', () => { }; }, { - wrapper: RecoilRoot, + wrapper: Provider, }, ); @@ -52,11 +52,11 @@ describe('useSelectableList', () => { selectableListComponentInstanceId, ); - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, selectableListComponentInstanceId, ); - const setSelectedItemId = useSetRecoilComponentState( + const setSelectedItemId = useSetRecoilComponentStateV2( selectedItemIdComponentState, selectableListComponentInstanceId, ); @@ -67,7 +67,7 @@ describe('useSelectableList', () => { }; }, { - wrapper: RecoilRoot, + wrapper: Provider, }, ); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts index f96a4159317..52437bcb6cf 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys.ts @@ -1,12 +1,12 @@ import { isNonEmptyString } from '@sniptt/guards'; -import { useRecoilCallback } from 'recoil'; +import { useCallback } from 'react'; +import { useStore } from 'jotai'; import { Key } from 'ts-key-enum'; import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; type Direction = 'up' | 'down' | 'left' | 'right'; @@ -31,110 +31,109 @@ export const useSelectableListHotKeys = ( } }; - const handleSelect = useRecoilCallback( - ({ snapshot, set }) => - (direction: Direction) => { - const selectedItemId = getSnapshotValue( - snapshot, - selectedItemIdComponentState.atomFamily({ - instanceId: instanceId, - }), - ); - const selectableItemIds = getSnapshotValue( - snapshot, - selectableItemIdsComponentState.atomFamily({ - instanceId: instanceId, - }), - ); + const store = useStore(); - const currentPosition = findPosition(selectableItemIds, selectedItemId); + const handleSelect = useCallback( + (direction: Direction) => { + const selectedItemId = store.get( + selectedItemIdComponentState.atomFamily({ + instanceId: instanceId, + }), + ); + const selectableItemIds = store.get( + selectableItemIdsComponentState.atomFamily({ + instanceId: instanceId, + }), + ); - const computeNextId = (direction: Direction) => { - if ( - selectableItemIds.length === 0 || - selectableItemIds[0]?.length === 0 - ) { - return; - } + const currentPosition = findPosition(selectableItemIds, selectedItemId); - if (!selectedItemId || !currentPosition) { - return selectableItemIds[0][0]; - } - - const { row: currentRow, col: currentCol } = currentPosition; - - const isSingleRow = selectableItemIds.length === 1; - - let nextRow: number; - let nextCol: number; - - switch (direction) { - case 'up': - nextRow = isSingleRow ? currentRow : Math.max(0, currentRow - 1); - nextCol = isSingleRow ? Math.max(0, currentCol - 1) : currentCol; - break; - case 'down': - nextRow = isSingleRow - ? currentRow - : Math.min(selectableItemIds.length - 1, currentRow + 1); - nextCol = isSingleRow - ? Math.min( - selectableItemIds[currentRow].length - 1, - currentCol + 1, - ) - : currentCol; - break; - case 'left': - nextRow = currentRow; - nextCol = Math.max(0, currentCol - 1); - break; - case 'right': - nextRow = currentRow; - nextCol = Math.min( - selectableItemIds[currentRow].length - 1, - currentCol + 1, - ); - break; - default: - nextRow = currentRow; - nextCol = currentCol; - } - - return selectableItemIds[nextRow][nextCol]; - }; - - const nextId = computeNextId(direction); - - if (selectedItemId !== nextId) { - if (isNonEmptyString(nextId)) { - set( - isSelectedItemIdComponentFamilySelector.selectorFamily({ - instanceId: instanceId, - familyKey: nextId, - }), - true, - ); - set( - selectedItemIdComponentState.atomFamily({ - instanceId: instanceId, - }), - nextId, - ); - onSelect?.(nextId); - } - - if (isNonEmptyString(selectedItemId)) { - set( - isSelectedItemIdComponentFamilySelector.selectorFamily({ - instanceId: instanceId, - familyKey: selectedItemId, - }), - false, - ); - } + const computeNextId = (direction: Direction) => { + if ( + selectableItemIds.length === 0 || + selectableItemIds[0]?.length === 0 + ) { + return; } - }, - [instanceId, onSelect], + + if (!selectedItemId || !currentPosition) { + return selectableItemIds[0][0]; + } + + const { row: currentRow, col: currentCol } = currentPosition; + + const isSingleRow = selectableItemIds.length === 1; + + let nextRow: number; + let nextCol: number; + + switch (direction) { + case 'up': + nextRow = isSingleRow ? currentRow : Math.max(0, currentRow - 1); + nextCol = isSingleRow ? Math.max(0, currentCol - 1) : currentCol; + break; + case 'down': + nextRow = isSingleRow + ? currentRow + : Math.min(selectableItemIds.length - 1, currentRow + 1); + nextCol = isSingleRow + ? Math.min( + selectableItemIds[currentRow].length - 1, + currentCol + 1, + ) + : currentCol; + break; + case 'left': + nextRow = currentRow; + nextCol = Math.max(0, currentCol - 1); + break; + case 'right': + nextRow = currentRow; + nextCol = Math.min( + selectableItemIds[currentRow].length - 1, + currentCol + 1, + ); + break; + default: + nextRow = currentRow; + nextCol = currentCol; + } + + return selectableItemIds[nextRow][nextCol]; + }; + + const nextId = computeNextId(direction); + + if (selectedItemId !== nextId) { + if (isNonEmptyString(nextId)) { + store.set( + isSelectedItemIdComponentFamilyState.atomFamily({ + instanceId: instanceId, + familyKey: nextId, + }), + true, + ); + store.set( + selectedItemIdComponentState.atomFamily({ + instanceId: instanceId, + }), + nextId, + ); + onSelect?.(nextId); + } + + if (isNonEmptyString(selectedItemId)) { + store.set( + isSelectedItemIdComponentFamilyState.atomFamily({ + instanceId: instanceId, + familyKey: selectedItemId, + }), + false, + ); + } + } + }, + [store, instanceId, onSelect], ); useHotkeysOnFocusedElement({ diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts index ce33cb5525b..84e4920613d 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableList.ts @@ -1,10 +1,10 @@ -import { useRecoilCallback } from 'recoil'; +import { useCallback } from 'react'; +import { useStore } from 'jotai'; import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { isDefined } from 'twenty-shared/utils'; export const useSelectableList = (instanceId?: string) => { @@ -12,70 +12,66 @@ export const useSelectableList = (instanceId?: string) => { SelectableListComponentInstanceContext, instanceId, ); - const resetSelectedItem = useRecoilCallback( - ({ snapshot, set }) => - () => { - const selectedItemId = getSnapshotValue( - snapshot, - selectedItemIdComponentState.atomFamily({ - instanceId: selectableListInstanceId, - }), - ); - if (isDefined(selectedItemId)) { - set( - selectedItemIdComponentState.atomFamily({ - instanceId: selectableListInstanceId, - }), - null, - ); - set( - isSelectedItemIdComponentFamilySelector.selectorFamily({ - instanceId: selectableListInstanceId, - familyKey: selectedItemId, - }), - false, - ); - } - }, - [selectableListInstanceId], - ); + const store = useStore(); - const setSelectedItemId = useRecoilCallback( - ({ set, snapshot }) => - (itemId: string) => { - const selectedItemId = getSnapshotValue( - snapshot, - selectedItemIdComponentState.atomFamily({ - instanceId: selectableListInstanceId, - }), - ); + const resetSelectedItem = useCallback(() => { + const selectedItemId = store.get( + selectedItemIdComponentState.atomFamily({ + instanceId: selectableListInstanceId, + }), + ); - if (isDefined(selectedItemId)) { - set( - isSelectedItemIdComponentFamilySelector.selectorFamily({ - instanceId: selectableListInstanceId, - familyKey: selectedItemId, - }), - false, - ); - } + if (isDefined(selectedItemId)) { + store.set( + selectedItemIdComponentState.atomFamily({ + instanceId: selectableListInstanceId, + }), + null, + ); + store.set( + isSelectedItemIdComponentFamilyState.atomFamily({ + instanceId: selectableListInstanceId, + familyKey: selectedItemId, + }), + false, + ); + } + }, [store, selectableListInstanceId]); - set( - selectedItemIdComponentState.atomFamily({ + const setSelectedItemId = useCallback( + (itemId: string) => { + const selectedItemId = store.get( + selectedItemIdComponentState.atomFamily({ + instanceId: selectableListInstanceId, + }), + ); + + if (isDefined(selectedItemId)) { + store.set( + isSelectedItemIdComponentFamilyState.atomFamily({ instanceId: selectableListInstanceId, + familyKey: selectedItemId, }), - itemId, + false, ); - set( - isSelectedItemIdComponentFamilySelector.selectorFamily({ - instanceId: selectableListInstanceId, - familyKey: itemId, - }), - true, - ); - }, - [selectableListInstanceId], + } + + store.set( + selectedItemIdComponentState.atomFamily({ + instanceId: selectableListInstanceId, + }), + itemId, + ); + store.set( + isSelectedItemIdComponentFamilyState.atomFamily({ + instanceId: selectableListInstanceId, + familyKey: itemId, + }), + true, + ); + }, + [store, selectableListInstanceId], ); return { diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts index 02990dd7423..32fd36fabc0 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem.ts @@ -2,9 +2,9 @@ import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-l import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { isNonEmptyString } from '@sniptt/guards'; -import { useRecoilCallback } from 'recoil'; +import { useCallback } from 'react'; +import { useStore } from 'jotai'; import { Key } from 'ts-key-enum'; export const useSelectableListListenToEnterHotkeyOnItem = ({ @@ -20,24 +20,23 @@ export const useSelectableListListenToEnterHotkeyOnItem = ({ SelectableListComponentInstanceContext, ); + const store = useStore(); + + const handleEnterKey = useCallback(() => { + const selectedItemId = store.get( + selectedItemIdComponentState.atomFamily({ + instanceId, + }), + ); + + if (isNonEmptyString(selectedItemId) && selectedItemId === itemId) { + onEnter?.(); + } + }, [store, instanceId, itemId, onEnter]); + useHotkeysOnFocusedElement({ keys: Key.Enter, - callback: useRecoilCallback( - ({ snapshot }) => - () => { - const selectedItemId = getSnapshotValue( - snapshot, - selectedItemIdComponentState.atomFamily({ - instanceId, - }), - ); - - if (isNonEmptyString(selectedItemId) && selectedItemId === itemId) { - onEnter?.(); - } - }, - [instanceId, itemId, onEnter], - ), + callback: handleEnterKey, focusId, dependencies: [itemId, onEnter], }); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState.ts index ff1b27efb75..ef0f7a41c46 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState.ts @@ -1,11 +1,9 @@ import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; +import { createComponentFamilyStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentFamilyStateV2'; -export const isSelectedItemIdComponentFamilyState = createComponentFamilyState< - boolean, - string ->({ - key: 'isSelectedItemIdComponentFamilyState', - defaultValue: false, - componentInstanceContext: SelectableListComponentInstanceContext, -}); +export const isSelectedItemIdComponentFamilyState = + createComponentFamilyStateV2({ + key: 'isSelectedItemIdComponentFamilyState', + defaultValue: false, + componentInstanceContext: SelectableListComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableItemIdsComponentState.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableItemIdsComponentState.ts index c89f1d70fd2..e32610377af 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableItemIdsComponentState.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectableItemIdsComponentState.ts @@ -1,10 +1,10 @@ import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; -export const selectableItemIdsComponentState = createComponentState( - { - key: 'selectableItemIdsComponentState', - defaultValue: [[]], - componentInstanceContext: SelectableListComponentInstanceContext, - }, -); +export const selectableItemIdsComponentState = createComponentStateV2< + string[][] +>({ + key: 'selectableItemIdsComponentState', + defaultValue: [[]], + componentInstanceContext: SelectableListComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectedItemIdComponentState.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectedItemIdComponentState.ts index 9a832bb9b38..4110ecf155d 100644 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectedItemIdComponentState.ts +++ b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectedItemIdComponentState.ts @@ -1,10 +1,10 @@ import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; -export const selectedItemIdComponentState = createComponentState( - { - key: 'selectedItemIdComponentState', - defaultValue: null, - componentInstanceContext: SelectableListComponentInstanceContext, - }, -); +export const selectedItemIdComponentState = createComponentStateV2< + string | null +>({ + key: 'selectedItemIdComponentState', + defaultValue: null, + componentInstanceContext: SelectableListComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector.ts b/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector.ts deleted file mode 100644 index 3e402a0729a..00000000000 --- a/packages/twenty-front/src/modules/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext'; -import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; -import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; - -export const isSelectedItemIdComponentFamilySelector = - createComponentFamilySelector({ - key: 'isSelectedItemIdComponentFamilySelector', - componentInstanceContext: SelectableListComponentInstanceContext, - get: - ({ instanceId, familyKey }: { instanceId: string; familyKey: string }) => - ({ get }) => - get( - isSelectedItemIdComponentFamilyState.atomFamily({ - instanceId: instanceId, - familyKey: familyKey, - }), - ), - set: - ({ instanceId, familyKey }: { instanceId: string; familyKey: string }) => - ({ set }, newValue) => - set( - isSelectedItemIdComponentFamilyState.atomFamily({ - instanceId: instanceId, - familyKey: familyKey, - }), - newValue, - ), - }); diff --git a/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabList.tsx b/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabList.tsx index ca2d4aee128..01801e3d842 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabList.tsx +++ b/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabList.tsx @@ -6,7 +6,7 @@ import { type TabListProps } from '@/ui/layout/tab-list/types/TabListProps'; import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension'; import { TabListHiddenMeasurements } from '@/ui/layout/tab-list/components/TabListHiddenMeasurements'; import { useTabListMeasurements } from '@/ui/layout/tab-list/hooks/useTabListMeasurements'; -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2'; import styled from '@emotion/styled'; import { useCallback, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -53,7 +53,7 @@ export const TabList = ({ const visibleTabs = tabs.filter((tab) => !tab.hide); const navigate = useNavigate(); - const [activeTabId, setActiveTabId] = useRecoilComponentState( + const [activeTabId, setActiveTabId] = useRecoilComponentStateV2( activeTabIdComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabListFromUrlOptionalEffect.tsx b/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabListFromUrlOptionalEffect.tsx index fd9b338de3c..bf8951c7356 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabListFromUrlOptionalEffect.tsx +++ b/packages/twenty-front/src/modules/ui/layout/tab-list/components/TabListFromUrlOptionalEffect.tsx @@ -1,6 +1,6 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; @@ -14,8 +14,10 @@ export const TabListFromUrlOptionalEffect = ({ isInRightDrawer, }: TabListFromUrlOptionalEffectProps) => { const location = useLocation(); - const activeTabId = useRecoilComponentValue(activeTabIdComponentState); - const setActiveTabId = useSetRecoilComponentState(activeTabIdComponentState); + const activeTabId = useRecoilComponentValueV2(activeTabIdComponentState); + const setActiveTabId = useSetRecoilComponentStateV2( + activeTabIdComponentState, + ); const hash = location.hash.replace('#', ''); diff --git a/packages/twenty-front/src/modules/ui/layout/tab-list/states/activeTabIdComponentState.ts b/packages/twenty-front/src/modules/ui/layout/tab-list/states/activeTabIdComponentState.ts index 02804bd195c..c486a93f575 100644 --- a/packages/twenty-front/src/modules/ui/layout/tab-list/states/activeTabIdComponentState.ts +++ b/packages/twenty-front/src/modules/ui/layout/tab-list/states/activeTabIdComponentState.ts @@ -1,7 +1,7 @@ import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; -export const activeTabIdComponentState = createComponentState({ +export const activeTabIdComponentState = createComponentStateV2({ key: 'activeTabIdComponentState', defaultValue: null, componentInstanceContext: TabListComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/layout/table/components/SortableTableHeader.tsx b/packages/twenty-front/src/modules/ui/layout/table/components/SortableTableHeader.tsx index 5342ede1758..ebb2a1b4710 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/components/SortableTableHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/table/components/SortableTableHeader.tsx @@ -2,7 +2,8 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableHeaderText } from '@/ui/layout/table/components/TableHeaderText'; import { sortedFieldByTableFamilyState } from '@/ui/layout/table/states/sortedFieldByTableFamilyState'; import { type TableSortValue } from '@/ui/layout/table/types/TableSortValue'; -import { useRecoilState } from 'recoil'; +import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2'; +import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2'; import { IconArrowDown, IconArrowUp, @@ -24,8 +25,13 @@ export const SortableTableHeader = ({ initialSort?: TableSortValue; Icon?: IconComponent; }) => { - const [sortedFieldByTable, setSortedFieldByTable] = useRecoilState( - sortedFieldByTableFamilyState({ tableId }), + const sortedFieldByTable = useFamilyRecoilValueV2( + sortedFieldByTableFamilyState, + { tableId }, + ); + const setSortedFieldByTable = useSetFamilyRecoilStateV2( + sortedFieldByTableFamilyState, + { tableId }, ); const sortValue = sortedFieldByTable ?? initialSort; diff --git a/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx b/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx index aa4a7ec8435..223b47339cc 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/table/hooks/__tests__/useSortedArray.test.tsx @@ -1,6 +1,6 @@ import { renderHook } from '@testing-library/react'; import React, { type ReactNode } from 'react'; -import { type MutableSnapshot, RecoilRoot } from 'recoil'; +import { createStore, Provider } from 'jotai'; import { mockedTableMetadata, @@ -17,41 +17,32 @@ import { sortedFieldByTableFamilyState } from '@/ui/layout/table/states/sortedFi import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray'; -interface WrapperProps { - children: ReactNode; - initializeState?: (mutableSnapshot: MutableSnapshot) => void; -} - -const Wrapper: React.FC = ({ children, initializeState }) => ( - {children} -); +const createSortedWrapper = ( + fieldName: keyof MockedTableType, + orderBy: OrderBy, +) => { + const store = createStore(); + store.set( + sortedFieldByTableFamilyState.atomFamily({ + tableId: mockedTableMetadata.tableId, + }), + { + fieldName: fieldName as string, + orderBy, + }, + ); + const Wrapper = ({ children }: { children: ReactNode }) => ( + {children} + ); + return Wrapper; +}; describe('useSortedArray hook', () => { - const initializeState = - (fieldName: keyof MockedTableType, orderBy: OrderBy) => - ({ set }: MutableSnapshot) => { - set( - sortedFieldByTableFamilyState({ - tableId: mockedTableMetadata.tableId, - }), - { - fieldName, - orderBy, - }, - ); - }; - test('initial sorting behavior for string fields - Ascending', () => { const { result } = renderHook( () => useSortedArray(tableData, mockedTableMetadata), { - wrapper: ({ children }: { children: ReactNode }) => ( - - {children} - - ), + wrapper: createSortedWrapper('labelPlural', 'AscNullsLast'), }, ); @@ -64,13 +55,7 @@ describe('useSortedArray hook', () => { const { result } = renderHook( () => useSortedArray(tableData, mockedTableMetadata), { - wrapper: ({ children }: { children: ReactNode }) => ( - - {children} - - ), + wrapper: createSortedWrapper('labelPlural', 'DescNullsLast'), }, ); @@ -83,13 +68,7 @@ describe('useSortedArray hook', () => { const { result } = renderHook( () => useSortedArray(tableData, mockedTableMetadata), { - wrapper: ({ children }: { children: ReactNode }) => ( - - {children} - - ), + wrapper: createSortedWrapper('fieldsCount', 'AscNullsLast'), }, ); @@ -102,13 +81,7 @@ describe('useSortedArray hook', () => { const { result } = renderHook( () => useSortedArray(tableData, mockedTableMetadata), { - wrapper: ({ children }: { children: ReactNode }) => ( - - {children} - - ), + wrapper: createSortedWrapper('fieldsCount', 'DescNullsLast'), }, ); diff --git a/packages/twenty-front/src/modules/ui/layout/table/hooks/useSortedArray.ts b/packages/twenty-front/src/modules/ui/layout/table/hooks/useSortedArray.ts index 4d293d4107d..72623a23e63 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/hooks/useSortedArray.ts +++ b/packages/twenty-front/src/modules/ui/layout/table/hooks/useSortedArray.ts @@ -1,15 +1,16 @@ import { sortedFieldByTableFamilyState } from '@/ui/layout/table/states/sortedFieldByTableFamilyState'; import { type TableMetadata } from '@/ui/layout/table/types/TableMetadata'; +import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2'; import { useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const useSortedArray = ( arrayToSort: T[], tableMetadata: TableMetadata, ): T[] => { - const sortedFieldByTable = useRecoilValue( - sortedFieldByTableFamilyState({ tableId: tableMetadata.tableId }), + const sortedFieldByTable = useFamilyRecoilValueV2( + sortedFieldByTableFamilyState, + { tableId: tableMetadata.tableId }, ); const initialSort = tableMetadata.initialSort; diff --git a/packages/twenty-front/src/modules/ui/layout/table/states/sortedFieldByTableFamilyState.ts b/packages/twenty-front/src/modules/ui/layout/table/states/sortedFieldByTableFamilyState.ts index c7ef627dc73..0c7df64e723 100644 --- a/packages/twenty-front/src/modules/ui/layout/table/states/sortedFieldByTableFamilyState.ts +++ b/packages/twenty-front/src/modules/ui/layout/table/states/sortedFieldByTableFamilyState.ts @@ -1,11 +1,11 @@ import { type TableSortValue } from '@/ui/layout/table/types/TableSortValue'; -import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState'; +import { createFamilyStateV2 } from '@/ui/utilities/state/jotai/utils/createFamilyStateV2'; export type SortedFieldByTableFamilyStateKey = { tableId: string; }; -export const sortedFieldByTableFamilyState = createFamilyState< +export const sortedFieldByTableFamilyState = createFamilyStateV2< TableSortValue | null, SortedFieldByTableFamilyStateKey >({ diff --git a/packages/twenty-front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts b/packages/twenty-front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts index 05141d67161..0b02f33583e 100644 --- a/packages/twenty-front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts +++ b/packages/twenty-front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts @@ -2,9 +2,11 @@ import { useCallback } from 'react'; import { isDragSelectionStartEnabledStateV2 } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledStateV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; export const useDragSelect = () => { + const store = useStore(); + const setIsDragSelectionStartEnabled = useSetRecoilStateV2( isDragSelectionStartEnabledStateV2, ); @@ -14,8 +16,8 @@ export const useDragSelect = () => { }; const isDragSelectionStartEnabled = useCallback( - () => jotaiStore.get(isDragSelectionStartEnabledStateV2.atom), - [], + () => store.get(isDragSelectionStartEnabledStateV2.atom), + [store], ); return { diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/usePushFocusItemToFocusStack.test.tsx b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/usePushFocusItemToFocusStack.test.tsx index 5ce2d251235..91a959fdf84 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/usePushFocusItemToFocusStack.test.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/usePushFocusItemToFocusStack.test.tsx @@ -2,16 +2,27 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { renderHook } from '@testing-library/react'; +import { createStore, Provider as JotaiProvider } from 'jotai'; import { act } from 'react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; + +const createTestWrapper = () => { + const store = createStore(); + + const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + return Wrapper; +}; const renderHooks = () => { const { result } = renderHook( () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); - const focusStack = useRecoilValue(focusStackState); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const focusStack = useRecoilValueV2(focusStackState); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); return { pushFocusItemToFocusStack, @@ -20,7 +31,7 @@ const renderHooks = () => { }; }, { - wrapper: RecoilRoot, + wrapper: createTestWrapper(), }, ); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useRemoveFocusItemFromFocusStackById.test.tsx b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useRemoveFocusItemFromFocusStackById.test.tsx index 9cff6e11d93..99dbbc08030 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useRemoveFocusItemFromFocusStackById.test.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useRemoveFocusItemFromFocusStackById.test.tsx @@ -3,9 +3,18 @@ import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { + jotaiStore, + resetJotaiStore, +} from '@/ui/utilities/state/jotai/jotaiStore'; import { renderHook } from '@testing-library/react'; +import { Provider as JotaiProvider } from 'jotai'; import { act } from 'react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); const renderHooks = () => { const { result } = renderHook( @@ -13,8 +22,8 @@ const renderHooks = () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); const { removeFocusItemFromFocusStackById } = useRemoveFocusItemFromFocusStackById(); - const focusStack = useRecoilValue(focusStackState); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const focusStack = useRecoilValueV2(focusStackState); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); return { pushFocusItemToFocusStack, @@ -24,7 +33,7 @@ const renderHooks = () => { }; }, { - wrapper: RecoilRoot, + wrapper: Wrapper, }, ); @@ -56,6 +65,10 @@ const secondFocusItem = { }; describe('useRemoveFocusItemFromFocusStackById', () => { + beforeEach(() => { + resetJotaiStore(); + }); + it('should remove focus item from the stack', async () => { const { result } = renderHooks(); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStack.test.tsx b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStack.test.tsx index 2b06db69b6d..f15e9d27272 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStack.test.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStack.test.tsx @@ -3,17 +3,26 @@ import { useResetFocusStack } from '@/ui/utilities/focus/hooks/useResetFocusStac import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { + jotaiStore, + resetJotaiStore, +} from '@/ui/utilities/state/jotai/jotaiStore'; import { renderHook } from '@testing-library/react'; +import { Provider as JotaiProvider } from 'jotai'; import { act } from 'react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); const renderHooks = () => { const { result } = renderHook( () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); const { resetFocusStack } = useResetFocusStack(); - const focusStack = useRecoilValue(focusStackState); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const focusStack = useRecoilValueV2(focusStackState); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); return { pushFocusItemToFocusStack, @@ -23,7 +32,7 @@ const renderHooks = () => { }; }, { - wrapper: RecoilRoot, + wrapper: Wrapper, }, ); @@ -31,6 +40,10 @@ const renderHooks = () => { }; describe('useResetFocusStack', () => { + beforeEach(() => { + resetJotaiStore(); + }); + it('should reset the focus stack', async () => { const { result } = renderHooks(); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStackToFocusItem.test.tsx b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStackToFocusItem.test.tsx index 08209626dcd..856aabf31af 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStackToFocusItem.test.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/__tests__/useResetFocusStackToFocusItem.test.tsx @@ -3,17 +3,26 @@ import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useRes import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; +import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { + jotaiStore, + resetJotaiStore, +} from '@/ui/utilities/state/jotai/jotaiStore'; import { renderHook } from '@testing-library/react'; +import { Provider as JotaiProvider } from 'jotai'; import { act } from 'react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); const renderHooks = () => { const { result } = renderHook( () => { const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); const { resetFocusStackToFocusItem } = useResetFocusStackToFocusItem(); - const focusStack = useRecoilValue(focusStackState); - const currentFocusId = useRecoilValue(currentFocusIdSelector); + const focusStack = useRecoilValueV2(focusStackState); + const currentFocusId = useRecoilValueV2(currentFocusIdSelector); return { pushFocusItemToFocusStack, @@ -23,7 +32,7 @@ const renderHooks = () => { }; }, { - wrapper: RecoilRoot, + wrapper: Wrapper, }, ); @@ -31,6 +40,10 @@ const renderHooks = () => { }; describe('useResetFocusStackToFocusItem', () => { + beforeEach(() => { + resetJotaiStore(); + }); + it('should reset the focus stack to a specific focus item', async () => { const { result } = renderHooks(); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/usePushFocusItemToFocusStack.ts b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/usePushFocusItemToFocusStack.ts index 228b9adc954..a2a0c43f301 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/usePushFocusItemToFocusStack.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/usePushFocusItemToFocusStack.ts @@ -1,9 +1,11 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { type FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { type FocusStackItem } from '@/ui/utilities/focus/types/FocusStackItem'; import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { logDebug } from '~/utils/logDebug'; const addOrMoveItemToTheTopOfTheStack = ({ @@ -21,54 +23,53 @@ const addOrMoveItemToTheTopOfTheStack = ({ ]; export const usePushFocusItemToFocusStack = () => { - const pushFocusItemToFocusStack = useRecoilCallback( - ({ snapshot, set }) => - ({ + const store = useStore(); + + const pushFocusItemToFocusStack = useCallback( + ({ + focusId, + component, + globalHotkeysConfig, + }: { + focusId: string; + component: { + type: FocusComponentType; + instanceId: string; + }; + globalHotkeysConfig?: Partial; + }) => { + const focusStackItem: FocusStackItem = { focusId, - component, - globalHotkeysConfig, - }: { - focusId: string; - component: { - type: FocusComponentType; - instanceId: string; - }; - globalHotkeysConfig?: Partial; - }) => { - const focusStackItem: FocusStackItem = { - focusId, - componentInstance: { - componentType: component.type, - componentInstanceId: component.instanceId, - }, - globalHotkeysConfig: { - enableGlobalHotkeysWithModifiers: - globalHotkeysConfig?.enableGlobalHotkeysWithModifiers ?? true, - enableGlobalHotkeysConflictingWithKeyboard: - globalHotkeysConfig?.enableGlobalHotkeysConflictingWithKeyboard ?? - true, - }, - }; + componentInstance: { + componentType: component.type, + componentInstanceId: component.instanceId, + }, + globalHotkeysConfig: { + enableGlobalHotkeysWithModifiers: + globalHotkeysConfig?.enableGlobalHotkeysWithModifiers ?? true, + enableGlobalHotkeysConflictingWithKeyboard: + globalHotkeysConfig?.enableGlobalHotkeysConflictingWithKeyboard ?? + true, + }, + }; - const currentFocusStack = snapshot - .getLoadable(focusStackState) - .getValue(); + const currentFocusStack = store.get(focusStackState.atom); - const newFocusStack = addOrMoveItemToTheTopOfTheStack({ + const newFocusStack = addOrMoveItemToTheTopOfTheStack({ + focusStackItem, + currentFocusStack, + }); + + store.set(focusStackState.atom, newFocusStack); + + if (DEBUG_FOCUS_STACK) { + logDebug(`DEBUG: pushFocusItemToFocusStack ${focusId}`, { focusStackItem, - currentFocusStack, + newFocusStack, }); - - set(focusStackState, newFocusStack); - - if (DEBUG_FOCUS_STACK) { - logDebug(`DEBUG: pushFocusItemToFocusStack ${focusId}`, { - focusStackItem, - newFocusStack, - }); - } - }, - [], + } + }, + [store], ); return { pushFocusItemToFocusStack }; diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType.ts b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType.ts index 042f674628e..0971014ddac 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackByComponentType.ts @@ -1,48 +1,51 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { type FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { logDebug } from '~/utils/logDebug'; export const useRemoveLastFocusItemFromFocusStackByComponentType = () => { - const removeLastFocusItemFromFocusStackByComponentType = useRecoilCallback( - ({ snapshot, set }) => - ({ componentType }: { componentType: FocusComponentType }) => { - const focusStack = snapshot.getLoadable(focusStackState).getValue(); + const store = useStore(); - const lastMatchingIndex = focusStack.findLastIndex( - (focusStackItem) => - focusStackItem.componentInstance.componentType === componentType, - ); + const removeLastFocusItemFromFocusStackByComponentType = useCallback( + ({ componentType }: { componentType: FocusComponentType }) => { + const focusStack = store.get(focusStackState.atom); - if (lastMatchingIndex === -1) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: removeFocusItemFromFocusStackByComponentType - no item found for type ${componentType}`, - { focusStack }, - ); - } - return; - } - - const removedFocusItem = focusStack[lastMatchingIndex]; - const newFocusStack = focusStack.filter( - (_, index) => index !== lastMatchingIndex, - ); - - set(focusStackState, newFocusStack); + const lastMatchingIndex = focusStack.findLastIndex( + (focusStackItem) => + focusStackItem.componentInstance.componentType === componentType, + ); + if (lastMatchingIndex === -1) { if (DEBUG_FOCUS_STACK) { logDebug( - `DEBUG: removeFocusItemFromFocusStackByComponentType ${componentType}`, - { - removedFocusItem, - newFocusStack, - }, + `DEBUG: removeFocusItemFromFocusStackByComponentType - no item found for type ${componentType}`, + { focusStack }, ); } - }, - [], + return; + } + + const removedFocusItem = focusStack[lastMatchingIndex]; + const newFocusStack = focusStack.filter( + (_, index) => index !== lastMatchingIndex, + ); + + store.set(focusStackState.atom, newFocusStack); + + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: removeFocusItemFromFocusStackByComponentType ${componentType}`, + { + removedFocusItem, + newFocusStack, + }, + ); + } + }, + [store], ); return { removeLastFocusItemFromFocusStackByComponentType }; diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById.ts b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById.ts index 8e9520860cf..a305af79925 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById.ts @@ -1,35 +1,38 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { logDebug } from '~/utils/logDebug'; export const useRemoveFocusItemFromFocusStackById = () => { - const removeFocusItemFromFocusStackById = useRecoilCallback( - ({ snapshot, set }) => - ({ focusId }: { focusId: string }) => { - const focusStack = snapshot.getLoadable(focusStackState).getValue(); + const store = useStore(); - const removedFocusItem = focusStack.find( - (focusStackItem) => focusStackItem.focusId === focusId, - ); + const removeFocusItemFromFocusStackById = useCallback( + ({ focusId }: { focusId: string }) => { + const focusStack = store.get(focusStackState.atom); - if (!removedFocusItem) { - return; - } + const removedFocusItem = focusStack.find( + (focusStackItem) => focusStackItem.focusId === focusId, + ); - const newFocusStack = focusStack.filter( - (focusStackItem) => focusStackItem.focusId !== focusId, - ); + if (!removedFocusItem) { + return; + } - set(focusStackState, newFocusStack); + const newFocusStack = focusStack.filter( + (focusStackItem) => focusStackItem.focusId !== focusId, + ); - if (DEBUG_FOCUS_STACK) { - logDebug(`DEBUG: removeFocusItemFromFocusStack ${focusId}`, { - newFocusStack, - }); - } - }, - [], + store.set(focusStackState.atom, newFocusStack); + + if (DEBUG_FOCUS_STACK) { + logDebug(`DEBUG: removeFocusItemFromFocusStack ${focusId}`, { + newFocusStack, + }); + } + }, + [store], ); return { removeFocusItemFromFocusStackById }; diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStack.ts b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStack.ts index 5514ebc6b6a..f655814d304 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStack.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStack.ts @@ -1,20 +1,20 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { logDebug } from '~/utils/logDebug'; export const useResetFocusStack = () => { - const resetFocusStack = useRecoilCallback( - ({ reset }) => - () => { - reset(focusStackState); + const store = useStore(); - if (DEBUG_FOCUS_STACK) { - logDebug(`DEBUG: reset focus stack`); - } - }, - [], - ); + const resetFocusStack = useCallback(() => { + store.set(focusStackState.atom, []); + + if (DEBUG_FOCUS_STACK) { + logDebug(`DEBUG: reset focus stack`); + } + }, [store]); return { resetFocusStack }; }; diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStackToFocusItem.ts b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStackToFocusItem.ts index c79f206a6d4..385bee64477 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStackToFocusItem.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/hooks/useResetFocusStackToFocusItem.ts @@ -1,22 +1,25 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { type FocusStackItem } from '@/ui/utilities/focus/types/FocusStackItem'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { logDebug } from '~/utils/logDebug'; export const useResetFocusStackToFocusItem = () => { - const resetFocusStackToFocusItem = useRecoilCallback( - ({ set }) => - ({ focusStackItem }: { focusStackItem: FocusStackItem }) => { - set(focusStackState, [focusStackItem]); + const store = useStore(); - if (DEBUG_FOCUS_STACK) { - logDebug(`DEBUG: reset focus stack to focus item`, { - focusStackItem, - }); - } - }, - [], + const resetFocusStackToFocusItem = useCallback( + ({ focusStackItem }: { focusStackItem: FocusStackItem }) => { + store.set(focusStackState.atom, [focusStackItem]); + + if (DEBUG_FOCUS_STACK) { + logDebug(`DEBUG: reset focus stack to focus item`, { + focusStackItem, + }); + } + }, + [store], ); return { resetFocusStackToFocusItem }; diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusIdSelector.ts b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusIdSelector.ts index 606256ee0dc..45e61c4d8fd 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusIdSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusIdSelector.ts @@ -1,7 +1,7 @@ -import { selector } from 'recoil'; +import { createSelectorV2 } from '@/ui/utilities/state/jotai/utils/createSelectorV2'; import { focusStackState } from './focusStackState'; -export const currentFocusIdSelector = selector({ +export const currentFocusIdSelector = createSelectorV2({ key: 'currentFocusIdSelector', get: ({ get }) => { const focusStack = get(focusStackState); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusedItemSelector.ts b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusedItemSelector.ts index 491b7caabbe..725a9c28580 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusedItemSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentFocusedItemSelector.ts @@ -1,8 +1,10 @@ import { type FocusStackItem } from '@/ui/utilities/focus/types/FocusStackItem'; -import { selector } from 'recoil'; +import { createSelectorV2 } from '@/ui/utilities/state/jotai/utils/createSelectorV2'; import { focusStackState } from './focusStackState'; -export const currentFocusedItemSelector = selector({ +export const currentFocusedItemSelector = createSelectorV2< + FocusStackItem | undefined +>({ key: 'currentFocusedItemSelector', get: ({ get }) => { const focusStack = get(focusStackState); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentGlobalHotkeysConfigSelector.ts b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentGlobalHotkeysConfigSelector.ts index 9cd94d21ae9..807f22e96e8 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/states/currentGlobalHotkeysConfigSelector.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/states/currentGlobalHotkeysConfigSelector.ts @@ -1,11 +1,11 @@ import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { DEFAULT_GLOBAL_HOTKEYS_CONFIG } from '@/ui/utilities/hotkey/constants/DefaultGlobalHotkeysConfig'; import { type GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig'; -import { selector } from 'recoil'; +import { createSelectorV2 } from '@/ui/utilities/state/jotai/utils/createSelectorV2'; import { isDefined } from 'twenty-shared/utils'; -export const currentGlobalHotkeysConfigSelector = selector( - { +export const currentGlobalHotkeysConfigSelector = + createSelectorV2({ key: 'currentGlobalHotkeysConfigSelector', get: ({ get }) => { const focusStack = get(focusStackState); @@ -17,5 +17,4 @@ export const currentGlobalHotkeysConfigSelector = selector( return lastFocusStackItem.globalHotkeysConfig; }, - }, -); + }); diff --git a/packages/twenty-front/src/modules/ui/utilities/focus/states/focusStackState.ts b/packages/twenty-front/src/modules/ui/utilities/focus/states/focusStackState.ts index f3e88c326f6..296a87d565b 100644 --- a/packages/twenty-front/src/modules/ui/utilities/focus/states/focusStackState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/focus/states/focusStackState.ts @@ -1,7 +1,7 @@ import { type FocusStackItem } from '@/ui/utilities/focus/types/FocusStackItem'; -import { createState } from '@/ui/utilities/state/utils/createState'; +import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2'; -export const focusStackState = createState({ +export const focusStackState = createStateV2({ key: 'focusStackState', defaultValue: [], }); diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeys.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeys.ts index b69d274bfb6..074f517f8fe 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeys.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeys.ts @@ -1,12 +1,14 @@ +import { useCallback } from 'react'; + import { useGlobalHotkeysCallback } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback'; import { pendingHotkeyState } from '@/ui/utilities/hotkey/states/internal/pendingHotkeysState'; +import { useStore } from 'jotai'; import { useHotkeys } from 'react-hotkeys-hook'; import { type HotkeyCallback, type Keys, type Options, } from 'react-hotkeys-hook/dist/types'; -import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; type UseHotkeysOptionsWithoutBuggyOptions = Omit; @@ -24,6 +26,8 @@ export const useGlobalHotkeys = ({ dependencies?: unknown[]; options?: UseHotkeysOptionsWithoutBuggyOptions; }) => { + const store = useStore(); + const callGlobalHotkeysCallback = useGlobalHotkeysCallback(dependencies); const enableOnContentEditable = isDefined(options?.enableOnContentEditable) @@ -42,20 +46,17 @@ export const useGlobalHotkeys = ({ ? options.ignoreModifiers === true : false; - const handleCallback = useRecoilCallback( - ({ snapshot, set }) => - async (keyboardEvent: KeyboardEvent, hotkeysEvent: any) => { - const pendingHotkey = snapshot - .getLoadable(pendingHotkeyState) - .getValue(); + const handleCallback = useCallback( + async (keyboardEvent: KeyboardEvent, hotkeysEvent: any) => { + const pendingHotkey = store.get(pendingHotkeyState.atom); - if (!pendingHotkey) { - callback(keyboardEvent, hotkeysEvent); - } + if (!pendingHotkey) { + callback(keyboardEvent, hotkeysEvent); + } - set(pendingHotkeyState, null); - }, - [callback], + store.set(pendingHotkeyState.atom, null); + }, + [callback, store], ); return useHotkeys( diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback.ts index 0f5de65f602..133f7347481 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback.ts @@ -1,83 +1,86 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; import { currentGlobalHotkeysConfigSelector } from '@/ui/utilities/focus/states/currentGlobalHotkeysConfigSelector'; +import { useStore } from 'jotai'; import { type Hotkey, type OptionsOrDependencyArray, } from 'react-hotkeys-hook/dist/types'; -import { useRecoilCallback } from 'recoil'; import { logDebug } from '~/utils/logDebug'; export const useGlobalHotkeysCallback = ( dependencies?: OptionsOrDependencyArray, ) => { + const store = useStore(); + const dependencyArray = Array.isArray(dependencies) ? dependencies : []; - return useRecoilCallback( - ({ snapshot }) => - ({ - callback, - containsModifier, - hotkeysEvent, - keyboardEvent, - preventDefault, - }: { - keyboardEvent: KeyboardEvent; - hotkeysEvent: Hotkey; - containsModifier: boolean; - callback: (keyboardEvent: KeyboardEvent, hotkeysEvent: Hotkey) => void; - preventDefault?: boolean; - }) => { - const currentGlobalHotkeysConfig = snapshot - .getLoadable(currentGlobalHotkeysConfigSelector) - .getValue(); + return useCallback( + ({ + callback, + containsModifier, + hotkeysEvent, + keyboardEvent, + preventDefault, + }: { + keyboardEvent: KeyboardEvent; + hotkeysEvent: Hotkey; + containsModifier: boolean; + callback: (keyboardEvent: KeyboardEvent, hotkeysEvent: Hotkey) => void; + preventDefault?: boolean; + }) => { + const currentGlobalHotkeysConfig = store.get( + currentGlobalHotkeysConfigSelector.atom, + ); - if ( - containsModifier && - !currentGlobalHotkeysConfig.enableGlobalHotkeysWithModifiers - ) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: %cI can't call hotkey (${ - hotkeysEvent.keys - }) because global hotkeys with modifiers are disabled`, - 'color: gray; ', - ); - } - - return; + if ( + containsModifier && + !currentGlobalHotkeysConfig.enableGlobalHotkeysWithModifiers + ) { + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: %cI can't call hotkey (${ + hotkeysEvent.keys + }) because global hotkeys with modifiers are disabled`, + 'color: gray; ', + ); } - if ( - !containsModifier && - !currentGlobalHotkeysConfig.enableGlobalHotkeysConflictingWithKeyboard - ) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: %cI can't call hotkey (${ - hotkeysEvent.keys - }) because global hotkeys conflicting with keyboard are disabled`, - 'color: gray; ', - ); - } - return; + return; + } + + if ( + !containsModifier && + !currentGlobalHotkeysConfig.enableGlobalHotkeysConflictingWithKeyboard + ) { + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: %cI can't call hotkey (${ + hotkeysEvent.keys + }) because global hotkeys conflicting with keyboard are disabled`, + 'color: gray; ', + ); + } + return; + } + + if (preventDefault === true) { + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: %cI prevent global default for hotkey (${hotkeysEvent.keys})`, + 'color: gray;', + ); } - if (preventDefault === true) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: %cI prevent global default for hotkey (${hotkeysEvent.keys})`, - 'color: gray;', - ); - } + keyboardEvent.stopPropagation(); + keyboardEvent.preventDefault(); + keyboardEvent.stopImmediatePropagation(); + } - keyboardEvent.stopPropagation(); - keyboardEvent.preventDefault(); - keyboardEvent.stopImmediatePropagation(); - } - - return callback(keyboardEvent, hotkeysEvent); - }, + return callback(keyboardEvent, hotkeysEvent); + }, // eslint-disable-next-line react-hooks/exhaustive-deps - dependencyArray, + [...dependencyArray, store], ); }; diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysSequence.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysSequence.ts index 7a68e754162..670d449451a 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysSequence.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useGlobalHotkeysSequence.ts @@ -1,10 +1,10 @@ import { type Options, useHotkeys } from 'react-hotkeys-hook'; import { type Keys } from 'react-hotkeys-hook/dist/types'; -import { useRecoilState } from 'recoil'; import { pendingHotkeyState } from '@/ui/utilities/hotkey/states/internal/pendingHotkeysState'; import { useGlobalHotkeysCallback } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeysCallback'; +import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2'; import { isDefined } from 'twenty-shared/utils'; export const useGlobalHotkeysSequence = ( @@ -18,7 +18,8 @@ export const useGlobalHotkeysSequence = ( }, deps: any[] = [], ) => { - const [pendingHotkey, setPendingHotkey] = useRecoilState(pendingHotkeyState); + const [pendingHotkey, setPendingHotkey] = + useRecoilStateV2(pendingHotkeyState); const callGlobalHotkeysCallback = useGlobalHotkeysCallback(); diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement.ts index 21a9f8f6e83..6d1dc6cacca 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement.ts @@ -1,12 +1,12 @@ import { useHotkeysOnFocusedElementCallback } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElementCallback'; import { pendingHotkeyState } from '@/ui/utilities/hotkey/states/internal/pendingHotkeysState'; +import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2'; import { useHotkeys } from 'react-hotkeys-hook'; import { type HotkeyCallback, type Keys, type Options, } from 'react-hotkeys-hook/dist/types'; -import { useRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; type UseHotkeysOptionsWithoutBuggyOptions = Omit; @@ -24,7 +24,8 @@ export const useHotkeysOnFocusedElement = ({ dependencies?: unknown[]; options?: UseHotkeysOptionsWithoutBuggyOptions; }) => { - const [pendingHotkey, setPendingHotkey] = useRecoilState(pendingHotkeyState); + const [pendingHotkey, setPendingHotkey] = + useRecoilStateV2(pendingHotkeyState); const callScopedHotkeyCallback = useHotkeysOnFocusedElementCallback(dependencies); diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElementCallback.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElementCallback.ts index 56a5f07c956..66c59b9573e 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElementCallback.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElementCallback.ts @@ -1,74 +1,75 @@ +import { useCallback } from 'react'; + import { DEBUG_FOCUS_STACK } from '@/ui/utilities/focus/constants/DebugFocusStack'; +import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; +import { useStore } from 'jotai'; import { type Hotkey, type OptionsOrDependencyArray, } from 'react-hotkeys-hook/dist/types'; -import { useRecoilCallback } from 'recoil'; import { logDebug } from '~/utils/logDebug'; -import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector'; export const useHotkeysOnFocusedElementCallback = ( dependencies?: OptionsOrDependencyArray, ) => { + const store = useStore(); + const dependencyArray = Array.isArray(dependencies) ? dependencies : []; - return useRecoilCallback( - ({ snapshot }) => - ({ - callback, - hotkeysEvent, - keyboardEvent, - focusId, - preventDefault, - }: { - keyboardEvent: KeyboardEvent; - hotkeysEvent: Hotkey; - callback: (keyboardEvent: KeyboardEvent, hotkeysEvent: Hotkey) => void; - focusId: string; - preventDefault?: boolean; - }) => { - const currentFocusId = snapshot - .getLoadable(currentFocusIdSelector) - .getValue(); - - if (currentFocusId !== focusId) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: %cI can't call hotkey (${ - hotkeysEvent.keys - }) because I'm in [${focusId}] and the current focus identifier is [${currentFocusId}]`, - 'color: gray; ', - ); - } - - return; - } + return useCallback( + ({ + callback, + hotkeysEvent, + keyboardEvent, + focusId, + preventDefault, + }: { + keyboardEvent: KeyboardEvent; + hotkeysEvent: Hotkey; + callback: (keyboardEvent: KeyboardEvent, hotkeysEvent: Hotkey) => void; + focusId: string; + preventDefault?: boolean; + }) => { + const currentFocusId = store.get(currentFocusIdSelector.atom); + if (currentFocusId !== focusId) { if (DEBUG_FOCUS_STACK) { logDebug( - `DEBUG: %cI can call hotkey (${ + `DEBUG: %cI can't call hotkey (${ hotkeysEvent.keys }) because I'm in [${focusId}] and the current focus identifier is [${currentFocusId}]`, - 'color: green;', + 'color: gray; ', ); } - if (preventDefault === true) { - if (DEBUG_FOCUS_STACK) { - logDebug( - `DEBUG: %cI prevent default for hotkey (${hotkeysEvent.keys})`, - 'color: gray;', - ); - } + return; + } - keyboardEvent.stopPropagation(); - keyboardEvent.preventDefault(); - keyboardEvent.stopImmediatePropagation(); + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: %cI can call hotkey (${ + hotkeysEvent.keys + }) because I'm in [${focusId}] and the current focus identifier is [${currentFocusId}]`, + 'color: green;', + ); + } + + if (preventDefault === true) { + if (DEBUG_FOCUS_STACK) { + logDebug( + `DEBUG: %cI prevent default for hotkey (${hotkeysEvent.keys})`, + 'color: gray;', + ); } - return callback(keyboardEvent, hotkeysEvent); - }, + keyboardEvent.stopPropagation(); + keyboardEvent.preventDefault(); + keyboardEvent.stopImmediatePropagation(); + } + + return callback(keyboardEvent, hotkeysEvent); + }, // eslint-disable-next-line react-hooks/exhaustive-deps - dependencyArray, + [...dependencyArray, store], ); }; diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts index b4cca97b3bb..550e290b3f9 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/states/internal/pendingHotkeysState.ts @@ -1,7 +1,7 @@ import { type Keys } from 'react-hotkeys-hook/dist/types'; -import { createState } from '@/ui/utilities/state/utils/createState'; +import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2'; -export const pendingHotkeyState = createState({ +export const pendingHotkeyState = createStateV2({ key: 'pendingHotkeyState', defaultValue: null, }); diff --git a/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts b/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts index 960315fcfbc..c33da128c90 100644 --- a/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/loading-state/states/currentPageLocationState.ts @@ -1,5 +1,5 @@ -import { createState } from '@/ui/utilities/state/utils/createState'; -export const currentPageLocationState = createState({ +import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2'; +export const currentPageLocationState = createStateV2({ key: 'currentPageLocationState', defaultValue: '', }); diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/__tests__/useClickOutsideListener.test.tsx b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/__tests__/useClickOutsideListener.test.tsx index 51f7f6524e1..a8060efc8ac 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/__tests__/useClickOutsideListener.test.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/__tests__/useClickOutsideListener.test.tsx @@ -1,26 +1,38 @@ import { act, renderHook } from '@testing-library/react'; -import { RecoilRoot } from 'recoil'; +import { Provider as JotaiProvider } from 'jotai'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; +import { + jotaiStore, + resetJotaiStore, +} from '@/ui/utilities/state/jotai/jotaiStore'; const componentId = 'componentId'; +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); + describe('useClickOutsideListener', () => { + beforeEach(() => { + resetJotaiStore(); + }); + it('should toggle the click outside listener activation state', async () => { const { result } = renderHook( () => { return { useClickOutside: useClickOutsideListener(componentId), - isActivated: useRecoilComponentValue( + isActivated: useRecoilComponentValueV2( clickOutsideListenerIsActivatedComponentState, componentId, ), }; }, { - wrapper: RecoilRoot, + wrapper: Wrapper, }, ); diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useClickOutsideListener.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useClickOutsideListener.ts index 54782257d3e..55c8e7b5f39 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useClickOutsideListener.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useClickOutsideListener.ts @@ -1,34 +1,31 @@ -import { useRecoilCallback } from 'recoil'; +import { useCallback } from 'react'; import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; import { clickOutsideListenerMouseDownHappenedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useStore } from 'jotai'; export const useClickOutsideListener = (instanceId: string) => { - const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( - clickOutsideListenerIsActivatedComponentState, - instanceId, - ); + const store = useStore(); - const clickOutsideListenerMouseDownHappenedState = - useRecoilComponentCallbackState( - clickOutsideListenerMouseDownHappenedComponentState, - instanceId, - ); + const toggleClickOutside = useCallback( + (activated: boolean) => { + store.set( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId, + }), + activated, + ); - const toggleClickOutside = useRecoilCallback( - ({ set }) => - (activated: boolean) => { - set(clickOutsideListenerIsActivatedState, activated); - - if (!activated) { - set(clickOutsideListenerMouseDownHappenedState, false); - } - }, - [ - clickOutsideListenerIsActivatedState, - clickOutsideListenerMouseDownHappenedState, - ], + if (!activated) { + store.set( + clickOutsideListenerMouseDownHappenedComponentState.atomFamily({ + instanceId, + }), + false, + ); + } + }, + [instanceId, store], ); return { diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts index 4bcedfa7d43..df2ea0a9973 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts @@ -1,9 +1,9 @@ +import { useCallback, useEffect, type RefObject } from 'react'; + import { clickOutsideListenerIsActivatedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState'; import { clickOutsideListenerIsMouseDownInsideComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState'; import { clickOutsideListenerMouseDownHappenedComponentState } from '@/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { useEffect, type RefObject } from 'react'; -import { useRecoilCallback } from 'recoil'; +import { useStore } from 'jotai'; import { isDefined } from 'twenty-shared/utils'; const CLICK_OUTSIDE_DEBUG_MODE = false; @@ -23,132 +23,120 @@ export const useListenClickOutside = ({ listenerId, enabled = true, }: ClickOutsideListenerProps) => { - const clickOutsideListenerIsMouseDownInsideState = - useRecoilComponentCallbackState( - clickOutsideListenerIsMouseDownInsideComponentState, - listenerId, - ); - const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( - clickOutsideListenerIsActivatedComponentState, - listenerId, - ); - const clickOutsideListenerMouseDownHappenedState = - useRecoilComponentCallbackState( - clickOutsideListenerMouseDownHappenedComponentState, - listenerId, - ); + const store = useStore(); - const handleMouseDown = useRecoilCallback( - ({ snapshot, set }) => - (event: MouseEvent | TouchEvent) => { - const clickOutsideListenerIsActivated = snapshot - .getLoadable(clickOutsideListenerIsActivatedState) - .getValue(); + const handleMouseDown = useCallback( + (event: MouseEvent | TouchEvent) => { + const clickOutsideListenerIsActivated = store.get( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId: listenerId, + }), + ); - set(clickOutsideListenerMouseDownHappenedState, true); + store.set( + clickOutsideListenerMouseDownHappenedComponentState.atomFamily({ + instanceId: listenerId, + }), + true, + ); - const isListening = clickOutsideListenerIsActivated && enabled; + const isListening = clickOutsideListenerIsActivated && enabled; - if (!isListening) { - return; - } + if (!isListening) { + return; + } - const clickedOnAtLeastOneRef = refs - .filter((ref) => !!ref.current) - .some((ref) => ref.current?.contains(event.target as Node)); + const clickedOnAtLeastOneRef = refs + .filter((ref) => !!ref.current) + .some((ref) => ref.current?.contains(event.target as Node)); - set(clickOutsideListenerIsMouseDownInsideState, clickedOnAtLeastOneRef); - }, - [ - clickOutsideListenerIsActivatedState, - clickOutsideListenerMouseDownHappenedState, - enabled, - refs, - clickOutsideListenerIsMouseDownInsideState, - ], + store.set( + clickOutsideListenerIsMouseDownInsideComponentState.atomFamily({ + instanceId: listenerId, + }), + clickedOnAtLeastOneRef, + ); + }, + [listenerId, enabled, refs, store], ); - const handleClickOutside = useRecoilCallback( - ({ snapshot }) => - (event: MouseEvent | TouchEvent) => { - const clickOutsideListenerIsActivated = snapshot - .getLoadable(clickOutsideListenerIsActivatedState) - .getValue(); + const handleClickOutside = useCallback( + (event: MouseEvent | TouchEvent) => { + const clickOutsideListenerIsActivated = store.get( + clickOutsideListenerIsActivatedComponentState.atomFamily({ + instanceId: listenerId, + }), + ); - const isListening = clickOutsideListenerIsActivated && enabled; + const isListening = clickOutsideListenerIsActivated && enabled; - const isMouseDownInside = snapshot - .getLoadable(clickOutsideListenerIsMouseDownInsideState) - .getValue(); + const isMouseDownInside = store.get( + clickOutsideListenerIsMouseDownInsideComponentState.atomFamily({ + instanceId: listenerId, + }), + ); - const hasMouseDownHappened = snapshot - .getLoadable(clickOutsideListenerMouseDownHappenedState) - .getValue(); + const hasMouseDownHappened = store.get( + clickOutsideListenerMouseDownHappenedComponentState.atomFamily({ + instanceId: listenerId, + }), + ); - const clickedElement = event.target as HTMLElement; - let isClickedOnExcluded = false; - let currentElement: HTMLElement | null = clickedElement; + const clickedElement = event.target as HTMLElement; + let isClickedOnExcluded = false; + let currentElement: HTMLElement | null = clickedElement; - while (currentElement) { - const currentDataAttributes = currentElement.dataset; - const isGloballyExcluded = - currentDataAttributes?.globallyPreventClickOutside === 'true'; + while (currentElement) { + const currentDataAttributes = currentElement.dataset; + const isGloballyExcluded = + currentDataAttributes?.globallyPreventClickOutside === 'true'; - const clickOutsideId = currentDataAttributes?.clickOutsideId; + const clickOutsideId = currentDataAttributes?.clickOutsideId; - isClickedOnExcluded = - isGloballyExcluded || - (isDefined(clickOutsideId) && - isDefined(excludedClickOutsideIds) && - excludedClickOutsideIds.includes(clickOutsideId)); + isClickedOnExcluded = + isGloballyExcluded || + (isDefined(clickOutsideId) && + isDefined(excludedClickOutsideIds) && + excludedClickOutsideIds.includes(clickOutsideId)); - if (isClickedOnExcluded) { - break; - } - - currentElement = currentElement.parentElement; + if (isClickedOnExcluded) { + break; } - const clickedOnAtLeastOneRef = refs - .filter((ref) => !!ref.current) - .some((ref) => ref.current?.contains(event.target as Node)); + currentElement = currentElement.parentElement; + } - const shouldTrigger = - isListening && - hasMouseDownHappened && - !clickedOnAtLeastOneRef && - !isMouseDownInside && - !isClickedOnExcluded; + const clickedOnAtLeastOneRef = refs + .filter((ref) => !!ref.current) + .some((ref) => ref.current?.contains(event.target as Node)); - if (CLICK_OUTSIDE_DEBUG_MODE) { - // eslint-disable-next-line no-console - console.log('click outside compare ref', { - listenerId, - shouldTrigger, - clickedOnAtLeastOneRef, - isMouseDownInside, - isListening, - hasMouseDownHappened, - isClickedOnExcluded, - enabled, - event, - }); - } + const shouldTrigger = + isListening && + hasMouseDownHappened && + !clickedOnAtLeastOneRef && + !isMouseDownInside && + !isClickedOnExcluded; - if (shouldTrigger) { - callback(event); - } - }, - [ - clickOutsideListenerIsActivatedState, - enabled, - clickOutsideListenerIsMouseDownInsideState, - clickOutsideListenerMouseDownHappenedState, - refs, - excludedClickOutsideIds, - callback, - listenerId, - ], + if (CLICK_OUTSIDE_DEBUG_MODE) { + // eslint-disable-next-line no-console + console.log('click outside compare ref', { + listenerId, + shouldTrigger, + clickedOnAtLeastOneRef, + isMouseDownInside, + isListening, + hasMouseDownHappened, + isClickedOnExcluded, + enabled, + event, + }); + } + + if (shouldTrigger) { + callback(event); + } + }, + [listenerId, enabled, refs, excludedClickOutsideIds, callback, store], ); useEffect(() => { diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState.ts index b9807f48a8b..935a1af492c 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState.ts @@ -1,8 +1,8 @@ import { ClickOutsideListenerComponentInstanceContext } from '@/ui/utilities/pointer-event/states/contexts/ClickOutsideListenerComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const clickOutsideListenerIsActivatedComponentState = - createComponentState({ + createComponentStateV2({ key: 'clickOutsideListenerIsActivatedComponentState', defaultValue: true, componentInstanceContext: ClickOutsideListenerComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState.ts index da32d5de2b8..31288caddae 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState.ts @@ -1,8 +1,8 @@ import { ClickOutsideListenerComponentInstanceContext } from '@/ui/utilities/pointer-event/states/contexts/ClickOutsideListenerComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const clickOutsideListenerIsMouseDownInsideComponentState = - createComponentState({ + createComponentStateV2({ key: 'clickOutsideListenerIsMouseDownInsideComponentState', defaultValue: false, componentInstanceContext: ClickOutsideListenerComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState.ts index da7ed51e85f..6f48a9b887f 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState.ts @@ -1,8 +1,8 @@ import { ClickOutsideListenerComponentInstanceContext } from '@/ui/utilities/pointer-event/states/contexts/ClickOutsideListenerComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const clickOutsideListenerMouseDownHappenedComponentState = - createComponentState({ + createComponentStateV2({ key: 'clickOutsideListenerMouseDownHappenedComponentState', defaultValue: false, componentInstanceContext: ClickOutsideListenerComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/components/ScrollWrapper.tsx b/packages/twenty-front/src/modules/ui/utilities/scroll/components/ScrollWrapper.tsx index 51544cfafeb..99ce167046b 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/components/ScrollWrapper.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/components/ScrollWrapper.tsx @@ -5,7 +5,7 @@ import { ScrollWrapperComponentInstanceContext } from '@/ui/utilities/scroll/sta import { scrollWrapperScrollBottomComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState'; import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState'; import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; const StyledScrollWrapper = styled.div` &.scroll-wrapper-x-enabled { @@ -35,17 +35,17 @@ export const ScrollWrapper = ({ defaultEnableXScroll = true, defaultEnableYScroll = true, }: ScrollWrapperProps) => { - const setScrollTop = useSetRecoilComponentState( + const setScrollTop = useSetRecoilComponentStateV2( scrollWrapperScrollTopComponentState, componentInstanceId, ); - const setScrollLeft = useSetRecoilComponentState( + const setScrollLeft = useSetRecoilComponentStateV2( scrollWrapperScrollLeftComponentState, componentInstanceId, ); - const setScrollBottom = useSetRecoilComponentState( + const setScrollBottom = useSetRecoilComponentStateV2( scrollWrapperScrollBottomComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/hooks/useScrollRestoration.ts b/packages/twenty-front/src/modules/ui/utilities/scroll/hooks/useScrollRestoration.ts index 9f3fb07f78f..5c4bc06f1fd 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/hooks/useScrollRestoration.ts +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/hooks/useScrollRestoration.ts @@ -1,6 +1,6 @@ import { SCROLL_RESTORATION_TOP_THRESHOLD_PX } from '@/ui/utilities/scroll/constants/ScrollRestorationTopThreshold'; import { scrollWrapperScrollTopComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useCallback, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; @@ -10,7 +10,7 @@ export const useScrollRestoration = (componentInstanceId: string) => { const storageKey = `scroll-${location.pathname}`; const [isRestoring, setIsRestoring] = useState(false); - const scrollTop = useRecoilComponentValue( + const scrollTop = useRecoilComponentValueV2( scrollWrapperScrollTopComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState.ts index f4273632938..5fdf869ac03 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState.ts @@ -1,8 +1,8 @@ import { ScrollWrapperComponentInstanceContext } from '@/ui/utilities/scroll/states/contexts/ScrollWrapperComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const scrollWrapperScrollBottomComponentState = - createComponentState({ + createComponentStateV2({ key: 'scrollWrapperScrollBottomComponentState', defaultValue: 0, componentInstanceContext: ScrollWrapperComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState.ts index cebbe5558e7..f41f87ab19f 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState.ts @@ -1,8 +1,8 @@ import { ScrollWrapperComponentInstanceContext } from '@/ui/utilities/scroll/states/contexts/ScrollWrapperComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const scrollWrapperScrollLeftComponentState = - createComponentState({ + createComponentStateV2({ key: 'scrollWrapperScrollLeftComponentState', defaultValue: 0, componentInstanceContext: ScrollWrapperComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState.ts b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState.ts index db6ce50dadd..23edb731b94 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState.ts +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/states/scrollWrapperScrollTopComponentState.ts @@ -1,8 +1,8 @@ import { ScrollWrapperComponentInstanceContext } from '@/ui/utilities/scroll/states/contexts/ScrollWrapperComponentInstanceContext'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2'; export const scrollWrapperScrollTopComponentState = - createComponentState({ + createComponentStateV2({ key: 'scrollWrapperScrollTopComponentState', defaultValue: 0, componentInstanceContext: ScrollWrapperComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md b/packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md new file mode 100644 index 00000000000..1b268a29eb8 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md @@ -0,0 +1,427 @@ +# Recoil → Jotai Migration Plan + +## Overview + +- **429 state files** + **69 selector files** = ~500 total Recoil-based state definitions +- **5 already migrated** (dropdown component states) +- Organized into **14 PRs** by module area, starting from lowest-level (fewest dependencies) to highest-level + +## State Creation Patterns (Recoil → Jotai) + +| Recoil Pattern | Count | Jotai Equivalent | +|---|---|---| +| `createState()` | ~100 global states | `createStateV2()` → Jotai `atom()` | +| `createComponentState()` | ~200 component states | `createComponentStateV2()` → Jotai `atom()` with instance cache | +| `createComponentFamilyState()` | ~27 component family states | New `createComponentFamilyStateV2()` → Jotai `atom()` with two-level cache | +| `createComponentSelector()` | ~28 component selectors | New `createComponentSelectorV2()` → Jotai derived `atom()` | +| `createComponentFamilySelector()` | ~9 component family selectors | New `createComponentFamilySelectorV2()` | +| Raw `atom()` from recoil | 3 | Jotai `atom()` via `createStateV2()` | +| Raw `atomFamily()` from recoil | 3 | Jotai `atom()` via `createFamilyStateV2()` | +| Raw `selector()` from recoil | 11 | Jotai derived `atom()` via `createSelectorV2()` | +| Raw `selectorFamily()` from recoil | ~21 | Jotai derived `atom()` via family pattern | + +## Hook Migration + +| Recoil Hook | Jotai Equivalent | +|---|---| +| `useRecoilValue()` | `useRecoilValueV2()` (already exists, wraps `useAtomValue`) | +| `useRecoilState()` | `useRecoilStateV2()` (already exists, wraps `useAtom`) | +| `useSetRecoilState()` | `useSetRecoilStateV2()` (already exists, wraps `useSetAtom`) | +| `useResetRecoilState()` | `jotaiStore.set(atom, defaultValue)` or new `useResetAtom` wrapper | +| `useRecoilComponentValue()` | `useRecoilComponentValueV2()` (already exists) | +| `useSetRecoilComponentState()` | `useSetRecoilComponentStateV2()` (already exists) | +| `useRecoilComponentState()` | `useRecoilComponentStateV2()` (already exists) | +| `useRecoilCallback()` | `useCallback()` + `jotaiStore.get/set` | + +## Infrastructure Needed Before Migration + +Before starting the PRs below, these Jotai V2 utilities need to be created (if not already): + +- [x] `createStateV2()` — global atom +- [x] `createFamilyStateV2()` — global atom family +- [x] `createSelectorV2()` — global derived atom +- [x] `createComponentStateV2()` — instance-scoped atom +- [ ] `createComponentFamilyStateV2()` — instance-scoped atom family +- [ ] `createComponentSelectorV2()` — instance-scoped derived atom +- [ ] `createComponentFamilySelectorV2()` — instance-scoped derived atom family +- [ ] `createFamilySelectorV2()` — global derived atom family (for raw selectorFamily replacements) + +--- + +## PR 1: UI Utilities — Focus, Hotkeys, Loading, Pointer Events, Scroll + +Low-level utilities used across the entire app. No dependencies on other app states. + +### States (16 files) +``` +ui/utilities/focus/states/focusStackState.ts +ui/utilities/hotkey/states/internal/pendingHotkeysState.ts +ui/utilities/loading-state/states/currentPageLocationState.ts +ui/utilities/pointer-event/states/clickOutsideListenerIsActivatedComponentState.ts +ui/utilities/pointer-event/states/clickOutsideListenerIsMouseDownInsideComponentState.ts +ui/utilities/pointer-event/states/clickOutsideListenerMouseDownHappenedComponentState.ts +ui/utilities/scroll/states/scrollWrapperScrollBottomComponentState.ts +ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState.ts +ui/utilities/scroll/states/scrollWrapperScrollTopComponentState.ts +``` + +### Selectors (3 files) +``` +ui/utilities/focus/states/currentFocusIdSelector.ts +ui/utilities/focus/states/currentFocusedItemSelector.ts +ui/utilities/focus/states/currentGlobalHotkeysConfigSelector.ts +``` + +### Notes +- `focusStackState` is used by the dropdown hooks — needs to be migrated to unblock removing `useRecoilCallback` from dropdown hooks +- Focus selectors depend on `focusStackState` + +--- + +## PR 2: UI Layout — Selectable List, Modal, Tab List, Table + +Core layout primitives used by record-table, record-board, and many other modules. + +### States (7 files) +``` +ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState.ts +ui/layout/selectable-list/states/selectableItemIdsComponentState.ts +ui/layout/selectable-list/states/selectedItemIdComponentState.ts +ui/layout/modal/states/isModalOpenedComponentState.ts +ui/layout/tab-list/states/activeTabIdComponentState.ts +ui/layout/table/states/sortedFieldByTableFamilyState.ts +``` + +### Selectors (1 file) +``` +ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector.ts +``` + +--- + +## PR 3: UI Navigation, Theme, Input, Feedback, Field + +### States (12 files) +``` +ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState.ts +ui/navigation/navigation-drawer/states/isNavigationSectionOpenFamilyState.ts +ui/navigation/states/isNavigationDrawerExpanded.ts (raw Recoil atom) +ui/navigation/states/navigationDrawerWidthState.ts +ui/navigation/states/navigationMemorizedUrlState.ts +ui/navigation/states/shouldNavigateBackToMemorizedUrlOnSaveState.ts +ui/navigation/step-bar/states/stepBarInternalState.ts +ui/theme/states/persistedColorSchemeState.ts +ui/input/states/iconPickerState.ts +ui/input/states/iconPickerVisibleCountState.ts +ui/feedback/dialog-manager/states/dialogInternalComponentState.ts +ui/feedback/snack-bar-manager/states/snackBarInternalComponentState.ts +ui/field/display/states/filePreviewState.ts +``` + +--- + +## PR 4: Auth, Domain Manager, App, Captcha, Chrome Extension + +Core authentication and app-level states. Very widely imported (~195 external imports for auth). + +### States (25 files) +``` +auth/states/availableWorkspacesState.ts +auth/states/billingCheckoutSessionState.ts +auth/states/currentUserState.ts +auth/states/currentUserWorkspaceState.ts +auth/states/currentWorkspaceDeletedMembersState.ts +auth/states/currentWorkspaceMemberState.ts +auth/states/currentWorkspaceMembersState.ts +auth/states/currentWorkspaceState.ts +auth/states/isCurrentUserLoadedState.ts +auth/states/lastAuthenticatedMethodState.ts +auth/states/loginTokenState.ts +auth/states/previousUrlState.ts +auth/states/signInUpModeState.ts +auth/states/signInUpStepState.ts +auth/states/tokenPairState.ts +auth/states/workspacePublicDataState.ts +domain-manager/states/domainConfigurationState.ts +domain-manager/states/lastAuthenticatedWorkspaceDomainState.ts +app/states/isAppEffectRedirectEnabledState.ts +app/states/verifyEmailRedirectPathState.ts +captcha/states/captchaTokenState.ts +captcha/states/isCaptchaScriptLoadedState.ts +captcha/states/isRequestingCaptchaTokenState.ts +chrome-extension-sidecar/states/isLoadingTokensFromExtensionState.ts +workspace/states/workspaceAuthBypassProvidersState.ts +workspace/states/workspaceAuthProvidersState.ts +workspace/states/workspaceBypassModeState.ts +``` + +### Selectors (2 files) +``` +auth/states/isImpersonatingState.ts (raw Recoil selector) +auth/states/objectPermissionsFamilySelector.ts +``` + +--- + +## PR 5: Client Config + +26 simple global states, mostly boolean feature flags. Low risk. + +### States (26 files) +``` +client-config/states/aiModelsState.ts +client-config/states/apiConfigState.ts +client-config/states/appVersionState.ts +client-config/states/authProvidersState.ts +client-config/states/billingState.ts +client-config/states/calendarBookingPageIdState.ts +client-config/states/canManageFeatureFlagsState.ts +client-config/states/captchaState.ts +client-config/states/chromeExtensionIdState.ts +client-config/states/clientConfigApiStatusState.ts +client-config/states/isAnalyticsEnabledState.ts +client-config/states/isAttachmentPreviewEnabledState.ts +client-config/states/isClickHouseConfiguredState.ts +client-config/states/isCloudflareIntegrationEnabledState.ts +client-config/states/isConfigVariablesInDbEnabledState.ts +client-config/states/isDeveloperDefaultSignInPrefilledState.ts +client-config/states/isEmailVerificationRequiredState.ts +client-config/states/isEmailingDomainsEnabledState.ts +client-config/states/isGoogleCalendarEnabledState.ts +client-config/states/isGoogleMessagingEnabledState.ts +client-config/states/isImapSmtpCaldavEnabledState.ts +client-config/states/isMicrosoftCalendarEnabledState.ts +client-config/states/isMicrosoftMessagingEnabledState.ts +client-config/states/isMultiWorkspaceEnabledState.ts +client-config/states/sentryConfigState.ts +client-config/states/supportChatState.ts +``` + +--- + +## PR 6: Object Metadata + +Core metadata states used by record-table, record-board, views, etc. + +### States (3 files) +``` +object-metadata/states/lastFieldMetadataItemUpdateState.ts +object-metadata/states/objectMetadataItemsState.ts +object-metadata/states/shouldAppBeLoadingState.ts +``` + +### Selectors (12 files) +``` +object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector.ts +object-metadata/states/availableFieldMetadataItemsForSortFamilySelector.ts +object-metadata/states/fieldMetadataItemByIdSelector.ts +object-metadata/states/flattenedFieldMetadataItemsSelector.ts +object-metadata/states/flattenedReadableFieldMetadataItemIdsSelector.ts +object-metadata/states/isFieldMetadataItemFilterableAndSortableSelector.ts +object-metadata/states/isFieldMetadataItemLabelIdentifierSelector.ts +object-metadata/states/labelIdentifierFieldMetadataItemSelector.ts +object-metadata/states/objectMetadataItemFamilySelector.ts +object-metadata/states/objectMetadataItemsByNamePluralMapSelector.ts +object-metadata/states/objectMetadataItemsByNameSingularMapSelector.ts +object-metadata/states/objectMetadataItemsBySingularNameSelector.ts +``` + +### Notes +- Object metadata selectors are raw `selectorFamily()` from Recoil — needs `createFamilySelectorV2()` utility +- Very widely depended on (~135 external imports) + +--- + +## PR 7: Object Record — Record Store + +The central record store, depended on by almost everything (~190 external imports). + +### States (2 files) +``` +object-record/states/recordStoreFamilyState.ts (raw atomFamily) +object-record/record-store/states/recordStoreFamilyState.ts +``` + +### Selectors (6 files) +``` +object-record/record-store/states/selectors/recordStoreFamilySelector.ts +object-record/record-store/states/selectors/recordStoreFieldValueSelector.ts +object-record/record-store/states/selectors/recordStoreIdentifierSelector.ts +object-record/record-store/states/selectors/recordStoreIdentifiersSelector.ts +object-record/record-store/states/selectors/recordStoreMorphOneToManyValueWithObjectNameFamilySelector.ts +object-record/record-store/states/selectors/recordStoreRecordsSelector.ts +``` + +--- + +## PR 8: Object Record — Filters, Sorts, Groups, Drag, Field, Calendar, Board, Picker + +### States (~80 files) +``` +object-record/object-filter-dropdown/states/ (12 files) +object-record/object-sort-dropdown/states/ (3 files) +object-record/record-filter/states/ (2 files) +object-record/record-filter-group/states/ (1 file) +object-record/record-sort/states/ (1 file) +object-record/record-group/states/ (3 files) +object-record/record-drag/states/ (7 files) +object-record/record-field/states/ (7 files) +object-record/record-field-list/states/ (2 files) +object-record/record-calendar/states/ (6 files) +object-record/record-board/states/ (18 files) +object-record/record-picker/states/ (15 files) +object-record/record-right-drawer/states/ (2 files) +object-record/record-merge/states/ (2 files) +object-record/record-title-cell/states/ (1 file) +object-record/advanced-filter/states/ (1 selector) +``` + +### Selectors (~20 files) +``` +object-record/record-group/states/selectors/ (6 files) +object-record/record-index/states/selectors/ (5 files) +object-record/record-board/states/selectors/ (2 files) +object-record/record-calendar/states/selectors/ (2 files) +object-record/record-picker/states/selectors/ (5 files) +object-record/record-field/states/ (1 selector) +object-record/object-filter-dropdown/states/ (1 selector) +``` + +--- + +## PR 9: Object Record — Record Table + +Largest single module (40 states + 10 selectors). + +### States (~40 files) +``` +object-record/record-table/states/ (all component states) +object-record/record-table/record-table-row/states/ +object-record/record-table/record-table-footer/states/ +object-record/record-table/record-table-section/states/ +object-record/record-table/virtualization/states/ +``` + +### Selectors (~10 files) +``` +object-record/record-table/states/selectors/ +object-record/record-table/record-table-row/states/ +object-record/record-table/virtualization/states/ +``` + +--- + +## PR 10: Object Record — Record Index + +### States (~17 files) +``` +object-record/record-index/states/ (all files) +``` + +### Selectors (~5 files) +``` +object-record/record-index/states/selectors/ (all files) +``` + +--- + +## PR 11: Views, Favorites, Navigation, Prefetch, Localization + +### States (~30 files) +``` +views/states/ (10 files) +views/view-picker/states/ (13 files) +favorites/favorite-folder-picker/states/ (4 files) +navigation/states/ (3 files) +prefetch/states/ (4 files) +localization/states/ (1 file) +information-banner/states/ (1 file) +``` + +### Selectors (~8 files) +``` +views/states/selectors/ (6 files) +favorites/states/selectors/ (1 file) +favorites/favorite-folder-picker/states/selectors/ (1 file) +``` + +--- + +## PR 12: Context Store, Command Menu, Action Menu + +### States (~22 files) +``` +context-store/states/ (10 files) +command-menu/states/ (10 files) +command-menu/pages/*/states/ (10 files) +action-menu/states/ (1 file) +action-menu/actions/states/ (1 file) +``` + +--- + +## PR 13: Workflow, Page Layout, AI, Activities + +### States (~45 files) +``` +workflow/states/ (6 files) +workflow/workflow-diagram/states/ (12 files) +workflow/workflow-steps/states/ (7 files) +workflow/workflow-variables/states/ (2 files) +page-layout/states/ (17 files) +page-layout/widgets/states/ (10 files) +ai/states/ (2 files) +activities/states/ (5 files) +blocknote-editor/states/ (1 file) +``` + +### Selectors (~3 files) +``` +workflow/states/selectors/ (2 files) +workflow/workflow-steps/filters/states/ (1 selector) +page-layout/states/selectors/ (1 file) +``` + +--- + +## PR 14: Settings, SSE, Spreadsheet Import, Misc + +Leaf modules with few external consumers. + +### States (~30 files) +``` +settings/admin-panel/ (4 files) +settings/data-model/ (2 files) +settings/developers/ (1 file) +settings/domains/ (3 files) +settings/logic-functions/ (1 file) +settings/playground/ (1 file) +settings/roles/ (4 files) +settings/security/ (2 files) +sse-db-event/states/ (9 files) +spreadsheet-import/states/ (4 files) +``` + +### Selectors (1 file) +``` +settings/roles/states/settingsAllRolesSelector.ts +``` + +--- + +## Testing Strategy + +Each PR should: +1. Migrate state definitions (swap `createState` → `createStateV2`, etc.) +2. Update all consumer hooks (swap `useRecoilValue` → `useRecoilValueV2`, etc.) +3. Update `useRecoilCallback` → `useCallback` + `jotaiStore.get/set` +4. Run `npx nx typecheck twenty-front` +5. Run `npx nx test twenty-front` for affected test files +6. Verify affected Storybook stories + +## Notes + +- The `beforeEach: resetJotaiStore()` in `.storybook/preview.tsx` handles Storybook isolation globally +- Unit tests need `JotaiProvider store={jotaiStore}` in their wrappers and `beforeEach` resets for affected atoms +- Some states use `effects` (e.g., `cookieStorageEffect`, `localStorageEffect`) — these need Jotai `onMount` equivalents +- States with `useResetRecoilState` need a wrapper or direct `jotaiStore.set(atom, defaultValue)` diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyStateV2.ts new file mode 100644 index 00000000000..b6816ebac0e --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyStateV2.ts @@ -0,0 +1,31 @@ +import { useAtom } from 'jotai'; + +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { type ComponentFamilyStateV2 } from '@/ui/utilities/state/jotai/types/ComponentFamilyStateV2'; + +export const useRecoilComponentFamilyStateV2 = ( + componentState: ComponentFamilyStateV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +): [ + StateType, + (value: StateType | ((prev: StateType) => StateType)) => void, +] => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useAtom(componentState.atomFamily({ instanceId, familyKey })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2.ts new file mode 100644 index 00000000000..02635bdd5ae --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2.ts @@ -0,0 +1,28 @@ +import { useAtomValue } from 'jotai'; + +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { type ComponentFamilyStateV2 } from '@/ui/utilities/state/jotai/types/ComponentFamilyStateV2'; + +export const useRecoilComponentFamilyValueV2 = ( + componentState: ComponentFamilyStateV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +): StateType => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useAtomValue(componentState.atomFamily({ instanceId, familyKey })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useSetRecoilComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useSetRecoilComponentFamilyStateV2.ts new file mode 100644 index 00000000000..62a53ddd4a5 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/hooks/useSetRecoilComponentFamilyStateV2.ts @@ -0,0 +1,28 @@ +import { useSetAtom } from 'jotai'; + +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { type ComponentFamilyStateV2 } from '@/ui/utilities/state/jotai/types/ComponentFamilyStateV2'; + +export const useSetRecoilComponentFamilyStateV2 = ( + componentState: ComponentFamilyStateV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +): ((value: ValueType | ((prev: ValueType) => ValueType)) => void) => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useSetAtom(componentState.atomFamily({ instanceId, familyKey })); +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/types/ComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/jotai/types/ComponentFamilyStateV2.ts new file mode 100644 index 00000000000..c943c7ae2a0 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/types/ComponentFamilyStateV2.ts @@ -0,0 +1,21 @@ +import { type WritableAtom } from 'jotai'; + +import { type ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey'; + +type JotaiWritableAtom = WritableAtom< + ValueType, + [ValueType | ((prev: ValueType) => ValueType)], + void +>; + +export type ComponentFamilyStateKey = ComponentStateKey & { + familyKey: FamilyKey; +}; + +export type ComponentFamilyStateV2 = { + type: 'ComponentFamilyStateV2'; + key: string; + atomFamily: ( + key: ComponentFamilyStateKey, + ) => JotaiWritableAtom; +}; diff --git a/packages/twenty-front/src/modules/ui/utilities/state/jotai/utils/createComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/jotai/utils/createComponentFamilyStateV2.ts new file mode 100644 index 00000000000..e61a2abac83 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/jotai/utils/createComponentFamilyStateV2.ts @@ -0,0 +1,57 @@ +import { atom } from 'jotai'; + +import { type ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { + type ComponentFamilyStateKey, + type ComponentFamilyStateV2, +} from '@/ui/utilities/state/jotai/types/ComponentFamilyStateV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const createComponentFamilyStateV2 = ({ + key, + defaultValue, + componentInstanceContext, +}: { + key: string; + defaultValue: ValueType; + componentInstanceContext: ComponentInstanceStateContext | null; +}): ComponentFamilyStateV2 => { + if (isDefined(componentInstanceContext)) { + globalComponentInstanceContextMap.set(key, componentInstanceContext); + } + + const atomCache = new Map< + string, + ReturnType['atomFamily']> + >(); + + const familyFunction = ({ + instanceId, + familyKey, + }: ComponentFamilyStateKey): ReturnType< + ComponentFamilyStateV2['atomFamily'] + > => { + const familyKeyStr = + typeof familyKey === 'string' ? familyKey : JSON.stringify(familyKey); + + const cacheKey = `${instanceId}__${familyKeyStr}`; + const existing = atomCache.get(cacheKey); + + if (existing !== undefined) { + return existing; + } + + const baseAtom = atom(defaultValue); + baseAtom.debugLabel = `${key}__${cacheKey}`; + atomCache.set(cacheKey, baseAtom); + + return baseAtom; + }; + + return { + type: 'ComponentFamilyStateV2', + key, + atomFamily: familyFunction, + }; +}; diff --git a/packages/twenty-front/src/modules/users/components/MetadataProviderEffect.tsx b/packages/twenty-front/src/modules/users/components/MetadataProviderEffect.tsx index 7fdb9c77617..d18ebb7a047 100644 --- a/packages/twenty-front/src/modules/users/components/MetadataProviderEffect.tsx +++ b/packages/twenty-front/src/modules/users/components/MetadataProviderEffect.tsx @@ -15,7 +15,7 @@ import { type PageLayout } from '@/page-layout/types/PageLayout'; import { transformPageLayout } from '@/page-layout/utils/transformPageLayout'; import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState'; import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { coreViewsState } from '@/views/states/coreViewState'; import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations'; import { type ColorScheme } from '@/workspace-member/types/WorkspaceMember'; @@ -57,6 +57,8 @@ export const MetadataProviderEffect = () => { const { initializeFormatPreferences } = useInitializeFormatPreferences(); const isLoggedIn = useIsLogged(); + const store = useStore(); + const updateLocaleCatalog = useRecoilCallback( ({ snapshot, set }) => async (newLocale: keyof typeof APP_LOCALES) => { @@ -68,11 +70,11 @@ export const MetadataProviderEffect = () => { localeCatalog: localeCatalog || enUS, }; set(dateLocaleState, newValue); - jotaiStore.set(dateLocaleStateV2.atom, newValue); + store.set(dateLocaleStateV2.atom, newValue); }); } }, - [], + [store], ); const setCoreViews = useRecoilCallback( diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx index 12b82a01c49..0a5d094e3b7 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx @@ -4,8 +4,8 @@ import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS } from '@/views/constants/ViewBarFilterBottomMenuItemIds'; import { useSetRecordFilterUsedInAdvancedFilterDropdownRow } from '@/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow'; @@ -35,8 +35,8 @@ export const ViewBarFilterDropdownAdvancedFilterButton = () => { const { t } = useLingui(); - const isSelected = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelected = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS.ADVANCED_FILTER, ); diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx index 1e4e30908ba..eec556f58d0 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx @@ -1,6 +1,6 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; @@ -29,8 +29,8 @@ export const ViewBarFilterDropdownAnyFieldSearchButtonMenuItem = ({ objectFilterDropdownSearchInputComponentState, ); - const isSelected = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelected = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS.SEARCH, ); diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownFieldSelectMenuItem.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownFieldSelectMenuItem.tsx index 36c8cfa7e0a..34ea269709c 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownFieldSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownFieldSelectMenuItem.tsx @@ -2,8 +2,8 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; -import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyValueV2'; import { useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown } from '@/views/hooks/useInitializeFilterOnFieldMetadataItemFromViewBarFilterDropdown'; import { useIcons } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; @@ -17,8 +17,8 @@ export const ViewBarFilterDropdownFieldSelectMenuItem = ({ }: ViewBarFilterDropdownFieldSelectMenuItemProps) => { const { resetSelectedItem } = useSelectableList(FILTER_FIELD_LIST_ID); - const isSelectedItem = useRecoilComponentFamilyValue( - isSelectedItemIdComponentFamilySelector, + const isSelectedItem = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilyState, fieldMetadataItemToSelect.id, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab.ts index 04ca1b13c23..48c00bd9951 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab.ts @@ -2,6 +2,7 @@ import { getIsInputTabDisabled } from '@/command-menu/pages/workflow/step/view-r import { getIsOutputTabDisabled } from '@/command-menu/pages/workflow/step/view-run/utils/getIsOutputTabDisabled'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useStore } from 'jotai'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { WorkflowRunTabId } from '@/workflow/workflow-steps/types/WorkflowRunTabId'; import { useRecoilCallback } from 'recoil'; @@ -9,8 +10,10 @@ import { isDefined } from 'twenty-shared/utils'; import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow'; export const useSetInitialWorkflowRunRightDrawerTab = () => { + const store = useStore(); + const setInitialWorkflowRunRightDrawerTab = useRecoilCallback( - ({ snapshot, set }) => + ({ snapshot }) => ({ workflowSelectedNode, stepExecutionStatus, @@ -23,11 +26,12 @@ export const useSetInitialWorkflowRunRightDrawerTab = () => { commandMenuPageInfoState, ); - const activeWorkflowRunRightDrawerTab = getSnapshotValue( - snapshot, - activeTabIdComponentState.atomFamily({ - instanceId: commandMenuPageInfo.instanceId, - }), + const activeTabIdAtom = activeTabIdComponentState.atomFamily({ + instanceId: commandMenuPageInfo.instanceId, + }); + + const activeWorkflowRunRightDrawerTab = store.get( + activeTabIdAtom, ) as WorkflowRunTabId | null; const isInputTabDisabled = getIsInputTabDisabled({ @@ -43,12 +47,7 @@ export const useSetInitialWorkflowRunRightDrawerTab = () => { ? WorkflowRunTabId.NODE : WorkflowRunTabId.OUTPUT; - set( - activeTabIdComponentState.atomFamily({ - instanceId: commandMenuPageInfo.instanceId, - }), - defaultTabId, - ); + store.set(activeTabIdAtom, defaultTabId); return; } @@ -59,15 +58,10 @@ export const useSetInitialWorkflowRunRightDrawerTab = () => { (isOutputTabDisabled && activeWorkflowRunRightDrawerTab === WorkflowRunTabId.OUTPUT) ) { - set( - activeTabIdComponentState.atomFamily({ - instanceId: commandMenuPageInfo.instanceId, - }), - WorkflowRunTabId.NODE, - ); + store.set(activeTabIdAtom, WorkflowRunTabId.NODE); } }, - [], + [store], ); return { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepFooter.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepFooter.tsx index 6de66453f17..0b182fe1ecc 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepFooter.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepFooter.tsx @@ -5,7 +5,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; import { useDuplicateStep } from '@/workflow/workflow-steps/hooks/useDuplicateStep'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; @@ -98,7 +98,7 @@ export const WorkflowStepFooter = ({ } }; - const selectedItemId = useRecoilComponentValue( + const selectedItemId = useRecoilComponentValueV2( selectedItemIdComponentState, dropdownId, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx index 3773c0186fc..ce839905268 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowEditActionAiAgent.tsx @@ -3,7 +3,7 @@ import { useAiModelOptions } from '@/ai/hooks/useAiModelOptions'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; import { type WorkflowAiAgentAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; @@ -204,7 +204,7 @@ export const WorkflowEditActionAiAgent = ({ }, ]; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, componentInstanceId, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx index 610530bad3e..aed74ab81de 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx @@ -18,6 +18,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowCodeEditor } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowCodeEditor'; import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields'; @@ -97,7 +98,7 @@ export const WorkflowEditActionCode = ({ const isMobile = useIsMobile(); const logicFunctionId = action.settings.input.logicFunctionId; const fullScreenFocusId = `code-editor-fullscreen-${logicFunctionId}`; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx index be410ee7ea7..b07ab978287 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/components/WorkflowEditActionHttpRequest.tsx @@ -8,7 +8,7 @@ import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionBut import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter'; import { getBodyTypeFromHeaders } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/getBodyTypeFromHeaders'; import { isMethodWithBody } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/isMethodWithBody'; @@ -90,7 +90,7 @@ export const WorkflowEditActionHttpRequest = ({ }: WorkflowEditActionHttpRequestProps) => { const { t } = useLingui(); const theme = useTheme(); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, WORKFLOW_HTTP_REQUEST_TAB_LIST_COMPONENT_ID, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts index c8577129143..f46cea18fad 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts @@ -3,6 +3,7 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; @@ -55,7 +56,7 @@ export const useVariableDropdown = ({ const setWorkflowSelectedNode = useSetRecoilComponentState( workflowSelectedNodeComponentState, ); - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, 'workflow-logic-function-tab-list-component-id', ); diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx index 876e8162e11..3403ff0003e 100644 --- a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx +++ b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx @@ -6,7 +6,7 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath } from 'twenty-shared/utils'; @@ -31,7 +31,7 @@ const StyledLink = styled(Link)` `; export const SettingsAI = () => { - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_AI_TABS.COMPONENT_INSTANCE_ID, ); diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAgentForm.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAgentForm.tsx index 857028b8c01..7bb4a452ce7 100644 --- a/packages/twenty-front/src/pages/settings/ai/SettingsAgentForm.tsx +++ b/packages/twenty-front/src/pages/settings/ai/SettingsAgentForm.tsx @@ -13,7 +13,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { AppPath, SettingsPath } from 'twenty-shared/types'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; @@ -70,7 +70,7 @@ export const SettingsAgentForm = ({ mode }: { mode: 'create' | 'edit' }) => { const isCreateMode = mode === 'create'; const tabListComponentId = `${SETTINGS_AGENT_DETAIL_TABS.COMPONENT_INSTANCE_ID}-${agentId}`; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, tabListComponentId, ); diff --git a/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentEvalsTab.tsx b/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentEvalsTab.tsx index 8447cd145f7..35146908fa5 100644 --- a/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentEvalsTab.tsx +++ b/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentEvalsTab.tsx @@ -10,7 +10,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2'; import { useMutation } from '@apollo/client'; import { getOperationName } from '@apollo/client/utilities'; import styled from '@emotion/styled'; @@ -70,7 +70,7 @@ export const SettingsAgentEvalsTab = ({ const navigate = useNavigate(); const tabListComponentId = `${SETTINGS_AGENT_DETAIL_TABS.COMPONENT_INSTANCE_ID}-${agentId}`; - const setActiveTabId = useSetRecoilComponentState( + const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, tabListComponentId, ); diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx index 456a52f24eb..e993c19fff5 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx @@ -14,7 +14,7 @@ import { import { SettingsApplicationDetailSkeletonLoader } from '~/pages/settings/applications/components/SettingsApplicationDetailSkeletonLoader'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { SettingsApplicationDetailContentTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailContentTab'; import { SettingsApplicationDetailAboutTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailAboutTab'; import { SettingsApplicationDetailSettingsTab } from '~/pages/settings/applications/tabs/SettingsApplicationDetailSettingsTab'; @@ -25,7 +25,7 @@ const APPLICATION_DETAIL_ID = 'application-detail-id'; export const SettingsApplicationDetails = () => { const { applicationId = '' } = useParams<{ applicationId: string }>(); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, APPLICATION_DETAIL_ID, ); diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx index 25625fe56f7..c636e3b61d1 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsApplications.tsx @@ -2,7 +2,7 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useLingui } from '@lingui/react/macro'; import { SettingsPath } from 'twenty-shared/types'; @@ -26,7 +26,7 @@ export const SettingsApplications = () => { 'IS_MARKETPLACE_ENABLED' as FeatureFlagKey, ); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, APPLICATIONS_TAB_LIST_ID, ); diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx index 7df68c8315d..62c5672030b 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx @@ -4,7 +4,7 @@ import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFla import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { useMemo, useState } from 'react'; @@ -260,7 +260,7 @@ export const SettingsAvailableApplicationDetails = () => { } }; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, AVAILABLE_APPLICATION_DETAIL_ID, ); diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetailPage.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetailPage.tsx index 9ecc0a839ab..86ed035e5f0 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetailPage.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetailPage.tsx @@ -16,7 +16,7 @@ import { AppPath, SettingsPath } from 'twenty-shared/types'; import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; @@ -61,7 +61,7 @@ export const SettingsObjectDetailPage = () => { objectMetadataItem, }); - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, SETTINGS_OBJECT_DETAIL_TABS.COMPONENT_INSTANCE_ID, ); diff --git a/packages/twenty-front/src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx b/packages/twenty-front/src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx index 46d074516d9..6bc131d9b10 100644 --- a/packages/twenty-front/src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx +++ b/packages/twenty-front/src/pages/settings/logic-functions/SettingsLogicFunctionDetail.tsx @@ -9,7 +9,7 @@ import { SettingsLogicFunctionTriggersTab } from '@/settings/logic-functions/com import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; @@ -46,7 +46,7 @@ export const SettingsLogicFunctionDetail = () => { const instanceId = `${LOGIC_FUNCTION_DETAIL_ID}-${logicFunctionId}`; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, instanceId, ); diff --git a/packages/twenty-front/src/pages/settings/members/SettingsWorkspaceMember.tsx b/packages/twenty-front/src/pages/settings/members/SettingsWorkspaceMember.tsx index 0a428617e37..8e4a3c3b83c 100644 --- a/packages/twenty-front/src/pages/settings/members/SettingsWorkspaceMember.tsx +++ b/packages/twenty-front/src/pages/settings/members/SettingsWorkspaceMember.tsx @@ -13,7 +13,7 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; import { SettingsPath } from 'twenty-shared/types'; @@ -75,7 +75,7 @@ export const SettingsWorkspaceMember = () => { }); const tabListComponentId = `${SETTINGS_WORKSPACE_MEMBER_TABS.COMPONENT_INSTANCE_ID}-${workspaceMemberId}`; - const activeTabId = useRecoilComponentValue( + const activeTabId = useRecoilComponentValueV2( activeTabIdComponentState, tabListComponentId, ); diff --git a/packages/twenty-front/src/pages/settings/profile/appearance/components/LocalePicker.tsx b/packages/twenty-front/src/pages/settings/profile/appearance/components/LocalePicker.tsx index 8e59c0df97f..b46eb477048 100644 --- a/packages/twenty-front/src/pages/settings/profile/appearance/components/LocalePicker.tsx +++ b/packages/twenty-front/src/pages/settings/profile/appearance/components/LocalePicker.tsx @@ -8,7 +8,7 @@ import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util import { Select } from '@/ui/input/components/Select'; import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItems'; -import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { useStore } from 'jotai'; import { useRefreshAllCoreViews } from '@/views/hooks/useRefreshAllCoreViews'; import { useLingui } from '@lingui/react/macro'; import { enUS } from 'date-fns/locale'; @@ -27,6 +27,7 @@ const StyledContainer = styled.div` export const LocalePicker = () => { const { t } = useLingui(); + const store = useStore(); const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState( currentWorkspaceMemberState, ); @@ -70,7 +71,7 @@ export const LocalePicker = () => { localeCatalog: dateFnsLocale || enUS, }; setDateLocale(newDateLocale); - jotaiStore.set(dateLocaleStateV2.atom, newDateLocale); + store.set(dateLocaleStateV2.atom, newDateLocale); await dynamicActivate(value); try {