Jotai 13 (#18178)
## Recoil to Jotai Migration — PR 13: Workflow, Page Layout, AI, Activities, Settings, SSE & Remaining Modules ### Summary This is the 13th PR in the [14-PR migration series](packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md) to replace Recoil with Jotai as the state management library in `twenty-front`. This PR migrates the remaining consumer code across all modules — updating ~1,076 files with ~10k insertions/deletions. ### What changed **New Jotai infrastructure (13 new files):** - `createComponentSelectorV2` and `createComponentFamilySelectorV2` — instance-scoped derived atoms, replacing Recoil's component selectors - 6 new hooks: `useRecoilComponentSelectorValueV2`, `useRecoilComponentFamilySelectorValueV2`, `useRecoilComponentSelectorCallbackStateV2`, `useRecoilComponentFamilySelectorCallbackStateV2`, `useRecoilComponentStateCallbackStateV2`, `useRecoilComponentFamilyStateCallbackStateV2` - New types: `ComponentSelectorV2`, `ComponentFamilySelectorV2`, `SelectorCallbacksV2` - Extended `buildGetHelper` to support the new selector patterns **State definition migrations:** - `createState()` → `createStateV2()` (Jotai `atom()`) - `createFamilyState()` → `createFamilyStateV2()` (Jotai `atom()` with family cache) - Recoil `selector`/`selectorFamily` → Jotai derived `atom()` via V2 utilities **Hook migrations (~2,400 removed, ~1,545 added V2 equivalents):** - `useRecoilValue` → `useRecoilValueV2` - `useRecoilState` → `useRecoilStateV2` - `useSetRecoilState` → `useSetRecoilStateV2` - `useRecoilCallback` → `useCallback` + `jotaiStore.get/set` - `useRecoilComponentValue` → `useRecoilComponentValueV2` - `useSetRecoilComponentState` → `useSetRecoilComponentStateV2` - `useRecoilComponentFamilyValue` → `useRecoilComponentFamilyValueV2` / `useFamilySelectorValueV2` - ~397 direct `import from 'recoil'` removed **Deleted files (9 files):** - `dateLocaleState.ts` — consolidated - `currentAIChatThreadTitleStateV2.ts` — renamed to replace the original - `isCommandMenuOpenedState.ts` — removed - `filesFieldUploadState.ts` — replaced by V2 - `recordStoreFamilySelector.ts`, `recordStoreFieldValueSelector.ts`, `recordStoreRecordsSelector.ts` — replaced by V2 equivalents - `settingsAllRolesSelector.ts` — replaced by `useSettingsAllRoles` hook - `settingsRoleIdsStateV2.ts` — consolidated **Modules migrated:** - Workflow (diagram, steps, variables, filters) - Page Layout (widgets, fields, graph, tabs) - AI (chat, agents, browsing context) - Activities (rich text editor, targets, timeline) - Action Menu (single/multiple record actions) - Command Menu (navigation, history, pages, workflow) - Context Store - Record Board, Record Table, Record Calendar, Record Index - Record Field, Record Filter, Record Sort, Record Group - Record Drag, Record Picker, Record Store - Views (view bar, view picker, filters, sorts) - Settings (roles, permissions, SSO, security, data model, developers, domains) - SSE (event streams, subscriptions) - Spreadsheet Import - Auth, Favorites, Front Components, Information Banner
This commit is contained in:
+2
-2
@@ -10,9 +10,9 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
|
||||
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isFieldMetadataDateKind } from 'twenty-shared/utils';
|
||||
import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
|
||||
@@ -29,7 +29,7 @@ export const ObjectOptionsDropdownCalendarFieldsContent = () => {
|
||||
const { updateCurrentView } = useUpdateCurrentView();
|
||||
const { navigateToDateFieldSettings } = useGetAvailableFieldsForCalendar();
|
||||
|
||||
const setRecordIndexCalendarFieldMetadataId = useSetRecoilState(
|
||||
const setRecordIndexCalendarFieldMetadataId = useSetRecoilStateV2(
|
||||
recordIndexCalendarFieldMetadataIdState,
|
||||
);
|
||||
const availableFieldsForCalendar = objectMetadataItem.fields.filter((field) =>
|
||||
|
||||
+4
-3
@@ -9,9 +9,10 @@ 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Pill } from 'twenty-ui/components';
|
||||
import {
|
||||
IconCalendarMonth,
|
||||
@@ -24,10 +25,10 @@ import { ViewCalendarLayout } from '~/generated-metadata/graphql';
|
||||
|
||||
export const ObjectOptionsDropdownCalendarViewContent = () => {
|
||||
const { resetContent } = useObjectOptionsDropdown();
|
||||
const recordIndexCalendarLayout = useRecoilValue(
|
||||
const recordIndexCalendarLayout = useRecoilValueV2(
|
||||
recordIndexCalendarLayoutState,
|
||||
);
|
||||
const setRecordIndexCalendarLayout = useSetRecoilState(
|
||||
const setRecordIndexCalendarLayout = useSetRecoilStateV2(
|
||||
recordIndexCalendarLayoutState,
|
||||
);
|
||||
const { updateCurrentView } = useUpdateCurrentView();
|
||||
|
||||
+6
-6
@@ -11,16 +11,15 @@ 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 { 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 { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType';
|
||||
import { useDestroyViewFromCurrentState } from '@/views/view-picker/hooks/useDestroyViewFromCurrentState';
|
||||
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
AppTooltip,
|
||||
@@ -55,7 +54,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
}
|
||||
: null;
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
@@ -67,7 +66,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
|
||||
const isDefaultView = currentView?.key === ViewKey.Index;
|
||||
|
||||
const recordIndexCalendarLayout = useRecoilValue(
|
||||
const recordIndexCalendarLayout = useRecoilValueV2(
|
||||
recordIndexCalendarLayoutState,
|
||||
);
|
||||
|
||||
@@ -80,8 +79,9 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
const visibleFieldsCount = visibleBoardFields.length;
|
||||
|
||||
const { destroyViewFromCurrentState } = useDestroyViewFromCurrentState();
|
||||
const setViewPickerReferenceViewId = useSetRecoilComponentState(
|
||||
const setViewPickerReferenceViewId = useSetRecoilComponentStateV2(
|
||||
viewPickerReferenceViewIdComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const handleDelete = () => {
|
||||
|
||||
+2
-2
@@ -8,7 +8,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 { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { useOpenCreateViewDropdown } from '@/views/hooks/useOpenCreateViewDropown';
|
||||
@@ -29,7 +29,7 @@ export const ObjectOptionsDropdownDefaultView = () => {
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const visibleRecordFields = useRecoilComponentValue(
|
||||
const visibleRecordFields = useRecoilComponentSelectorValueV2(
|
||||
visibleRecordFieldsComponentSelector,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
+6
-5
@@ -14,9 +14,10 @@ import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilComponentFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilySelectorValueV2';
|
||||
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { VIEW_GROUP_VISIBLE_OPTIONS_MAX } from 'twenty-shared/constants';
|
||||
@@ -35,15 +36,15 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
viewType,
|
||||
} = useObjectOptionsDropdown();
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const hiddenRecordGroupIds = useRecoilComponentValue(
|
||||
const hiddenRecordGroupIds = useRecoilComponentSelectorValueV2(
|
||||
hiddenRecordGroupIdsComponentSelector,
|
||||
);
|
||||
|
||||
const visibleRecordGroupIds = useRecoilComponentFamilyValue(
|
||||
const visibleRecordGroupIds = useRecoilComponentFamilySelectorValueV2(
|
||||
visibleRecordGroupIdsComponentFamilySelector,
|
||||
viewType,
|
||||
);
|
||||
|
||||
+6
-5
@@ -13,7 +13,6 @@ 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 { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
|
||||
@@ -22,9 +21,9 @@ import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType';
|
||||
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
|
||||
import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconBaselineDensitySmall,
|
||||
@@ -61,11 +60,13 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
||||
[updateCurrentView],
|
||||
);
|
||||
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
const recordIndexCalendarLayout = useRecoilValue(
|
||||
const recordIndexOpenRecordIn = useRecoilValueV2(
|
||||
recordIndexOpenRecordInState,
|
||||
);
|
||||
const recordIndexCalendarLayout = useRecoilValueV2(
|
||||
recordIndexCalendarLayoutState,
|
||||
);
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
|
||||
+4
-2
@@ -12,10 +12,10 @@ 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconLayoutNavbar,
|
||||
@@ -25,7 +25,9 @@ import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
|
||||
export const ObjectOptionsDropdownLayoutOpenInContent = () => {
|
||||
const { onContentChange } = useObjectOptionsDropdown();
|
||||
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
|
||||
const recordIndexOpenRecordIn = useRecoilValueV2(
|
||||
recordIndexOpenRecordInState,
|
||||
);
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
const { setAndPersistOpenRecordIn } = useUpdateObjectViewOptions();
|
||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
|
||||
+6
-6
@@ -3,9 +3,9 @@ import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
|
||||
import { useUpdateViewFromCurrentState } from '@/views/view-picker/hooks/useUpdateViewFromCurrentState';
|
||||
@@ -60,12 +60,12 @@ export const ObjectOptionsDropdownMenuViewName = ({
|
||||
currentView,
|
||||
}: ObjectOptionsDropdownMenuViewNameProps) => {
|
||||
const [viewPickerSelectedIcon, setViewPickerSelectedIcon] =
|
||||
useRecoilComponentState(viewPickerSelectedIconComponentState);
|
||||
useRecoilComponentStateV2(viewPickerSelectedIconComponentState);
|
||||
|
||||
const viewPickerIsPersisting = useRecoilComponentValue(
|
||||
const viewPickerIsPersisting = useRecoilComponentValueV2(
|
||||
viewPickerIsPersistingComponentState,
|
||||
);
|
||||
const setViewPickerIsDirty = useSetRecoilComponentState(
|
||||
const setViewPickerIsDirty = useSetRecoilComponentStateV2(
|
||||
viewPickerIsDirtyComponentState,
|
||||
);
|
||||
|
||||
|
||||
+4
-3
@@ -15,7 +15,8 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
@@ -47,11 +48,11 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const hiddenRecordGroupIds = useRecoilComponentValue(
|
||||
const hiddenRecordGroupIds = useRecoilComponentSelectorValueV2(
|
||||
hiddenRecordGroupIdsComponentSelector,
|
||||
);
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
|
||||
+4
-4
@@ -12,8 +12,8 @@ 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 { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
@@ -27,11 +27,11 @@ import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
export const ObjectOptionsDropdownRecordGroupSortContent = () => {
|
||||
const { currentContentId, onContentChange } = useObjectOptionsDropdown();
|
||||
|
||||
const hiddenRecordGroupIds = useRecoilComponentValue(
|
||||
const hiddenRecordGroupIds = useRecoilComponentSelectorValueV2(
|
||||
hiddenRecordGroupIdsComponentSelector,
|
||||
);
|
||||
|
||||
const [recordGroupSort, setRecordGroupSort] = useRecoilComponentState(
|
||||
const [recordGroupSort, setRecordGroupSort] = useRecoilComponentStateV2(
|
||||
recordIndexRecordGroupSortComponentState,
|
||||
);
|
||||
|
||||
|
||||
+7
-7
@@ -17,9 +17,9 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
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 { 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 { useRecoilComponentFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilySelectorValueV2';
|
||||
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
@@ -48,27 +48,27 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
const recordGroupFieldMetadata = useRecoilComponentValueV2(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const visibleRecordGroupIds = useRecoilComponentFamilyValue(
|
||||
const visibleRecordGroupIds = useRecoilComponentFamilySelectorValueV2(
|
||||
visibleRecordGroupIdsComponentFamilySelector,
|
||||
viewType,
|
||||
);
|
||||
|
||||
const hiddenRecordGroupIds = useRecoilComponentValue(
|
||||
const hiddenRecordGroupIds = useRecoilComponentSelectorValueV2(
|
||||
hiddenRecordGroupIdsComponentSelector,
|
||||
);
|
||||
|
||||
const hideEmptyRecordGroup = useRecoilComponentValue(
|
||||
const hideEmptyRecordGroup = useRecoilComponentValueV2(
|
||||
recordIndexShouldHideEmptyRecordGroupsComponentState,
|
||||
);
|
||||
|
||||
const shouldHideEmptyGroups =
|
||||
hideEmptyRecordGroup ?? currentView?.shouldHideEmptyGroups ?? false;
|
||||
|
||||
const recordGroupSort = useRecoilComponentValue(
|
||||
const recordGroupSort = useRecoilComponentValueV2(
|
||||
recordIndexRecordGroupSortComponentState,
|
||||
);
|
||||
|
||||
|
||||
+4
-4
@@ -1,6 +1,5 @@
|
||||
import { type OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useColumnDefinitionsFromObjectMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromObjectMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
@@ -12,7 +11,8 @@ import { type RecordField } from '@/object-record/record-field/types/RecordField
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { type ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields';
|
||||
import { mapRecordFieldToViewField } from '@/views/utils/mapRecordFieldToViewField';
|
||||
import { produce } from 'immer';
|
||||
@@ -32,7 +32,7 @@ export const useObjectOptionsForBoard = ({
|
||||
recordBoardId,
|
||||
}: useObjectOptionsForBoardParams) => {
|
||||
const [recordIndexFieldDefinitions, setRecordIndexFieldDefinitions] =
|
||||
useRecoilState(recordIndexFieldDefinitionsState);
|
||||
useRecoilStateV2(recordIndexFieldDefinitionsState);
|
||||
|
||||
const { saveViewFields } = useSaveCurrentViewFields();
|
||||
|
||||
@@ -130,7 +130,7 @@ export const useObjectOptionsForBoard = ({
|
||||
],
|
||||
);
|
||||
|
||||
const currentRecordFields = useRecoilComponentValue(
|
||||
const currentRecordFields = useRecoilComponentValueV2(
|
||||
currentRecordFieldsComponentState,
|
||||
recordBoardId,
|
||||
);
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ import { useReorderVisibleRecordFields } from '@/object-record/record-field/hook
|
||||
|
||||
import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields';
|
||||
import { mapRecordFieldToViewField } from '@/views/utils/mapRecordFieldToViewField';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useProcessOptionDropdownDragEnd = (recordTableId: string) => {
|
||||
const { reorderVisibleRecordFields } =
|
||||
@@ -12,8 +12,8 @@ export const useProcessOptionDropdownDragEnd = (recordTableId: string) => {
|
||||
|
||||
const { saveViewFields } = useSaveCurrentViewFields();
|
||||
|
||||
const processOptionDropdownDragEnd: OnDragEndResponder = useRecoilCallback(
|
||||
() => async (result) => {
|
||||
const processOptionDropdownDragEnd: OnDragEndResponder = useCallback(
|
||||
async (result) => {
|
||||
if (
|
||||
!result.destination ||
|
||||
result.destination.index === 1 ||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { objectOptionsDropdownSearchInputComponentState } from '@/object-record/object-options-dropdown/states/objectOptionsDropdownSearchInputComponentState';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
|
||||
import { useMemo } from 'react';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -8,7 +8,7 @@ export const useSearchRecordGroupField = () => {
|
||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
|
||||
const [recordGroupFieldSearchInput, setRecordGroupFieldSearchInput] =
|
||||
useRecoilComponentState(objectOptionsDropdownSearchInputComponentState);
|
||||
useRecoilComponentStateV2(objectOptionsDropdownSearchInputComponentState);
|
||||
|
||||
const filteredRecordGroupFieldMetadataItems = useMemo(() => {
|
||||
const searchInputLowerCase =
|
||||
|
||||
+121
-119
@@ -13,13 +13,15 @@ import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { convertViewTypeToCore } from '@/views/utils/convertViewTypeToCore';
|
||||
import { useGetAvailableFieldsForCalendar } from '@/views/view-picker/hooks/useGetAvailableFieldsForCalendar';
|
||||
import { useGetAvailableFieldsToGroupRecordsBy } from '@/views/view-picker/hooks/useGetAvailableFieldsToGroupRecordsBy';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||
import { ViewCalendarLayout } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useSetViewTypeFromLayoutOptionsMenu = () => {
|
||||
const { updateCurrentView } = useUpdateCurrentView();
|
||||
const setRecordIndexViewType = useSetRecoilState(recordIndexViewTypeState);
|
||||
const setRecordIndexViewType = useSetRecoilStateV2(recordIndexViewTypeState);
|
||||
const { availableFieldsForGrouping } =
|
||||
useGetAvailableFieldsToGroupRecordsBy();
|
||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||
@@ -28,137 +30,137 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
|
||||
|
||||
const { availableFieldsForCalendar } = useGetAvailableFieldsForCalendar();
|
||||
|
||||
const setAndPersistViewType = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewType: ViewType) => {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const existingCoreViews = jotaiStore.get(coreViewsState.atom);
|
||||
const setAndPersistViewType = useCallback(
|
||||
async (viewType: ViewType) => {
|
||||
const currentViewId = store.get(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(currentViewId)) {
|
||||
throw new Error('No view id found');
|
||||
const existingCoreViews = jotaiStore.get(coreViewsState.atom);
|
||||
|
||||
if (!isDefined(currentViewId)) {
|
||||
throw new Error('No view id found');
|
||||
}
|
||||
|
||||
const currentCoreView = existingCoreViews.find(
|
||||
(coreView) => coreView.id === currentViewId,
|
||||
);
|
||||
|
||||
if (!isDefined(currentCoreView)) {
|
||||
throw new Error('No current view found');
|
||||
}
|
||||
|
||||
const currentView = convertCoreViewToView(currentCoreView);
|
||||
|
||||
const updateCurrentViewParams: Partial<GraphQLView> = {};
|
||||
updateCurrentViewParams.type = viewType;
|
||||
|
||||
switch (viewType) {
|
||||
case ViewType.Kanban: {
|
||||
if (availableFieldsForGrouping.length === 0) {
|
||||
throw new Error('No fields for kanban - should not happen');
|
||||
}
|
||||
|
||||
const mainGroupByFieldMetadataId = availableFieldsForGrouping[0].id;
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId =
|
||||
mainGroupByFieldMetadataId;
|
||||
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
mainGroupByFieldMetadataId,
|
||||
},
|
||||
]);
|
||||
await updateCurrentView(updateCurrentViewParams);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentCoreView = existingCoreViews.find(
|
||||
(coreView) => coreView.id === currentViewId,
|
||||
);
|
||||
|
||||
if (!isDefined(currentCoreView)) {
|
||||
throw new Error('No current view found');
|
||||
case ViewType.Table: {
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
|
||||
await updateCurrentView(updateCurrentViewParams);
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentView = convertCoreViewToView(currentCoreView);
|
||||
|
||||
const updateCurrentViewParams: Partial<GraphQLView> = {};
|
||||
updateCurrentViewParams.type = viewType;
|
||||
|
||||
switch (viewType) {
|
||||
case ViewType.Kanban: {
|
||||
if (availableFieldsForGrouping.length === 0) {
|
||||
throw new Error('No fields for kanban - should not happen');
|
||||
}
|
||||
|
||||
const mainGroupByFieldMetadataId = availableFieldsForGrouping[0].id;
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId =
|
||||
mainGroupByFieldMetadataId;
|
||||
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
mainGroupByFieldMetadataId,
|
||||
},
|
||||
]);
|
||||
await updateCurrentView(updateCurrentViewParams);
|
||||
return;
|
||||
case ViewType.Calendar: {
|
||||
if (availableFieldsForCalendar.length === 0) {
|
||||
throw new Error('No date fields for calendar');
|
||||
}
|
||||
case ViewType.Table: {
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
|
||||
await updateCurrentView(updateCurrentViewParams);
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
case ViewType.Calendar: {
|
||||
if (availableFieldsForCalendar.length === 0) {
|
||||
throw new Error('No date fields for calendar');
|
||||
}
|
||||
|
||||
const calendarFieldMetadataId = availableFieldsForCalendar[0].id;
|
||||
const calendarFieldMetadataId = availableFieldsForCalendar[0].id;
|
||||
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
calendarLayout: ViewCalendarLayout.MONTH,
|
||||
calendarFieldMetadataId,
|
||||
},
|
||||
]);
|
||||
setRecordIndexViewType(viewType);
|
||||
jotaiStore.set(coreViewsState.atom, [
|
||||
...existingCoreViews.filter(
|
||||
(coreView) => coreView.id !== currentView.id,
|
||||
),
|
||||
{
|
||||
...currentCoreView,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
type: convertViewTypeToCore(viewType),
|
||||
calendarLayout: ViewCalendarLayout.MONTH,
|
||||
calendarFieldMetadataId,
|
||||
},
|
||||
]);
|
||||
|
||||
loadRecordIndexStates(
|
||||
{
|
||||
...currentView,
|
||||
type: viewType,
|
||||
calendarFieldMetadataId,
|
||||
calendarLayout: ViewCalendarLayout.MONTH,
|
||||
},
|
||||
objectMetadataItem,
|
||||
);
|
||||
loadRecordIndexStates(
|
||||
{
|
||||
...currentView,
|
||||
type: viewType,
|
||||
calendarFieldMetadataId,
|
||||
calendarLayout: ViewCalendarLayout.MONTH,
|
||||
},
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
updateCurrentViewParams.calendarLayout = ViewCalendarLayout.MONTH;
|
||||
updateCurrentViewParams.calendarFieldMetadataId =
|
||||
calendarFieldMetadataId;
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
|
||||
return await updateCurrentView(updateCurrentViewParams);
|
||||
}
|
||||
case ViewType.FieldsWidget: {
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
return assertUnreachable(viewType);
|
||||
if (shouldChangeIcon(currentView.icon, currentView.type)) {
|
||||
updateCurrentViewParams.icon =
|
||||
viewTypeIconMapping(viewType).displayName;
|
||||
}
|
||||
updateCurrentViewParams.calendarLayout = ViewCalendarLayout.MONTH;
|
||||
updateCurrentViewParams.calendarFieldMetadataId =
|
||||
calendarFieldMetadataId;
|
||||
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
|
||||
return await updateCurrentView(updateCurrentViewParams);
|
||||
}
|
||||
},
|
||||
case ViewType.FieldsWidget: {
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
return assertUnreachable(viewType);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
availableFieldsForGrouping,
|
||||
setRecordIndexViewType,
|
||||
store,
|
||||
updateCurrentView,
|
||||
availableFieldsForCalendar,
|
||||
loadRecordIndexStates,
|
||||
|
||||
+5
-5
@@ -1,6 +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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
@@ -8,20 +9,19 @@ import { type ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
|
||||
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
export const useUpdateObjectViewOptions = () => {
|
||||
const store = useStore();
|
||||
|
||||
const setRecordIndexOpenRecordIn = useSetRecoilState(
|
||||
const setRecordIndexOpenRecordIn = useSetRecoilStateV2(
|
||||
recordIndexOpenRecordInState,
|
||||
);
|
||||
|
||||
const setRecordIndexViewName = useSetRecoilComponentState(
|
||||
const setRecordIndexViewName = useSetRecoilComponentStateV2(
|
||||
viewPickerInputNameComponentState,
|
||||
);
|
||||
|
||||
const setRecordIndexViewIcon = useSetRecoilComponentState(
|
||||
const setRecordIndexViewIcon = useSetRecoilComponentStateV2(
|
||||
viewPickerSelectedIconComponentState,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
|
||||
export const objectOptionsDropdownSearchInputComponentState =
|
||||
createComponentState<string>({
|
||||
createComponentStateV2<string>({
|
||||
key: 'objectOptionsDropdownSearchInputComponentState',
|
||||
defaultValue: '',
|
||||
componentInstanceContext: ViewComponentInstanceContext,
|
||||
|
||||
Reference in New Issue
Block a user