## 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:
Charles Bochet
2026-02-24 17:37:01 +01:00
committed by GitHub
parent c6ec764b23
commit 0f2af6a6cb
1326 changed files with 19248 additions and 19389 deletions
@@ -7,9 +7,9 @@ import { useMatchingCommandMenuActions } from '@/command-menu/hooks/useMatchingC
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export type ActionGroupConfig = {
@@ -20,7 +20,7 @@ export type ActionGroupConfig = {
export const CommandMenu = () => {
const { t } = useLingui();
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
const commandMenuSearch = useRecoilValueV2(commandMenuSearchState);
const { objectMetadataItems } = useObjectMetadataItems();
const {
@@ -40,12 +40,12 @@ export const CommandMenu = () => {
});
const previousContextStoreCurrentObjectMetadataItemId =
useRecoilComponentValue(
useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
'command-menu-previous',
);
const objectMetadataItemId = useRecoilComponentValue(
const objectMetadataItemId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
);
const currentObjectMetadataItem = objectMetadataItems.find(
@@ -1,4 +1,4 @@
import { currentAIChatThreadTitleStateV2 } from '@/ai/states/currentAIChatThreadTitleStateV2';
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
@@ -12,7 +12,7 @@ const StyledPageTitle = styled.div`
export const CommandMenuAskAIInfo = () => {
const currentAIChatThreadTitle = useRecoilValueV2(
currentAIChatThreadTitleStateV2,
currentAIChatThreadTitleState,
);
return (
@@ -8,7 +8,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
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 { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
const StyledCommandMenuContainer = styled.div<{ isMobile: boolean }>`
@@ -29,7 +29,7 @@ type CommandMenuContainerProps = {
export const CommandMenuContainer = ({
children,
}: CommandMenuContainerProps) => {
const objectMetadataItemId = useRecoilComponentValue(
const objectMetadataItemId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
@@ -41,7 +41,7 @@ export const CommandMenuContainer = ({
(objectMetadataItem) => objectMetadataItem.id === objectMetadataItemId,
);
const currentViewId = useRecoilComponentValue(
const currentViewId = useRecoilComponentValueV2(
contextStoreCurrentViewIdComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
@@ -3,8 +3,8 @@ import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelect
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
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 { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export const CommandMenuDefaultSelectionEffect = ({
@@ -21,7 +21,7 @@ export const CommandMenuDefaultSelectionEffect = ({
COMMAND_MENU_LIST_SELECTABLE_LIST_ID,
);
const hasUserSelectedCommand = useRecoilValue(hasUserSelectedCommandState);
const hasUserSelectedCommand = useRecoilValueV2(hasUserSelectedCommandState);
useEffect(() => {
if (
@@ -1,7 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { useIcons } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
@@ -17,7 +16,7 @@ import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValu
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
const StyledClickableIconWrapper = styled.div`
cursor: pointer;
@@ -27,9 +26,9 @@ export const CommandMenuFolderInfo = () => {
const theme = useTheme();
const { t } = useLingui();
const { getIcon } = useIcons();
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const commandMenuPageInfo = useRecoilValueV2(commandMenuPageInfoState);
const [shouldFocusTitleInput, setShouldFocusTitleInput] =
useRecoilComponentState(
useRecoilComponentStateV2(
commandMenuShouldFocusTitleInputComponentState,
commandMenuPageInfo.instanceId,
);
@@ -1,6 +1,5 @@
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { IconLink } from 'twenty-ui/display';
import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout';
@@ -14,14 +13,14 @@ import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-i
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
export const CommandMenuLinkInfo = () => {
const theme = useTheme();
const { t } = useLingui();
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const commandMenuPageInfo = useRecoilValueV2(commandMenuPageInfoState);
const [shouldFocusTitleInput, setShouldFocusTitleInput] =
useRecoilComponentState(
useRecoilComponentStateV2(
commandMenuShouldFocusTitleInputComponentState,
commandMenuPageInfo.instanceId,
);
@@ -8,10 +8,10 @@ import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/Comman
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useSetRecoilState } from 'recoil';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
export type CommandMenuListProps = {
@@ -63,7 +63,7 @@ export const CommandMenuList = ({
noResults = false,
noResultsText,
}: CommandMenuListProps) => {
const setHasUserSelectedCommand = useSetRecoilState(
const setHasUserSelectedCommand = useSetRecoilStateV2(
hasUserSelectedCommandState,
);
@@ -3,11 +3,11 @@ import { CommandMenuRecordPageLayoutInfo } from '@/command-menu/components/Comma
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
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';
export const CommandMenuPageLayoutInfo = () => {
const objectMetadataId = useRecoilComponentValue(
const objectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
);
@@ -9,12 +9,12 @@ import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDr
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useTheme } from '@emotion/react';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
@@ -27,11 +27,11 @@ export const CommandMenuPageLayoutInfoContent = ({
}) => {
const theme = useTheme();
const { getIcon } = useIcons();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const commandMenuPageInfo = useRecoilValueV2(commandMenuPageInfoState);
const [shouldFocusTitleInput, setShouldFocusTitleInput] =
useRecoilComponentState(
useRecoilComponentStateV2(
commandMenuShouldFocusTitleInputComponentState,
commandMenuPageInfo.instanceId,
);
@@ -40,17 +40,17 @@ export const CommandMenuPageLayoutInfoContent = ({
setShouldFocusTitleInput(false);
};
const draftPageLayout = useRecoilComponentValue(
const draftPageLayout = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const pageLayoutEditingWidgetId = useRecoilComponentValue(
const pageLayoutEditingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
const [openTabId] = useRecoilComponentState(
const [openTabId] = useRecoilComponentStateV2(
pageLayoutTabSettingsOpenTabIdComponentState,
pageLayoutId,
);
@@ -7,23 +7,22 @@ import { useIsRecordFieldReadOnly } from '@/object-record/read-only/hooks/useIsR
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useRecordShowContainerActions } from '@/object-record/record-show/hooks/useRecordShowContainerActions';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { recordStoreIdentifierFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifierSelector';
import { recordStoreFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreFamilySelectorV2';
import { recordStoreIdentifierFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreIdentifierFamilySelectorV2';
import { RecordTitleCell } from '@/object-record/record-title-cell/components/RecordTitleCell';
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
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 { Trans } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { Avatar } from 'twenty-ui/display';
import {
FeatureFlagKey,
FieldMetadataType,
} from '~/generated-metadata/graphql';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout';
@@ -32,7 +31,7 @@ export const CommandMenuRecordInfo = ({
}: {
commandMenuPageInstanceId: string;
}) => {
const viewableRecordNameSingular = useRecoilComponentValue(
const viewableRecordNameSingular = useRecoilComponentValueV2(
viewableRecordNameSingularComponentState,
commandMenuPageInstanceId,
);
@@ -40,7 +39,7 @@ export const CommandMenuRecordInfo = ({
allowRequestsToTwentyIconsState,
);
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
commandMenuPageInstanceId,
);
@@ -50,26 +49,25 @@ export const CommandMenuRecordInfo = ({
viewableRecordId!,
);
const recordCreatedAt = useRecoilValue<string | null>(
recordStoreFamilySelector({
recordId: objectRecordId,
fieldName: 'createdAt',
}),
);
const recordCreatedAt = useFamilySelectorValueV2(
recordStoreFamilySelectorV2,
{ recordId: objectRecordId, fieldName: 'createdAt' },
) as string | null;
const isFilesFieldMigrated = useIsFeatureEnabled(
FeatureFlagKey.IS_FILES_FIELD_MIGRATED,
);
const recordIdentifier = useRecoilValue(
recordStoreIdentifierFamilySelector({
const recordIdentifier = useFamilySelectorValueV2(
recordStoreIdentifierFamilySelectorV2,
{
recordId: objectRecordId,
allowRequestsToTwentyIcons,
isFilesFieldMigrated,
}),
},
);
const { localeCatalog } = useRecoilValue(dateLocaleState);
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
const beautifiedCreatedAt = isNonEmptyString(recordCreatedAt)
? beautifyPastDateRelativeToNow(recordCreatedAt, localeCatalog)
: '';
@@ -5,10 +5,10 @@ import { COMMAND_MENU_PAGES_CONFIG } from '@/command-menu/constants/CommandMenuP
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
const StyledCommandMenuContent = styled.div`
@@ -17,8 +17,8 @@ const StyledCommandMenuContent = styled.div`
`;
export const CommandMenuRouter = () => {
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const commandMenuPageInfo = useRecoilValueV2(commandMenuPageInfoState);
const commandMenuPageComponent = isDefined(commandMenuPage) ? (
COMMAND_MENU_PAGES_CONFIG.get(commandMenuPage)
@@ -12,10 +12,11 @@ import { tableWidthResizeIsActiveState } from '@/object-record/record-table/stat
import { ModalContainerContext } from '@/ui/layout/modal/contexts/ModalContainerContext';
import { ResizablePanelGap } from '@/ui/layout/resizable-panel/components/ResizablePanelGap';
import { COMMAND_MENU_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/CommandMenuConstraints';
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 styled from '@emotion/styled';
import { useCallback, useState } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
const StyledSidePanelWrapper = styled.div<{
isOpen: boolean;
@@ -56,8 +57,8 @@ const GAP_WIDTH = 8;
export const CommandMenuSidePanelForDesktop = () => {
const isCommandMenuOpened = useRecoilValueV2(isCommandMenuOpenedStateV2);
const isCommandMenuClosing = useRecoilValue(isCommandMenuClosingState);
const [commandMenuWidth, setCommandMenuWidth] = useRecoilState(
const isCommandMenuClosing = useRecoilValueV2(isCommandMenuClosingState);
const [commandMenuWidth, setCommandMenuWidth] = useRecoilStateV2(
commandMenuWidthState,
);
const { closeCommandMenu } = useCommandMenu();
@@ -71,7 +72,7 @@ export const CommandMenuSidePanelForDesktop = () => {
const [shouldRenderContent, setShouldRenderContent] =
useState(isCommandMenuOpened);
const setTableWidthResizeIsActive = useSetRecoilState(
const setTableWidthResizeIsActive = useSetRecoilStateV2(
tableWidthResizeIsActiveState,
);
@@ -18,8 +18,9 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { AnimatePresence, motion } from 'framer-motion';
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useRef } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconX } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
@@ -79,7 +80,7 @@ const StyledContentContainer = styled.div`
`;
export const CommandMenuTopBar = () => {
const [commandMenuSearch, setCommandMenuSearch] = useRecoilState(
const [commandMenuSearch, setCommandMenuSearch] = useRecoilStateV2(
commandMenuSearchState,
);
const inputRef = useRef<HTMLInputElement>(null);
@@ -94,9 +95,9 @@ export const CommandMenuTopBar = () => {
const { closeCommandMenu } = useCommandMenu();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const commandMenuNavigationStack = useRecoilValue(
const commandMenuNavigationStack = useRecoilValueV2(
commandMenuNavigationStackState,
);
@@ -1,6 +1,5 @@
import { useRecoilValue } from 'recoil';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useEffect } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
@@ -11,7 +10,7 @@ type CommandMenuTopBarInputFocusEffectProps = {
export const CommandMenuTopBarInputFocusEffect = ({
inputRef,
}: CommandMenuTopBarInputFocusEffectProps) => {
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
useEffect(() => {
if (
@@ -1,9 +1,9 @@
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconEdit, IconSparkles } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
@@ -19,7 +19,7 @@ const StyledIconButton = styled(IconButton)`
export const CommandMenuTopBarRightCornerIcon = () => {
const isMobile = useIsMobile();
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
const { createChatThread } = useCreateNewAIChatThread();
@@ -1,13 +1,13 @@
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import {
COMMAND_MENU_WIDTH_VAR,
commandMenuWidthState,
} from '@/command-menu/states/commandMenuWidthState';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
export const CommandMenuWidthEffect = () => {
const commandMenuWidth = useRecoilValue(commandMenuWidthState);
const commandMenuWidth = useRecoilValueV2(commandMenuWidthState);
useEffect(() => {
document.documentElement.style.setProperty(
@@ -5,7 +5,7 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { TitleInput } from '@/ui/input/components/TitleInput';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { getAgentIdFromStep } from '@/workflow/utils/getAgentIdFromStep';
@@ -19,8 +19,8 @@ import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon
import { getTriggerIconColor } from '@/workflow/workflow-trigger/utils/getTriggerIconColor';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
@@ -35,11 +35,11 @@ export const CommandMenuWorkflowStepInfo = ({
const theme = useTheme();
const { getIcon } = useIcons();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const workflowId = useCommandMenuWorkflowIdOrThrow();
const workflowStepId = useRecoilComponentValue(
const workflowStepId = useRecoilComponentValueV2(
commandMenuWorkflowStepIdComponentState,
commandMenuPageInstanceId,
);
@@ -7,19 +7,19 @@ import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { IconArrowBackUp } from 'twenty-ui/display';
export const ResetContextToSelectionCommandButton = () => {
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
'command-menu-previous',
);
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValue(
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
'command-menu-previous',
);
@@ -3,12 +3,11 @@ import {
type Meta,
type StoryObj,
} from '@storybook/react-vite';
import { useSetRecoilState } from 'recoil';
import { expect, userEvent, within } from 'storybook/test';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@@ -28,13 +27,18 @@ import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { HttpResponse, graphql } from 'msw';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconDotsVertical } from 'twenty-ui/display';
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
@@ -73,26 +77,17 @@ const meta: Meta<typeof CommandMenu> = {
component: CommandMenuRouter,
decorators: [
(Story) => {
const setCurrentWorkspace = useSetRecoilStateV2(currentWorkspaceState);
const setCurrentUserWorkspace = useSetRecoilStateV2(
currentUserWorkspaceState,
jotaiStore.set(currentWorkspaceState.atom, mockCurrentWorkspace);
jotaiStore.set(
currentWorkspaceMemberState.atom,
mockedWorkspaceMemberData,
);
const setCurrentWorkspaceMember = useSetRecoilStateV2(
currentWorkspaceMemberState,
jotaiStore.set(
currentUserWorkspaceState.atom,
mockedUserData.currentUserWorkspace,
);
const setIsCommandMenuOpened = useSetRecoilState(
isCommandMenuOpenedState,
);
const setCommandMenuNavigationStack = useSetRecoilState(
commandMenuNavigationStackState,
);
setCurrentWorkspace(mockCurrentWorkspace);
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
setCurrentUserWorkspace(mockedUserData.currentUserWorkspace);
setIsCommandMenuOpened(true);
setCommandMenuNavigationStack([
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
jotaiStore.set(commandMenuNavigationStackState.atom, [
{
page: CommandMenuPages.Root,
pageTitle: 'Command Menu',
@@ -101,6 +96,25 @@ const meta: Meta<typeof CommandMenu> = {
},
]);
const objectMetadataItems = jotaiStore.get(objectMetadataItemsState.atom);
const companyMetadataItem = objectMetadataItems.find(
(item) => item.nameSingular === 'company',
);
if (isDefined(companyMetadataItem)) {
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
companyMetadataItem.id,
);
jotaiStore.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
ContextStoreViewType.Table,
);
}
return <Story />;
},
ContextStoreDecorator,
@@ -139,10 +153,8 @@ export const LimitedPermissions: Story = {
},
decorators: [
(Story) => {
const setCurrentUserWorkspace = useSetRecoilStateV2(
currentUserWorkspaceState,
);
setCurrentUserWorkspace(
jotaiStore.set(
currentUserWorkspaceState.atom,
mockedLimitedPermissionsUserData.currentUserWorkspace,
);
@@ -9,6 +9,7 @@ import { CommandMenuContextRecordsChip } from '@/command-menu/components/Command
import { PreComputedChipGeneratorsContext } from '@/object-metadata/contexts/PreComputedChipGeneratorsContext';
import { type RecordChipData } from '@/object-record/record-field/ui/types/RecordChipData';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ComponentDecorator } from 'twenty-ui/testing';
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
@@ -190,9 +191,12 @@ const createContextStoreWrapper = ({
selectedRecordIds: companies.map((company) => company.id),
},
contextStoreNumberOfSelectedRecords: companies.length,
onInitializeRecoilSnapshot: (snapshot) => {
onInitializeJotaiStore: () => {
for (const company of companies) {
snapshot.set(recordStoreFamilyState(company.id), company);
jotaiStore.set(
recordStoreFamilyState.atomFamily(company.id),
company as ObjectRecord,
);
}
},
});
@@ -1,13 +1,14 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { RecoilRoot } from 'recoil';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconDotsVertical } from 'twenty-ui/display';
@@ -26,19 +27,9 @@ const renderHooks = () => {
const { result } = renderHook(
() => {
const commandMenu = useCommandMenu();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const commandMenuNavigationStack = useRecoilValue(
commandMenuNavigationStackState,
);
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
return {
commandMenu,
isCommandMenuOpened,
commandMenuNavigationStack,
commandMenuPage,
commandMenuPageInfo,
};
},
{
@@ -60,31 +51,31 @@ describe('useCommandMenu', () => {
result.current.commandMenu.openCommandMenu();
});
expect(result.current.isCommandMenuOpened).toBe(true);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
act(() => {
result.current.commandMenu.closeCommandMenu();
});
expect(result.current.isCommandMenuOpened).toBe(false);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
});
it('should toggle the command menu', () => {
const { result } = renderHooks();
expect(result.current.isCommandMenuOpened).toBe(false);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
act(() => {
result.current.commandMenu.toggleCommandMenu();
});
expect(result.current.isCommandMenuOpened).toBe(true);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
act(() => {
result.current.commandMenu.toggleCommandMenu();
});
expect(result.current.isCommandMenuOpened).toBe(false);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
});
it('should navigate command menu and reset navigation stack when resetNavigationStack is true', () => {
@@ -99,9 +90,15 @@ describe('useCommandMenu', () => {
});
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.Root);
expect(result.current.commandMenuPageInfo.title).toBe('First Page');
expect(result.current.commandMenuNavigationStack).toHaveLength(1);
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.Root,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom).title).toBe(
'First Page',
);
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toHaveLength(
1,
);
act(() => {
result.current.commandMenu.navigateCommandMenu({
@@ -112,7 +109,9 @@ describe('useCommandMenu', () => {
});
});
expect(result.current.commandMenuNavigationStack).toHaveLength(2);
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toHaveLength(
2,
);
act(() => {
result.current.commandMenu.navigateCommandMenu({
@@ -123,8 +122,14 @@ describe('useCommandMenu', () => {
});
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.Root);
expect(result.current.commandMenuPageInfo.title).toBe('Reset Page');
expect(result.current.commandMenuNavigationStack).toHaveLength(1);
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.Root,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom).title).toBe(
'Reset Page',
);
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toHaveLength(
1,
);
});
});
@@ -1,21 +1,23 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
import { RecoilRoot } from 'recoil';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/constants/CommandMenuContextChipGroupsDropdownId';
import { COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuPreviousComponentInstanceId';
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
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 { CommandMenuPages } from 'twenty-shared/types';
import { IconList } from 'twenty-ui/display';
@@ -65,62 +67,13 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
const { commandMenuCloseAnimationCompleteCleanup } =
useCommandMenuCloseAnimationCompleteCleanup();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
const commandMenuNavigationStack = useRecoilValue(
commandMenuNavigationStackState,
);
const viewableRecordId = useRecoilValueV2(viewableRecordIdState);
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
commandMenuNavigationMorphItemsByPageState,
);
const hasUserSelectedCommand = useRecoilValue(
hasUserSelectedCommandState,
);
const isCommandMenuClosing = useRecoilValue(isCommandMenuClosingState);
const viewableRecordId = useRecoilValue(viewableRecordIdState);
// Get setters for state modification in tests
const setCommandMenuPage = useSetRecoilState(commandMenuPageState);
const setCommandMenuPageInfo = useSetRecoilState(
commandMenuPageInfoState,
);
const setIsCommandMenuOpened = useSetRecoilState(
isCommandMenuOpenedState,
);
const setCommandMenuSearch = useSetRecoilState(commandMenuSearchState);
const setCommandMenuNavigationStack = useSetRecoilState(
commandMenuNavigationStackState,
);
const setHasUserSelectedCommand = useSetRecoilState(
hasUserSelectedCommandState,
);
const setIsCommandMenuClosing = useSetRecoilState(
isCommandMenuClosingState,
);
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);
const setViewableRecordId = useSetRecoilStateV2(viewableRecordIdState);
return {
commandMenuCloseAnimationCompleteCleanup,
commandMenuPage,
commandMenuPageInfo,
isCommandMenuOpened,
commandMenuSearch,
commandMenuNavigationStack,
commandMenuNavigationMorphItemsByPage,
hasUserSelectedCommand,
isCommandMenuClosing,
viewableRecordId,
setCommandMenuPage,
setCommandMenuPageInfo,
setIsCommandMenuOpened,
setCommandMenuSearch,
setCommandMenuNavigationStack,
setHasUserSelectedCommand,
setIsCommandMenuClosing,
setViewableRecordId,
};
},
@@ -135,15 +88,15 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
const { result } = renderHooks();
act(() => {
result.current.setCommandMenuPage(CommandMenuPages.ViewRecord);
result.current.setCommandMenuPageInfo({
jotaiStore.set(commandMenuPageState.atom, CommandMenuPages.ViewRecord);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: 'Test Record',
Icon: IconList,
instanceId: 'test-id',
});
result.current.setIsCommandMenuOpened(true);
result.current.setCommandMenuSearch('test search');
result.current.setCommandMenuNavigationStack([
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
jotaiStore.set(commandMenuSearchState.atom, 'test search');
jotaiStore.set(commandMenuNavigationStackState.atom, [
{
page: CommandMenuPages.SearchRecords,
pageTitle: 'Search',
@@ -151,20 +104,22 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
pageId: '1',
},
]);
result.current.setHasUserSelectedCommand(true);
result.current.setIsCommandMenuClosing(true);
jotaiStore.set(hasUserSelectedCommandState.atom, true);
jotaiStore.set(isCommandMenuClosingState.atom, true);
result.current.setViewableRecordId('record-123');
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.ViewRecord);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.ViewRecord,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: 'Test Record',
Icon: IconList,
instanceId: 'test-id',
});
expect(result.current.isCommandMenuOpened).toBe(true);
expect(result.current.commandMenuSearch).toBe('test search');
expect(result.current.commandMenuNavigationStack).toEqual([
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(true);
expect(jotaiStore.get(commandMenuSearchState.atom)).toBe('test search');
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([
{
page: CommandMenuPages.SearchRecords,
pageTitle: 'Search',
@@ -172,25 +127,27 @@ describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
pageId: '1',
},
]);
expect(result.current.hasUserSelectedCommand).toBe(true);
expect(result.current.isCommandMenuClosing).toBe(true);
expect(jotaiStore.get(hasUserSelectedCommandState.atom)).toBe(true);
expect(jotaiStore.get(isCommandMenuClosingState.atom)).toBe(true);
expect(result.current.viewableRecordId).toBe('record-123');
act(() => {
result.current.commandMenuCloseAnimationCompleteCleanup();
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.Root);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.Root,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: undefined,
Icon: undefined,
instanceId: '',
});
expect(result.current.isCommandMenuOpened).toBe(false);
expect(result.current.commandMenuSearch).toBe('');
expect(result.current.commandMenuNavigationStack).toEqual([]);
expect(result.current.hasUserSelectedCommand).toBe(false);
expect(result.current.isCommandMenuClosing).toBe(false);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
expect(jotaiStore.get(commandMenuSearchState.atom)).toBe('');
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([]);
expect(jotaiStore.get(hasUserSelectedCommandState.atom)).toBe(false);
expect(jotaiStore.get(isCommandMenuClosingState.atom)).toBe(false);
expect(result.current.viewableRecordId).toBe(null);
});
@@ -1,7 +1,7 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { RecoilRoot } from 'recoil';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
@@ -9,7 +9,8 @@ import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistor
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconList, IconSearch } from 'twenty-ui/display';
@@ -26,20 +27,10 @@ const renderHooks = () => {
const commandMenuHistory = useCommandMenuHistory();
const commandMenuCloseAnimationCompleteCleanup =
useCommandMenuCloseAnimationCompleteCleanup();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const commandMenuNavigationStack = useRecoilValue(
commandMenuNavigationStackState,
);
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
return {
commandMenu,
commandMenuHistory,
isCommandMenuOpened,
commandMenuNavigationStack,
commandMenuPage,
commandMenuPageInfo,
commandMenuCloseAnimationCompleteCleanup,
};
},
@@ -72,7 +63,7 @@ describe('useCommandMenuHistory', () => {
});
});
expect(result.current.commandMenuNavigationStack).toEqual([
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([
{
page: CommandMenuPages.SearchRecords,
pageTitle: 'Search',
@@ -91,7 +82,7 @@ describe('useCommandMenuHistory', () => {
result.current.commandMenuHistory.goBackFromCommandMenu();
});
expect(result.current.commandMenuNavigationStack).toEqual([
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([
{
page: CommandMenuPages.SearchRecords,
pageTitle: 'Search',
@@ -99,8 +90,10 @@ describe('useCommandMenuHistory', () => {
pageId: '1',
},
]);
expect(result.current.commandMenuPage).toBe(CommandMenuPages.SearchRecords);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.SearchRecords,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: 'Search',
Icon: IconSearch,
instanceId: '1',
@@ -111,14 +104,16 @@ describe('useCommandMenuHistory', () => {
result.current.commandMenuCloseAnimationCompleteCleanup.commandMenuCloseAnimationCompleteCleanup();
});
expect(result.current.commandMenuNavigationStack).toEqual([]);
expect(result.current.commandMenuPage).toBe(CommandMenuPages.Root);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([]);
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.Root,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: undefined,
instanceId: '',
Icon: undefined,
});
expect(result.current.isCommandMenuOpened).toBe(false);
expect(jotaiStore.get(isCommandMenuOpenedStateV2.atom)).toBe(false);
});
it('should navigate to a page in history', () => {
@@ -137,8 +132,10 @@ describe('useCommandMenuHistory', () => {
result.current.commandMenuHistory.navigateCommandMenuHistory(0);
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.SearchRecords);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.SearchRecords,
);
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: 'Search',
Icon: IconSearch,
instanceId: '1',
@@ -1,8 +1,7 @@
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { RecoilRoot } from 'recoil';
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
@@ -21,11 +20,8 @@ const renderHooks = () => {
() => {
const { onItemClick } = useCommandMenuOnItemClick();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
return {
onItemClick,
isCommandMenuOpened,
};
},
{
@@ -1,59 +1,45 @@
import { useCommandMenuUpdateNavigationMorphItemsByPage } from '@/command-menu/hooks/useCommandMenuUpdateNavigationMorphItemsByPage';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { RecoilRoot } from 'recoil';
const pageId = 'merge-page-id';
const objectMetadataId = 'company-metadata-id';
const Wrapper = ({
children,
initialRecordIds,
}: {
children: React.ReactNode;
initialRecordIds: string[];
}) => (
<RecoilRoot
initializeState={({ set }) => {
set(
commandMenuNavigationMorphItemsByPageState,
new Map([
[
pageId,
initialRecordIds.map((recordId) => ({
objectMetadataId,
recordId,
})),
],
]),
);
}}
>
{children}
</RecoilRoot>
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
);
const renderHooks = (initialRecordIds: string[]) =>
renderHook(
const renderHooks = (initialRecordIds: string[]) => {
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
new Map([
[
pageId,
initialRecordIds.map((recordId) => ({
objectMetadataId,
recordId,
})),
],
]),
);
return renderHook(
() => {
const { updateCommandMenuNavigationMorphItemsByPage } =
useCommandMenuUpdateNavigationMorphItemsByPage();
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
commandMenuNavigationMorphItemsByPageState,
);
return {
updateCommandMenuNavigationMorphItemsByPage,
commandMenuNavigationMorphItemsByPage,
};
},
{
wrapper: ({ children }) => (
<Wrapper initialRecordIds={initialRecordIds}>{children}</Wrapper>
),
wrapper: Wrapper,
},
);
};
describe('useCommandMenuUpdateNavigationMorphItemsByPage', () => {
it('should replace existing items for a page instead of appending', async () => {
@@ -68,7 +54,9 @@ describe('useCommandMenuUpdateNavigationMorphItemsByPage', () => {
});
expect(
result.current.commandMenuNavigationMorphItemsByPage.get(pageId),
jotaiStore
.get(commandMenuNavigationMorphItemsByPageState.atom)
.get(pageId),
).toEqual([
{
objectMetadataId,
@@ -98,7 +86,9 @@ describe('useCommandMenuUpdateNavigationMorphItemsByPage', () => {
});
expect(
result.current.commandMenuNavigationMorphItemsByPage.get(pageId),
jotaiStore
.get(commandMenuNavigationMorphItemsByPageState.atom)
.get(pageId),
).toEqual([
{
objectMetadataId,
@@ -1,12 +1,12 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { useRecoilValue } from 'recoil';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { CommandMenuPages } from 'twenty-shared/types';
import { Icon123, useIcons } from 'twenty-ui/display';
@@ -40,19 +40,10 @@ const renderHooks = () => {
() => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuNavigationStack = useRecoilValue(
commandMenuNavigationStackState,
);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const { getIcon } = useIcons();
return {
navigateCommandMenu,
commandMenuPage,
commandMenuNavigationStack,
commandMenuPageInfo,
getIcon,
};
},
@@ -82,8 +73,10 @@ describe('useNavigateCommandMenu', () => {
});
});
expect(result.current.commandMenuPage).toBe(CommandMenuPages.Root);
expect(result.current.commandMenuNavigationStack).toEqual([
expect(jotaiStore.get(commandMenuPageState.atom)).toBe(
CommandMenuPages.Root,
);
expect(jotaiStore.get(commandMenuNavigationStackState.atom)).toEqual([
{
page: CommandMenuPages.Root,
pageTitle: 'Root',
@@ -92,7 +85,7 @@ describe('useNavigateCommandMenu', () => {
pageId: 'mocked-uuid',
},
]);
expect(result.current.commandMenuPageInfo).toEqual({
expect(jotaiStore.get(commandMenuPageInfoState.atom)).toEqual({
title: 'Root',
Icon: Icon123,
instanceId: 'mocked-uuid',
@@ -5,7 +5,7 @@ import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/Com
import { useOpenCalendarEventInCommandMenu } from '@/command-menu/hooks/useOpenCalendarEventInCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconCalendarEvent } from 'twenty-ui/display';
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
@@ -46,7 +46,7 @@ const renderHooks = () => {
const { openCalendarEventInCommandMenu } =
useOpenCalendarEventInCommandMenu();
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
'mocked-uuid',
);
@@ -5,7 +5,7 @@ import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/Com
import { useOpenEmailThreadInCommandMenu } from '@/command-menu/hooks/useOpenEmailThreadInCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconMail } from 'twenty-ui/display';
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
@@ -46,7 +46,7 @@ const renderHooks = () => {
const { openEmailThreadInCommandMenu } =
useOpenEmailThreadInCommandMenu();
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
'mocked-uuid',
);
@@ -1,19 +1,18 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { useRecoilValue } from 'recoil';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { CommandMenuPages } from 'twenty-shared/types';
import { useIcons } from 'twenty-ui/display';
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
@@ -53,32 +52,27 @@ const renderHooks = () => {
() => {
const { openRecordInCommandMenu } = useOpenRecordInCommandMenu();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
commandMenuNavigationMorphItemsByPageState,
);
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
'mocked-uuid',
);
const viewableRecordNameSingular = useRecoilComponentValue(
const viewableRecordNameSingular = useRecoilComponentValueV2(
viewableRecordNameSingularComponentState,
'mocked-uuid',
);
const currentObjectMetadataItemId = useRecoilComponentValue(
const currentObjectMetadataItemId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
'mocked-uuid',
);
const targetedRecordsRule = useRecoilComponentValue(
const targetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
'mocked-uuid',
);
const numberOfSelectedRecords = useRecoilComponentValue(
const numberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
'mocked-uuid',
);
const currentViewType = useRecoilComponentValue(
const currentViewType = useRecoilComponentValueV2(
contextStoreCurrentViewTypeComponentState,
'mocked-uuid',
);
@@ -87,8 +81,6 @@ const renderHooks = () => {
return {
openRecordInCommandMenu,
viewableRecordId,
commandMenuPage,
commandMenuNavigationMorphItemsByPage,
viewableRecordNameSingular,
currentObjectMetadataItemId,
targetedRecordsRule,
@@ -134,10 +126,11 @@ describe('useOpenRecordInCommandMenu', () => {
expect(result.current.numberOfSelectedRecords).toBe(1);
expect(result.current.currentViewType).toBe(ContextStoreViewType.ShowPage);
expect(result.current.commandMenuNavigationMorphItemsByPage.size).toBe(1);
expect(
result.current.commandMenuNavigationMorphItemsByPage.get('mocked-uuid'),
).toEqual([
const commandMenuNavigationMorphItemsByPage = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
expect(commandMenuNavigationMorphItemsByPage.size).toBe(1);
expect(commandMenuNavigationMorphItemsByPage.get('mocked-uuid')).toEqual([
{
objectMetadataId: personMockObjectMetadataItem.id,
recordId,
@@ -1,12 +1,12 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { useRecoilValue } from 'recoil';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuPreviousComponentInstanceId';
import { useSetGlobalCommandMenuContext } from '@/command-menu/hooks/useSetGlobalCommandMenuContext';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
@@ -14,6 +14,7 @@ import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-sto
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
@@ -47,9 +48,15 @@ const wrapper = getJestMetadataAndApolloMocksAndActionMenuWrapper({
},
contextStoreNumberOfSelectedRecords: 2,
contextStoreCurrentViewType: ContextStoreViewType.Table,
onInitializeRecoilSnapshot: (snapshot) => {
snapshot.set(recordStoreFamilyState(peopleMock[0].id), peopleMock[0]);
snapshot.set(recordStoreFamilyState(peopleMock[1].id), peopleMock[1]);
onInitializeRecoilSnapshot: (_snapshot) => {
jotaiStore.set(
recordStoreFamilyState.atomFamily(peopleMock[0].id),
peopleMock[0],
);
jotaiStore.set(
recordStoreFamilyState.atomFamily(peopleMock[1].id),
peopleMock[1],
);
},
});
@@ -64,46 +71,34 @@ describe('useSetGlobalCommandMenuContext', () => {
const { setGlobalCommandMenuContext } =
useSetGlobalCommandMenuContext();
const targetedRecordsRule = useRecoilValue(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
const targetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
const numberOfSelectedRecords = useRecoilValue(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
const numberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
const filters = useRecoilValue(
contextStoreFiltersComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
const filters = useRecoilComponentValueV2(
contextStoreFiltersComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
const filterGroups = useRecoilValue(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
const filterGroups = useRecoilComponentValueV2(
contextStoreFilterGroupsComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
const anyFieldFilterValue = useRecoilValue(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
const anyFieldFilterValue = useRecoilComponentValueV2(
contextStoreAnyFieldFilterValueComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
const currentViewType = useRecoilValue(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
const hasUserSelectedCommand = useRecoilValue(
hasUserSelectedCommandState,
const currentViewType = useRecoilComponentValueV2(
contextStoreCurrentViewTypeComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
return {
@@ -113,8 +108,6 @@ describe('useSetGlobalCommandMenuContext', () => {
filters,
filterGroups,
currentViewType,
commandMenuPageInfo,
hasUserSelectedCommand,
anyFieldFilterValue,
};
},
@@ -131,12 +124,16 @@ describe('useSetGlobalCommandMenuContext', () => {
expect(result.current.filters).toEqual([]);
expect(result.current.anyFieldFilterValue).toEqual('');
expect(result.current.currentViewType).toBe(ContextStoreViewType.Table);
expect(result.current.commandMenuPageInfo).toEqual({
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
expect(commandMenuPageInfo).toEqual({
title: undefined,
Icon: undefined,
instanceId: '',
});
expect(result.current.hasUserSelectedCommand).toBe(false);
const hasUserSelectedCommand = jotaiStore.get(
hasUserSelectedCommandState.atom,
);
expect(hasUserSelectedCommand).toBe(false);
act(() => {
result.current.setGlobalCommandMenuContext();
@@ -150,12 +147,18 @@ describe('useSetGlobalCommandMenuContext', () => {
expect(result.current.filters).toEqual([]);
expect(result.current.anyFieldFilterValue).toEqual('');
expect(result.current.currentViewType).toBe(ContextStoreViewType.Table);
expect(result.current.commandMenuPageInfo).toEqual({
const commandMenuPageInfoAfter = jotaiStore.get(
commandMenuPageInfoState.atom,
);
expect(commandMenuPageInfoAfter).toEqual({
title: undefined,
Icon: undefined,
instanceId: '',
});
expect(result.current.hasUserSelectedCommand).toBe(false);
const hasUserSelectedCommandAfter = jotaiStore.get(
hasUserSelectedCommandState.atom,
);
expect(hasUserSelectedCommandAfter).toBe(false);
});
it('should call copyContextStoreStates with correct parameters', () => {
@@ -1,9 +1,10 @@
import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateCommandMenuPageInfo';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { RecoilRoot } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconArrowDown, IconDotsVertical } from 'twenty-ui/display';
@@ -23,30 +24,22 @@ const mockedNavigationStack = [
];
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot
initializeState={({ set }) => {
set(commandMenuNavigationStackState, mockedNavigationStack);
set(commandMenuPageInfoState, mockedPageInfo);
}}
>
{children}
</RecoilRoot>
<RecoilRoot>{children}</RecoilRoot>
);
describe('useUpdateCommandMenuPageInfo', () => {
beforeEach(() => {
jotaiStore.set(commandMenuNavigationStackState.atom, mockedNavigationStack);
jotaiStore.set(commandMenuPageInfoState.atom, mockedPageInfo);
});
const renderHooks = () => {
const { result } = renderHook(
() => {
const { updateCommandMenuPageInfo } = useUpdateCommandMenuPageInfo();
const commandMenuNavigationStack = useRecoilValue(
commandMenuNavigationStackState,
);
const commandMenuPageInfo = useRecoilValue(commandMenuPageInfoState);
return {
updateCommandMenuPageInfo,
commandMenuNavigationStack,
commandMenuPageInfo,
};
},
{ wrapper: Wrapper },
@@ -67,7 +60,10 @@ describe('useUpdateCommandMenuPageInfo', () => {
});
});
expect(result.current.commandMenuNavigationStack).toEqual([
const commandMenuNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
expect(commandMenuNavigationStack).toEqual([
{
page: CommandMenuPages.Root,
pageTitle: 'New Title',
@@ -76,7 +72,8 @@ describe('useUpdateCommandMenuPageInfo', () => {
},
]);
expect(result.current.commandMenuPageInfo).toEqual({
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
expect(commandMenuPageInfo).toEqual({
title: 'New Title',
Icon: IconArrowDown,
instanceId: 'test-instance',
@@ -92,7 +89,10 @@ describe('useUpdateCommandMenuPageInfo', () => {
});
});
expect(result.current.commandMenuNavigationStack).toEqual([
const commandMenuNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
expect(commandMenuNavigationStack).toEqual([
{
page: CommandMenuPages.Root,
pageTitle: 'New Title',
@@ -101,7 +101,8 @@ describe('useUpdateCommandMenuPageInfo', () => {
},
]);
expect(result.current.commandMenuPageInfo).toEqual({
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
expect(commandMenuPageInfo).toEqual({
title: 'New Title',
Icon: IconDotsVertical,
instanceId: 'test-instance',
@@ -117,7 +118,10 @@ describe('useUpdateCommandMenuPageInfo', () => {
});
});
expect(result.current.commandMenuNavigationStack).toEqual([
const commandMenuNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
expect(commandMenuNavigationStack).toEqual([
{
page: CommandMenuPages.Root,
pageTitle: 'Initial Title',
@@ -126,7 +130,8 @@ describe('useUpdateCommandMenuPageInfo', () => {
},
]);
expect(result.current.commandMenuPageInfo).toEqual({
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
expect(commandMenuPageInfo).toEqual({
title: 'Initial Title',
Icon: IconArrowDown,
instanceId: 'test-instance',
@@ -1,5 +1,4 @@
import { renderHook } from '@testing-library/react';
import { useRecoilValue } from 'recoil';
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
@@ -7,14 +6,12 @@ import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page
import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState';
import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState';
import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
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 { act } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
@@ -67,40 +64,36 @@ const renderHooks = () => {
openWorkflowEditStepTypeInCommandMenu,
openWorkflowViewStepInCommandMenu,
} = useWorkflowCommandMenu();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
commandMenuNavigationMorphItemsByPageState,
);
const viewableRecordId = useRecoilComponentValue(
const _viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
'mocked-uuid',
);
const viewableRecordNameSingular = useRecoilComponentValue(
const viewableRecordNameSingular = useRecoilComponentValueV2(
viewableRecordNameSingularComponentState,
'mocked-uuid',
);
const currentObjectMetadataItemId = useRecoilComponentValue(
const currentObjectMetadataItemId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
'mocked-uuid',
);
const targetedRecordsRule = useRecoilComponentValue(
const targetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
'mocked-uuid',
);
const numberOfSelectedRecords = useRecoilComponentValue(
const numberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
'mocked-uuid',
);
const currentViewType = useRecoilComponentValue(
const currentViewType = useRecoilComponentValueV2(
contextStoreCurrentViewTypeComponentState,
'mocked-uuid',
);
const workflowId = useRecoilComponentValue(
const workflowId = useRecoilComponentValueV2(
commandMenuWorkflowIdComponentState,
'mocked-uuid',
);
const workflowVersionId = useRecoilComponentValue(
const workflowVersionId = useRecoilComponentValueV2(
commandMenuWorkflowVersionIdComponentState,
'mocked-uuid',
);
@@ -114,9 +107,6 @@ const renderHooks = () => {
openWorkflowViewStepInCommandMenu,
workflowId,
workflowVersionId,
viewableRecordId,
commandMenuPage,
commandMenuNavigationMorphItemsByPage,
viewableRecordNameSingular,
currentObjectMetadataItemId,
targetedRecordsRule,
@@ -1,17 +1,14 @@
import { useRecoilCallback } from 'recoil';
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
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 { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconDotsVertical } from 'twenty-ui/display';
@@ -23,28 +20,19 @@ export const useCommandMenu = () => {
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const store = useStore();
const closeCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
const closeCommandMenu = useRecoilCallback(
({ set, snapshot }) =>
() => {
const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
if (isCommandMenuOpened) {
store.set(addToNavPayloadRegistryStateV2.atom, new Map());
set(isCommandMenuOpenedState, false);
store.set(isCommandMenuOpenedStateV2.atom, false);
set(isCommandMenuClosingState, true);
closeAnyOpenDropdown();
removeFocusItemFromFocusStackById({
focusId: SIDE_PANEL_FOCUS_ID,
});
}
},
[closeAnyOpenDropdown, removeFocusItemFromFocusStackById, store],
);
if (isCommandMenuOpened) {
jotaiStore.set(addToNavPayloadRegistryStateV2.atom, new Map());
jotaiStore.set(isCommandMenuOpenedStateV2.atom, false);
jotaiStore.set(isCommandMenuClosingState.atom, true);
closeAnyOpenDropdown();
removeFocusItemFromFocusStackById({
focusId: SIDE_PANEL_FOCUS_ID,
});
}
}, [closeAnyOpenDropdown, removeFocusItemFromFocusStackById]);
const openCommandMenu = useCallback(() => {
emitSidePanelOpenEvent();
@@ -57,23 +45,17 @@ export const useCommandMenu = () => {
});
}, [closeAnyOpenDropdown, navigateCommandMenu]);
const toggleCommandMenu = useRecoilCallback(
({ snapshot, set }) =>
async () => {
const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
const toggleCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
set(commandMenuSearchState, '');
jotaiStore.set(commandMenuSearchState.atom, '');
if (isCommandMenuOpened) {
closeCommandMenu();
} else {
openCommandMenu();
}
},
[closeCommandMenu, openCommandMenu],
);
if (isCommandMenuOpened) {
closeCommandMenu();
} else {
openCommandMenu();
}
}, [closeCommandMenu, openCommandMenu]);
return {
openCommandMenu,
@@ -10,7 +10,6 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
@@ -23,10 +22,10 @@ import { emitSidePanelCloseEvent } from '@/ui/layout/right-drawer/utils/emitSide
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
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 { useStore } from 'jotai';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -39,107 +38,98 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
const { closeDropdown } = useCloseDropdown();
const store = useStore();
const commandMenuCloseAnimationCompleteCleanup = useCallback(() => {
closeDropdown(COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID);
const commandMenuCloseAnimationCompleteCleanup = useRecoilCallback(
({ snapshot, set }) =>
() => {
closeDropdown(COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID);
resetContextStoreStates(COMMAND_MENU_COMPONENT_INSTANCE_ID);
resetContextStoreStates(COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID);
resetContextStoreStates(COMMAND_MENU_COMPONENT_INSTANCE_ID);
resetContextStoreStates(COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID);
const currentPage = jotaiStore.get(commandMenuPageState.atom);
const currentPage = snapshot
.getLoadable(commandMenuPageState)
.getValue();
const isPageLayoutEditingPage =
currentPage === CommandMenuPages.PageLayoutWidgetTypeSelect ||
currentPage === CommandMenuPages.PageLayoutGraphTypeSelect ||
currentPage === CommandMenuPages.PageLayoutIframeSettings ||
currentPage === CommandMenuPages.PageLayoutTabSettings;
const isPageLayoutEditingPage =
currentPage === CommandMenuPages.PageLayoutWidgetTypeSelect ||
currentPage === CommandMenuPages.PageLayoutGraphTypeSelect ||
currentPage === CommandMenuPages.PageLayoutIframeSettings ||
currentPage === CommandMenuPages.PageLayoutTabSettings;
if (isPageLayoutEditingPage) {
const targetedRecordsRule = jotaiStore.get(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
);
if (isPageLayoutEditingPage) {
const targetedRecordsRule = snapshot
.getLoadable(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
)
.getValue();
if (
targetedRecordsRule.mode === 'selection' &&
targetedRecordsRule.selectedRecordIds.length === 1
) {
const recordId = targetedRecordsRule.selectedRecordIds[0];
const record = snapshot
.getLoadable(recordStoreFamilyState(recordId))
.getValue();
if (isDefined(record) && isDefined(record.pageLayoutId)) {
set(
pageLayoutEditingWidgetIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
set(
pageLayoutTabSettingsOpenTabIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
set(
pageLayoutDraggedAreaComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
}
}
}
set(viewableRecordIdState, null);
set(commandMenuPageState, CommandMenuPages.Root);
set(commandMenuPageInfoState, {
title: undefined,
Icon: undefined,
instanceId: '',
});
set(isCommandMenuOpenedState, false);
store.set(isCommandMenuOpenedStateV2.atom, false);
set(commandMenuSearchState, '');
set(commandMenuNavigationMorphItemsByPageState, new Map());
set(commandMenuNavigationStackState, []);
resetSelectedItem();
set(hasUserSelectedCommandState, false);
emitSidePanelCloseEvent();
set(isCommandMenuClosingState, false);
store.set(
activeTabIdComponentState.atomFamily({
instanceId: WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID,
}),
WorkflowLogicFunctionTabId.CODE,
if (
targetedRecordsRule.mode === 'selection' &&
targetedRecordsRule.selectedRecordIds.length === 1
) {
const recordId = targetedRecordsRule.selectedRecordIds[0];
const record = jotaiStore.get(
recordStoreFamilyState.atomFamily(recordId),
);
for (const [pageId, morphItems] of snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue()) {
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
targetObjectId: morphItems[0].recordId,
}),
if (isDefined(record) && isDefined(record.pageLayoutId)) {
jotaiStore.set(
pageLayoutEditingWidgetIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
jotaiStore.set(
pageLayoutTabSettingsOpenTabIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
jotaiStore.set(
pageLayoutDraggedAreaComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
}
},
[closeDropdown, resetContextStoreStates, resetSelectedItem, store],
);
}
}
jotaiStore.set(viewableRecordIdState.atom, null);
jotaiStore.set(commandMenuPageState.atom, CommandMenuPages.Root);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: undefined,
Icon: undefined,
instanceId: '',
});
jotaiStore.set(isCommandMenuOpenedStateV2.atom, false);
jotaiStore.set(commandMenuSearchState.atom, '');
jotaiStore.set(commandMenuNavigationMorphItemsByPageState.atom, new Map());
jotaiStore.set(commandMenuNavigationStackState.atom, []);
resetSelectedItem();
jotaiStore.set(hasUserSelectedCommandState.atom, false);
emitSidePanelCloseEvent();
jotaiStore.set(isCommandMenuClosingState.atom, false);
jotaiStore.set(
activeTabIdComponentState.atomFamily({
instanceId: WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID,
}),
WorkflowLogicFunctionTabId.CODE,
);
const morphItemsByPage = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
for (const [pageId, morphItems] of morphItemsByPage) {
jotaiStore.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}, [closeDropdown, resetContextStoreStates, resetSelectedItem]);
return {
commandMenuCloseAnimationCompleteCleanup,
@@ -5,11 +5,10 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe
import { CommandMenuPages } from 'twenty-shared/types';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { recordStoreIdentifiersFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifiersSelector';
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
import { recordStoreRecordsSelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelectorV2';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
@@ -24,7 +23,7 @@ const StyledIconWrapper = styled.div`
`;
export const useCommandMenuContextChips = () => {
const commandMenuNavigationStack = useRecoilValue(
const commandMenuNavigationStack = useRecoilValueV2(
commandMenuNavigationStackState,
);
@@ -38,7 +37,7 @@ export const useCommandMenuContextChips = () => {
const theme = useTheme();
const commandMenuNavigationMorphItemsByPage = useRecoilValue(
const commandMenuNavigationMorphItemsByPage = useRecoilValueV2(
commandMenuNavigationMorphItemsByPageState,
);
@@ -55,11 +54,9 @@ export const useCommandMenuContextChips = () => {
allowRequestsToTwentyIcons,
},
);
const records = useRecoilValue(
recordStoreRecordsSelector({
recordIds: allRecordIds,
}),
);
const records = useFamilySelectorValueV2(recordStoreRecordsSelectorV2, {
recordIds: allRecordIds,
});
const contextChips = useMemo(() => {
const filteredCommandMenuNavigationStack =
@@ -1,4 +1,4 @@
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
@@ -8,132 +8,123 @@ 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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { isNonEmptyArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
export const useCommandMenuHistory = () => {
const { closeCommandMenu } = useCommandMenu();
const store = useStore();
const goBackFromCommandMenu = useCallback(() => {
const currentNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const goBackFromCommandMenu = useRecoilCallback(
({ snapshot, set }) => {
return () => {
const currentNavigationStack = snapshot
.getLoadable(commandMenuNavigationStackState)
.getValue();
const newNavigationStack = currentNavigationStack.slice(0, -1);
const lastNavigationStackItem = newNavigationStack.at(-1);
const newNavigationStack = currentNavigationStack.slice(0, -1);
const lastNavigationStackItem = newNavigationStack.at(-1);
if (!isDefined(lastNavigationStackItem)) {
closeCommandMenu();
return;
}
if (!isDefined(lastNavigationStackItem)) {
closeCommandMenu();
return;
}
jotaiStore.set(commandMenuPageState.atom, lastNavigationStackItem.page);
set(commandMenuPageState, lastNavigationStackItem.page);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: lastNavigationStackItem.pageTitle,
Icon: lastNavigationStackItem.pageIcon,
instanceId: lastNavigationStackItem.pageId,
});
set(commandMenuPageInfoState, {
title: lastNavigationStackItem.pageTitle,
Icon: lastNavigationStackItem.pageIcon,
instanceId: lastNavigationStackItem.pageId,
});
jotaiStore.set(commandMenuNavigationStackState.atom, newNavigationStack);
set(commandMenuNavigationStackState, newNavigationStack);
const currentMorphItems = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
const currentMorphItems = snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue();
if (currentNavigationStack.length > 0) {
const removedItem = currentNavigationStack.at(-1);
if (currentNavigationStack.length > 0) {
const removedItem = currentNavigationStack.at(-1);
if (isDefined(removedItem)) {
const newMorphItems = new Map(currentMorphItems);
newMorphItems.delete(removedItem.pageId);
set(commandMenuNavigationMorphItemsByPageState, newMorphItems);
const morphItems = currentMorphItems.get(removedItem.pageId);
if (isNonEmptyArray(morphItems)) {
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId: removedItem.pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
}
set(hasUserSelectedCommandState, false);
};
},
[closeCommandMenu, store],
);
const navigateCommandMenuHistory = useRecoilCallback(
({ snapshot, set }) => {
return (pageIndex: number) => {
const currentNavigationStack = snapshot
.getLoadable(commandMenuNavigationStackState)
.getValue();
const newNavigationStack = currentNavigationStack.slice(
0,
pageIndex + 1,
if (isDefined(removedItem)) {
const newMorphItems = new Map(currentMorphItems);
newMorphItems.delete(removedItem.pageId);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItems,
);
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 morphItems = currentMorphItems.get(removedItem.pageId);
if (isNonEmptyArray(morphItems)) {
jotaiStore.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId: removedItem.pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
}
set(commandMenuPageState, newNavigationStackItem.page);
set(commandMenuPageInfoState, {
title: newNavigationStackItem.pageTitle,
Icon: newNavigationStackItem.pageIcon,
instanceId: newNavigationStackItem.pageId,
});
const currentMorphItems = snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue();
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, [closeCommandMenu]);
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,
);
}
}
const navigateCommandMenuHistory = useCallback((pageIndex: number) => {
const currentNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const newMorphItems = new Map(
Array.from(currentMorphItems.entries()).filter(([pageId]) =>
newNavigationStack.some((item) => item.pageId === pageId),
),
const newNavigationStack = currentNavigationStack.slice(0, pageIndex + 1);
jotaiStore.set(commandMenuNavigationStackState.atom, newNavigationStack);
const newNavigationStackItem = newNavigationStack.at(-1);
if (!isDefined(newNavigationStackItem)) {
throw new Error(
`No command menu navigation stack item found for index ${pageIndex}`,
);
}
jotaiStore.set(commandMenuPageState.atom, newNavigationStackItem.page);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: newNavigationStackItem.pageTitle,
Icon: newNavigationStackItem.pageIcon,
instanceId: newNavigationStackItem.pageId,
});
const currentMorphItems = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
for (const [pageId, morphItems] of currentMorphItems.entries()) {
if (!newNavigationStack.some((item) => item.pageId === pageId)) {
jotaiStore.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
set(commandMenuNavigationMorphItemsByPageState, newMorphItems);
const newMorphItems = new Map(
Array.from(currentMorphItems.entries()).filter(([pageId]) =>
newNavigationStack.some((item) => item.pageId === pageId),
),
);
set(hasUserSelectedCommandState, false);
};
},
[store],
);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItems,
);
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, []);
return {
goBackFromCommandMenu,
@@ -11,10 +11,10 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { useGlobalHotkeys } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeys';
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 { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { Key } from 'ts-key-enum';
import { CommandMenuPages } from 'twenty-shared/types';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
@@ -30,15 +30,15 @@ export const useCommandMenuHotKeys = () => {
const { setGlobalCommandMenuContext } = useSetGlobalCommandMenuContext();
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
const commandMenuSearch = useRecoilValueV2(commandMenuSearchState);
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
const commandMenuPage = useRecoilValue(commandMenuPageState);
const commandMenuPage = useRecoilValueV2(commandMenuPageState);
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
const contextStoreTargetedRecordsRuleComponent = useRecoilComponentValue(
const contextStoreTargetedRecordsRuleComponent = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
COMMAND_MENU_COMPONENT_INSTANCE_ID,
);
@@ -11,15 +11,15 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId';
import { t } from '@lingui/core/macro';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { AppPath } from 'twenty-shared/types';
import { Avatar } from 'twenty-ui/display';
import { useDebounce } from 'use-debounce';
import { useSearchQuery } from '~/generated/graphql';
export const useCommandMenuSearchRecords = () => {
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
const commandMenuSearch = useRecoilValueV2(commandMenuSearchState);
const coreClient = useApolloCoreClient();
const [deferredCommandMenuSearch] = useDebounce(commandMenuSearch, 300);
@@ -1,5 +1,6 @@
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
type UpdateNavigationMorphItemsByPageParams = {
pageId: string;
@@ -8,26 +9,28 @@ type UpdateNavigationMorphItemsByPageParams = {
};
export const useCommandMenuUpdateNavigationMorphItemsByPage = () => {
const updateCommandMenuNavigationMorphItemsByPage = useRecoilCallback(
({ set, snapshot }) =>
async ({
pageId,
const updateCommandMenuNavigationMorphItemsByPage = useCallback(
async ({
pageId,
objectMetadataId,
objectRecordIds,
}: UpdateNavigationMorphItemsByPageParams) => {
const currentMorphItems = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
const newMorphItems = objectRecordIds.map((recordId) => ({
objectMetadataId,
objectRecordIds,
}: UpdateNavigationMorphItemsByPageParams) => {
const currentMorphItems = snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue();
recordId,
}));
const newMorphItems = objectRecordIds.map((recordId) => ({
objectMetadataId,
recordId,
}));
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageId, newMorphItems);
set(commandMenuNavigationMorphItemsByPageState, newMorphItemsMap);
},
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageId, newMorphItems);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItemsMap,
);
},
[],
);
@@ -7,153 +7,135 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
export const useCopyContextStoreStates = () => {
const copyContextStoreStates = useRecoilCallback(
({ snapshot, set }) =>
({
instanceIdToCopyFrom,
instanceIdToCopyTo,
}: {
instanceIdToCopyFrom: string;
instanceIdToCopyTo: string;
}) => {
const contextStoreCurrentObjectMetadataItemId = snapshot
.getLoadable(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const copyContextStoreStates = useCallback(
({
instanceIdToCopyFrom,
instanceIdToCopyTo,
}: {
instanceIdToCopyFrom: string;
instanceIdToCopyTo: string;
}) => {
const contextStoreCurrentObjectMetadataItemId = jotaiStore.get(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentObjectMetadataItemId,
);
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentObjectMetadataItemId,
);
const contextStoreTargetedRecordsRule = snapshot
.getLoadable(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreTargetedRecordsRule = jotaiStore.get(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreTargetedRecordsRule,
);
jotaiStore.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreTargetedRecordsRule,
);
const contextStoreNumberOfSelectedRecords = snapshot
.getLoadable(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreNumberOfSelectedRecords = jotaiStore.get(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreNumberOfSelectedRecords,
);
jotaiStore.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreNumberOfSelectedRecords,
);
const contextStoreFilters = snapshot
.getLoadable(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreFilters = jotaiStore.get(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilters,
);
jotaiStore.set(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilters,
);
const contextStoreFilterGroups = snapshot
.getLoadable(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreFilterGroups = jotaiStore.get(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilterGroups,
);
jotaiStore.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilterGroups,
);
const contextStoreAnyFieldFilterValue = snapshot
.getLoadable(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreAnyFieldFilterValue = jotaiStore.get(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreAnyFieldFilterValue,
);
jotaiStore.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreAnyFieldFilterValue,
);
const contextStoreCurrentViewId = snapshot
.getLoadable(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreCurrentViewId = jotaiStore.get(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewId,
);
jotaiStore.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewId,
);
const contextStoreCurrentViewType = snapshot
.getLoadable(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreCurrentViewType = jotaiStore.get(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewType,
);
jotaiStore.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewType,
);
const contextStoreIsFullTabWidgetInEditMode = snapshot
.getLoadable(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
const contextStoreIsFullTabWidgetInEditMode = jotaiStore.get(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreIsFullTabWidgetInEditMode,
);
},
jotaiStore.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreIsFullTabWidgetInEditMode,
);
},
[],
);
@@ -9,13 +9,12 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState';
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
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 { useStore } from 'jotai';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { type CommandMenuPages } from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -36,121 +35,110 @@ export const useNavigateCommandMenu = () => {
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const store = useStore();
const openCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
const openCommandMenu = useRecoilCallback(
({ snapshot, set }) =>
() => {
const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
const isCommandMenuClosing = jotaiStore.get(isCommandMenuClosingState.atom);
const isCommandMenuClosing = snapshot
.getLoadable(isCommandMenuClosingState)
.getValue();
if (isCommandMenuClosing) {
commandMenuCloseAnimationCompleteCleanup();
}
if (isCommandMenuClosing) {
commandMenuCloseAnimationCompleteCleanup();
}
if (isCommandMenuOpened) {
return;
}
if (isCommandMenuOpened) {
return;
}
pushFocusItemToFocusStack({
focusId: SIDE_PANEL_FOCUS_ID,
component: {
type: FocusComponentType.SIDE_PANEL,
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
});
copyContextStoreStates({
instanceIdToCopyFrom: MAIN_CONTEXT_STORE_INSTANCE_ID,
instanceIdToCopyTo: COMMAND_MENU_COMPONENT_INSTANCE_ID,
});
set(isCommandMenuOpenedState, true);
store.set(isCommandMenuOpenedStateV2.atom, true);
set(hasUserSelectedCommandState, false);
pushFocusItemToFocusStack({
focusId: SIDE_PANEL_FOCUS_ID,
component: {
type: FocusComponentType.SIDE_PANEL,
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
},
[
copyContextStoreStates,
commandMenuCloseAnimationCompleteCleanup,
pushFocusItemToFocusStack,
store,
],
);
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
});
const navigateCommandMenu = useRecoilCallback(
({ snapshot, set }) => {
return ({
page,
pageTitle,
pageIcon,
pageIconColor,
pageId,
focusTitleInput = false,
resetNavigationStack = false,
}: CommandMenuNavigationStackItem & {
resetNavigationStack?: boolean;
focusTitleInput?: boolean;
}) => {
const computedPageId = pageId || v4();
copyContextStoreStates({
instanceIdToCopyFrom: MAIN_CONTEXT_STORE_INSTANCE_ID,
instanceIdToCopyTo: COMMAND_MENU_COMPONENT_INSTANCE_ID,
});
openCommandMenu();
set(commandMenuPageState, page);
set(commandMenuPageInfoState, {
title: pageTitle,
Icon: pageIcon,
instanceId: computedPageId,
});
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, [
copyContextStoreStates,
commandMenuCloseAnimationCompleteCleanup,
pushFocusItemToFocusStack,
]);
if (focusTitleInput) {
set(
commandMenuShouldFocusTitleInputComponentState.atomFamily({
instanceId: computedPageId,
}),
true,
);
}
const navigateCommandMenu = useCallback(
({
page,
pageTitle,
pageIcon,
pageIconColor,
pageId,
focusTitleInput = false,
resetNavigationStack = false,
}: CommandMenuNavigationStackItem & {
resetNavigationStack?: boolean;
focusTitleInput?: boolean;
}) => {
const computedPageId = pageId || v4();
const isCommandMenuClosing = snapshot
.getLoadable(isCommandMenuClosingState)
.getValue();
openCommandMenu();
jotaiStore.set(commandMenuPageState.atom, page);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: pageTitle,
Icon: pageIcon,
instanceId: computedPageId,
});
const currentNavigationStack = isCommandMenuClosing
? []
: snapshot.getLoadable(commandMenuNavigationStackState).getValue();
if (focusTitleInput) {
jotaiStore.set(
commandMenuShouldFocusTitleInputComponentState.atomFamily({
instanceId: computedPageId,
}),
true,
);
}
if (resetNavigationStack) {
set(commandMenuNavigationStackState, [
{
page,
pageTitle,
pageIcon,
pageIconColor,
pageId: computedPageId,
},
]);
const isCommandMenuClosing = jotaiStore.get(
isCommandMenuClosingState.atom,
);
set(commandMenuNavigationMorphItemsByPageState, new Map());
} else {
set(commandMenuNavigationStackState, [
...currentNavigationStack,
{
page,
pageTitle,
pageIcon,
pageIconColor,
pageId: computedPageId,
},
]);
}
};
const currentNavigationStack = isCommandMenuClosing
? []
: jotaiStore.get(commandMenuNavigationStackState.atom);
if (resetNavigationStack) {
jotaiStore.set(commandMenuNavigationStackState.atom, [
{
page,
pageTitle,
pageIcon,
pageIconColor,
pageId: computedPageId,
},
]);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
new Map(),
);
} else {
jotaiStore.set(commandMenuNavigationStackState.atom, [
...currentNavigationStack,
{
page,
pageTitle,
pageIcon,
pageIconColor,
pageId: computedPageId,
},
]);
}
},
[openCommandMenu],
);
@@ -1,7 +1,8 @@
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconCalendarEvent } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -9,50 +10,48 @@ import { v4 } from 'uuid';
export const useOpenCalendarEventInCommandMenu = () => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const openCalendarEventInCommandMenu = useRecoilCallback(
({ set }) => {
return (calendarEventId: string) => {
const pageComponentInstanceId = v4();
const openCalendarEventInCommandMenu = useCallback(
(calendarEventId: string) => {
const pageComponentInstanceId = v4();
set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
calendarEventId,
);
jotaiStore.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
calendarEventId,
);
// TODO: Uncomment this once we need to calendar event title in the navigation
// const objectMetadataItem = snapshot
// .getLoadable(objectMetadataItemsState)
// .getValue()
// .find(
// ({ nameSingular }) =>
// nameSingular === CoreObjectNameSingular.CalendarEvent,
// );
// TODO: Uncomment this once we need to calendar event title in the navigation
// const objectMetadataItem = snapshot
// .getLoadable(objectMetadataItemsState)
// .getValue()
// .find(
// ({ nameSingular }) =>
// nameSingular === CoreObjectNameSingular.CalendarEvent,
// );
// set(
// commandMenuNavigationMorphItemsState,
// new Map([
// ...snapshot
// .getLoadable(commandMenuNavigationMorphItemsState)
// .getValue(),
// [
// pageComponentInstanceId,
// {
// objectMetadataId: objectMetadataItem?.id,
// recordId: calendarEventId,
// },
// ],
// ]),
// );
// set(
// commandMenuNavigationMorphItemsState,
// new Map([
// ...snapshot
// .getLoadable(commandMenuNavigationMorphItemsState)
// .getValue(),
// [
// pageComponentInstanceId,
// {
// objectMetadataId: objectMetadataItem?.id,
// recordId: calendarEventId,
// },
// ],
// ]),
// );
navigateCommandMenu({
page: CommandMenuPages.ViewCalendarEvent,
pageTitle: t`Calendar Event`,
pageIcon: IconCalendarEvent,
pageId: pageComponentInstanceId,
});
};
navigateCommandMenu({
page: CommandMenuPages.ViewCalendarEvent,
pageTitle: t`Calendar Event`,
pageIcon: IconCalendarEvent,
pageId: pageComponentInstanceId,
});
},
[navigateCommandMenu],
);
@@ -1,7 +1,8 @@
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconMail } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -9,50 +10,48 @@ import { v4 } from 'uuid';
export const useOpenEmailThreadInCommandMenu = () => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const openEmailThreadInCommandMenu = useRecoilCallback(
({ set }) => {
return (emailThreadId: string) => {
const pageComponentInstanceId = v4();
const openEmailThreadInCommandMenu = useCallback(
(emailThreadId: string) => {
const pageComponentInstanceId = v4();
set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
emailThreadId,
);
jotaiStore.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
emailThreadId,
);
// TODO: Uncomment this once we need to show the thread title in the navigation
// const objectMetadataItem = snapshot
// .getLoadable(objectMetadataItemsState)
// .getValue()
// .find(
// ({ nameSingular }) =>
// nameSingular === CoreObjectNameSingular.MessageThread,
// );
// TODO: Uncomment this once we need to show the thread title in the navigation
// const objectMetadataItem = snapshot
// .getLoadable(objectMetadataItemsState)
// .getValue()
// .find(
// ({ nameSingular }) =>
// nameSingular === CoreObjectNameSingular.MessageThread,
// );
// set(
// commandMenuNavigationMorphItemsState,
// new Map([
// ...snapshot
// .getLoadable(commandMenuNavigationMorphItemsState)
// .getValue(),
// [
// pageComponentInstanceId,
// {
// objectMetadataId: objectMetadataItem?.id,
// recordId: emailThreadId,
// },
// ],
// ]),
// );
// set(
// commandMenuNavigationMorphItemsState,
// new Map([
// ...snapshot
// .getLoadable(commandMenuNavigationMorphItemsState)
// .getValue(),
// [
// pageComponentInstanceId,
// {
// objectMetadataId: objectMetadataItem?.id,
// recordId: emailThreadId,
// },
// ],
// ]),
// );
navigateCommandMenu({
page: CommandMenuPages.ViewEmailThread,
pageTitle: t`Email Thread`,
pageIcon: IconMail,
pageId: pageComponentInstanceId,
});
};
navigateCommandMenu({
page: CommandMenuPages.ViewEmailThread,
pageTitle: t`Email Thread`,
pageIcon: IconMail,
pageId: pageComponentInstanceId,
});
},
[navigateCommandMenu],
);
@@ -1,6 +1,6 @@
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { viewableFrontComponentIdComponentState } from '@/command-menu/pages/front-component/states/viewableFrontComponentIdComponentState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -8,38 +8,34 @@ import { v4 } from 'uuid';
export const useOpenFrontComponentInCommandMenu = () => {
const { navigateCommandMenu } = useCommandMenu();
const openFrontComponentInCommandMenu = useRecoilCallback(
({ set }) =>
({
frontComponentId,
pageTitle,
pageIcon,
resetNavigationStack = false,
}: {
frontComponentId: string;
pageTitle: string;
pageIcon: IconComponent;
resetNavigationStack?: boolean;
}) => {
const pageComponentInstanceId = v4();
const openFrontComponentInCommandMenu = ({
frontComponentId,
pageTitle,
pageIcon,
resetNavigationStack = false,
}: {
frontComponentId: string;
pageTitle: string;
pageIcon: IconComponent;
resetNavigationStack?: boolean;
}) => {
const pageComponentInstanceId = v4();
set(
viewableFrontComponentIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
frontComponentId,
);
jotaiStore.set(
viewableFrontComponentIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
frontComponentId,
);
navigateCommandMenu({
page: CommandMenuPages.ViewFrontComponent,
pageTitle,
pageIcon,
pageId: pageComponentInstanceId,
resetNavigationStack,
});
},
[navigateCommandMenu],
);
navigateCommandMenu({
page: CommandMenuPages.ViewFrontComponent,
pageTitle,
pageIcon,
pageId: pageComponentInstanceId,
resetNavigationStack,
});
};
return {
openFrontComponentInCommandMenu,
@@ -6,8 +6,9 @@ import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRec
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { msg, t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { IconArrowMerge } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -39,43 +40,38 @@ export const useOpenMergeRecordsPageInCommandMenu = ({
},
});
const openMergeRecordsPageInCommandMenu = useRecoilCallback(
({ set }) => {
return async () => {
const pageId = v4();
const openMergeRecordsPageInCommandMenu = useCallback(async () => {
const pageId = v4();
set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageId,
}),
objectMetadataItem.id,
);
await updateCommandMenuNavigationMorphItemsByPage({
pageId,
objectMetadataId: objectMetadataItem.id,
objectRecordIds,
});
const { records } = await findManyRecordsLazy();
upsertRecordsInStore({ partialRecords: records ?? [] });
navigateCommandMenu({
page: CommandMenuPages.MergeRecords,
pageTitle: t(msg`Merge records`),
pageIcon: IconArrowMerge,
pageId,
});
};
},
[
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageId,
}),
objectMetadataItem.id,
);
await updateCommandMenuNavigationMorphItemsByPage({
pageId,
objectMetadataId: objectMetadataItem.id,
objectRecordIds,
findManyRecordsLazy,
upsertRecordsInStore,
navigateCommandMenu,
updateCommandMenuNavigationMorphItemsByPage,
],
);
});
const { records } = await findManyRecordsLazy();
upsertRecordsInStore({ partialRecords: records ?? [] });
navigateCommandMenu({
page: CommandMenuPages.MergeRecords,
pageTitle: t(msg`Merge records`),
pageIcon: IconArrowMerge,
pageId,
});
}, [
objectMetadataItem.id,
objectRecordIds,
findManyRecordsLazy,
upsertRecordsInStore,
navigateCommandMenu,
updateCommandMenuNavigationMorphItemsByPage,
]);
return {
openMergeRecordsPageInCommandMenu,
@@ -15,14 +15,13 @@ import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objec
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { getIconColorForObjectType } from '@/object-metadata/utils/getIconColorForObjectType';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { useRunWorkflowRunOpeningInCommandMenuSideEffects } from '@/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { v4 } from 'uuid';
@@ -35,168 +34,163 @@ export const useOpenRecordInCommandMenu = () => {
const { runWorkflowRunOpeningInCommandMenuSideEffects } =
useRunWorkflowRunOpeningInCommandMenuSideEffects();
const openRecordInCommandMenu = useRecoilCallback(
({ set, snapshot }) => {
return ({
recordId,
objectNameSingular,
isNewRecord = false,
resetNavigationStack = false,
}: {
recordId: string;
objectNameSingular: string;
isNewRecord?: boolean;
resetNavigationStack?: boolean;
}) => {
const navigationStack = getSnapshotValue(
snapshot,
commandMenuNavigationStackState,
);
const openRecordInCommandMenu = useCallback(
({
recordId,
objectNameSingular,
isNewRecord = false,
resetNavigationStack = false,
}: {
recordId: string;
objectNameSingular: string;
isNewRecord?: boolean;
resetNavigationStack?: boolean;
}) => {
const navigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const currentNavigationStackItem = navigationStack.at(-1);
const currentNavigationStackItem = navigationStack.at(-1);
if (isDefined(currentNavigationStackItem)) {
const currentRecordId = getSnapshotValue(
snapshot,
viewableRecordIdComponentState.atomFamily({
instanceId: currentNavigationStackItem.pageId,
}),
);
if (currentRecordId === recordId) {
return;
}
}
const pageComponentInstanceId = v4();
set(
viewableRecordNameSingularComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectNameSingular,
);
set(
if (isDefined(currentNavigationStackItem)) {
const currentRecordId = jotaiStore.get(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
recordId,
);
set(viewableRecordIdState, recordId);
const objectMetadataItem = jotaiStore.get(
objectMetadataItemFamilySelector.selectorFamily({
objectName: objectNameSingular,
objectNameType: 'singular',
instanceId: currentNavigationStackItem.pageId,
}),
);
if (!objectMetadataItem) {
throw new Error(
`No object metadata item found for object name ${objectNameSingular}`,
);
if (currentRecordId === recordId) {
return;
}
}
set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectMetadataItem.id,
const pageComponentInstanceId = v4();
jotaiStore.set(
viewableRecordNameSingularComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectNameSingular,
);
jotaiStore.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
recordId,
);
jotaiStore.set(viewableRecordIdState.atom, recordId);
const objectMetadataItem = jotaiStore.get(
objectMetadataItemFamilySelector.selectorFamily({
objectName: objectNameSingular,
objectNameType: 'singular',
}),
);
if (!objectMetadataItem) {
throw new Error(
`No object metadata item found for object name ${objectNameSingular}`,
);
}
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
{
mode: 'selection',
selectedRecordIds: [recordId],
},
);
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectMetadataItem.id,
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
1,
);
jotaiStore.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
{
mode: 'selection',
selectedRecordIds: [recordId],
},
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
ContextStoreViewType.ShowPage,
);
jotaiStore.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
1,
);
set(
jotaiStore.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
ContextStoreViewType.ShowPage,
);
jotaiStore.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
jotaiStore.get(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
snapshot
.getLoadable(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
)
.getValue(),
);
),
);
set(
jotaiStore.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
jotaiStore.get(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
snapshot
.getLoadable(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
)
.getValue(),
);
),
);
const currentMorphItems = snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue();
const currentMorphItems = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
const morphItemToAdd = {
objectMetadataId: objectMetadataItem.id,
recordId,
};
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageComponentInstanceId, [morphItemToAdd]);
set(commandMenuNavigationMorphItemsByPageState, newMorphItemsMap);
const Icon = objectMetadataItem?.icon
? getIcon(objectMetadataItem.icon)
: getIcon('IconList');
const IconColor = getIconColorForObjectType({
objectType: objectMetadataItem.nameSingular,
theme,
});
const objectLabelSingular = objectMetadataItem.labelSingular;
navigateCommandMenu({
page: CommandMenuPages.ViewRecord,
pageTitle: isNewRecord
? t`New ${objectLabelSingular}`
: objectLabelSingular,
pageIcon: Icon,
pageIconColor: IconColor,
pageId: pageComponentInstanceId,
resetNavigationStack,
});
if (objectNameSingular === CoreObjectNameSingular.WorkflowRun) {
runWorkflowRunOpeningInCommandMenuSideEffects({
objectMetadataItem,
recordId,
});
}
const morphItemToAdd = {
objectMetadataId: objectMetadataItem.id,
recordId,
};
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageComponentInstanceId, [morphItemToAdd]);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItemsMap,
);
const Icon = objectMetadataItem?.icon
? getIcon(objectMetadataItem.icon)
: getIcon('IconList');
const IconColor = getIconColorForObjectType({
objectType: objectMetadataItem.nameSingular,
theme,
});
const objectLabelSingular = objectMetadataItem.labelSingular;
navigateCommandMenu({
page: CommandMenuPages.ViewRecord,
pageTitle: isNewRecord
? t`New ${objectLabelSingular}`
: objectLabelSingular,
pageIcon: Icon,
pageIconColor: IconColor,
pageId: pageComponentInstanceId,
resetNavigationStack,
});
if (objectNameSingular === CoreObjectNameSingular.WorkflowRun) {
runWorkflowRunOpeningInCommandMenuSideEffects({
objectMetadataItem,
recordId,
});
}
},
[
getIcon,
@@ -2,7 +2,7 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandM
import { CommandMenuPages } from 'twenty-shared/types';
import { msg, t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { IconBoxMultiple } from 'twenty-ui/display';
type UseOpenUpdateMultipleRecordsPageInCommandMenuProps = {
@@ -14,15 +14,13 @@ export const useOpenUpdateMultipleRecordsPageInCommandMenu = ({
}: UseOpenUpdateMultipleRecordsPageInCommandMenuProps) => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const openUpdateMultipleRecordsPageInCommandMenu = useRecoilCallback(() => {
return async () => {
navigateCommandMenu({
page: CommandMenuPages.UpdateRecords,
pageTitle: t(msg`Update records`),
pageIcon: IconBoxMultiple,
pageId: contextStoreInstanceId,
});
};
const openUpdateMultipleRecordsPageInCommandMenu = useCallback(async () => {
navigateCommandMenu({
page: CommandMenuPages.UpdateRecords,
pageTitle: t(msg`Update records`),
pageIcon: IconBoxMultiple,
pageId: contextStoreInstanceId,
});
}, [navigateCommandMenu, contextStoreInstanceId]);
return {
@@ -5,63 +5,62 @@ import { contextStoreFilterGroupsComponentState } from '@/context-store/states/c
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
export const useResetContextStoreStates = () => {
const resetContextStoreStates = useRecoilCallback(({ set }) => {
return (instanceId: string) => {
set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
const resetContextStoreStates = useCallback((instanceId: string) => {
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
jotaiStore.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId,
}),
0,
);
jotaiStore.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId,
}),
0,
);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId,
}),
[],
);
jotaiStore.set(
contextStoreFiltersComponentState.atomFamily({
instanceId,
}),
[],
);
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId,
}),
[],
);
jotaiStore.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId,
}),
[],
);
set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId,
}),
'',
);
jotaiStore.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId,
}),
'',
);
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
};
jotaiStore.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
}, []);
return { resetContextStoreStates };
@@ -10,75 +10,71 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
export const useSetGlobalCommandMenuContext = () => {
const { copyContextStoreStates } = useCopyContextStoreStates();
const setGlobalCommandMenuContext = useRecoilCallback(
({ set }) => {
return () => {
copyContextStoreStates({
instanceIdToCopyFrom: COMMAND_MENU_COMPONENT_INSTANCE_ID,
instanceIdToCopyTo: COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
});
const setGlobalCommandMenuContext = useCallback(() => {
copyContextStoreStates({
instanceIdToCopyFrom: COMMAND_MENU_COMPONENT_INSTANCE_ID,
instanceIdToCopyTo: COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
});
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
jotaiStore.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
0,
);
jotaiStore.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
0,
);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
jotaiStore.set(
contextStoreFiltersComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
jotaiStore.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
'',
);
jotaiStore.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
'',
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
ContextStoreViewType.Table,
);
jotaiStore.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
ContextStoreViewType.Table,
);
set(commandMenuPageInfoState, {
title: undefined,
Icon: undefined,
instanceId: '',
});
jotaiStore.set(commandMenuPageInfoState.atom, {
title: undefined,
Icon: undefined,
instanceId: '',
});
set(hasUserSelectedCommandState, false);
};
},
[copyContextStoreStates],
);
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, [copyContextStoreStates]);
return {
setGlobalCommandMenuContext,
@@ -1,53 +1,54 @@
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { type IconComponent, IconDotsVertical } from 'twenty-ui/display';
export const useUpdateCommandMenuPageInfo = () => {
const updateCommandMenuPageInfo = useRecoilCallback(
({ snapshot, set }) =>
({
pageTitle,
pageIcon,
}: {
pageTitle?: string;
pageIcon?: IconComponent;
}) => {
const commandMenuPageInfo = snapshot
.getLoadable(commandMenuPageInfoState)
.getValue();
const updateCommandMenuPageInfo = useCallback(
({
pageTitle,
pageIcon,
}: {
pageTitle?: string;
pageIcon?: IconComponent;
}) => {
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
const newCommandMenuPageInfo = {
...commandMenuPageInfo,
title: pageTitle ?? commandMenuPageInfo.title ?? '',
Icon: pageIcon ?? commandMenuPageInfo.Icon ?? IconDotsVertical,
};
const newCommandMenuPageInfo = {
...commandMenuPageInfo,
title: pageTitle ?? commandMenuPageInfo.title ?? '',
Icon: pageIcon ?? commandMenuPageInfo.Icon ?? IconDotsVertical,
};
set(commandMenuPageInfoState, newCommandMenuPageInfo);
jotaiStore.set(commandMenuPageInfoState.atom, newCommandMenuPageInfo);
const commandMenuNavigationStack = snapshot
.getLoadable(commandMenuNavigationStackState)
.getValue();
const commandMenuNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const lastCommandMenuNavigationStackItem =
commandMenuNavigationStack.at(-1);
const lastCommandMenuNavigationStackItem =
commandMenuNavigationStack.at(-1);
if (!lastCommandMenuNavigationStackItem) {
return;
}
if (!lastCommandMenuNavigationStackItem) {
return;
}
const newCommandMenuNavigationStack = [
...commandMenuNavigationStack.slice(0, -1),
{
page: lastCommandMenuNavigationStackItem.page,
pageTitle: newCommandMenuPageInfo.title,
pageIcon: newCommandMenuPageInfo.Icon,
pageId: lastCommandMenuNavigationStackItem.pageId,
},
];
const newCommandMenuNavigationStack = [
...commandMenuNavigationStack.slice(0, -1),
{
page: lastCommandMenuNavigationStackItem.page,
pageTitle: newCommandMenuPageInfo.title,
pageIcon: newCommandMenuPageInfo.Icon,
pageId: lastCommandMenuNavigationStackItem.pageId,
},
];
set(commandMenuNavigationStackState, newCommandMenuNavigationStack);
},
jotaiStore.set(
commandMenuNavigationStackState.atom,
newCommandMenuNavigationStack,
);
},
[],
);
@@ -3,11 +3,12 @@ import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workfl
import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState';
import { commandMenuWorkflowStepIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState';
import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { useSetInitialWorkflowRunRightDrawerTab } from '@/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import { t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
@@ -22,233 +23,221 @@ export const useWorkflowCommandMenu = () => {
const { setInitialWorkflowRunRightDrawerTab } =
useSetInitialWorkflowRunRightDrawerTab();
const openWorkflowTriggerTypeInCommandMenu = useRecoilCallback(
({ set }) => {
return (workflowId: string) => {
const pageId = v4();
const openWorkflowTriggerTypeInCommandMenu = useCallback(
(workflowId: string) => {
const pageId = v4();
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowTriggerSelectType,
pageTitle: t`Trigger Type`,
pageIcon: IconBolt,
pageId,
});
};
},
[navigateCommandMenu],
);
const openWorkflowCreateStepInCommandMenu = useRecoilCallback(
({ set }) => {
return (workflowId: string) => {
const pageId = v4();
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepCreate,
pageTitle: t`Select Action`,
pageIcon: IconSettingsAutomation,
pageId,
});
};
},
[navigateCommandMenu],
);
const openWorkflowEditStepInCommandMenu = useRecoilCallback(
({ set }) => {
return (
workflowId: string,
title: string,
icon: IconComponent,
stepId?: string,
) => {
const pageId = v4();
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
if (isDefined(stepId)) {
set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
stepId,
);
set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowId,
}),
stepId,
);
}
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepEdit,
pageTitle: title,
pageIcon: icon,
pageId,
});
};
},
[navigateCommandMenu],
);
const openWorkflowEditStepTypeInCommandMenu = useRecoilCallback(
({ set }) => {
return (workflowId: string) => {
const pageId = v4();
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepEditType,
pageTitle: t`Select action`,
pageIcon: IconSettingsAutomation,
pageId,
});
};
},
[navigateCommandMenu],
);
const openWorkflowViewStepInCommandMenu = useRecoilCallback(
({ set }) => {
return ({
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
workflowVersionId,
title,
icon,
stepId,
}: {
workflowId: string;
workflowVersionId: string;
title: string;
icon: IconComponent;
stepId?: string;
}) => {
const pageId = v4();
);
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
set(
commandMenuWorkflowVersionIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowVersionId,
);
if (isDefined(stepId)) {
set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
stepId,
);
set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowVersionId,
}),
stepId,
);
}
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepView,
pageTitle: title,
pageIcon: icon,
pageId,
});
};
navigateCommandMenu({
page: CommandMenuPages.WorkflowTriggerSelectType,
pageTitle: t`Trigger Type`,
pageIcon: IconBolt,
pageId,
});
},
[navigateCommandMenu],
);
const openWorkflowRunViewStepInCommandMenu = useRecoilCallback(
({ set }) => {
return ({
workflowId,
workflowRunId,
title,
icon,
workflowSelectedNode,
stepExecutionStatus,
}: {
workflowId: string;
workflowRunId: string;
title: string;
icon: IconComponent;
workflowSelectedNode: string;
stepExecutionStatus: WorkflowRunStepStatus;
}) => {
const pageId = v4();
const openWorkflowCreateStepInCommandMenu = useCallback(
(workflowId: string) => {
const pageId = v4();
set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
set(
commandMenuWorkflowRunIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowRunId,
);
set(
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepCreate,
pageTitle: t`Select Action`,
pageIcon: IconSettingsAutomation,
pageId,
});
},
[navigateCommandMenu],
);
const openWorkflowEditStepInCommandMenu = useCallback(
(
workflowId: string,
title: string,
icon: IconComponent,
stepId?: string,
) => {
const pageId = v4();
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
if (isDefined(stepId)) {
jotaiStore.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowSelectedNode,
stepId,
);
set(
jotaiStore.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowRunId,
instanceId: workflowId,
}),
workflowSelectedNode,
stepId,
);
}
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepEdit,
pageTitle: title,
pageIcon: icon,
pageId,
});
},
[navigateCommandMenu],
);
const openWorkflowEditStepTypeInCommandMenu = useCallback(
(workflowId: string) => {
const pageId = v4();
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepEditType,
pageTitle: t`Select action`,
pageIcon: IconSettingsAutomation,
pageId,
});
},
[navigateCommandMenu],
);
const openWorkflowViewStepInCommandMenu = useCallback(
({
workflowId,
workflowVersionId,
title,
icon,
stepId,
}: {
workflowId: string;
workflowVersionId: string;
title: string;
icon: IconComponent;
stepId?: string;
}) => {
const pageId = v4();
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
jotaiStore.set(
commandMenuWorkflowVersionIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowVersionId,
);
if (isDefined(stepId)) {
jotaiStore.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
stepId,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowRunStepView,
pageTitle: title,
pageIcon: icon,
pageId,
});
jotaiStore.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowVersionId,
}),
stepId,
);
}
setInitialWorkflowRunRightDrawerTab({
workflowSelectedNode,
stepExecutionStatus,
});
};
navigateCommandMenu({
page: CommandMenuPages.WorkflowStepView,
pageTitle: title,
pageIcon: icon,
pageId,
});
},
[navigateCommandMenu],
);
const openWorkflowRunViewStepInCommandMenu = useCallback(
({
workflowId,
workflowRunId,
title,
icon,
workflowSelectedNode,
stepExecutionStatus,
}: {
workflowId: string;
workflowRunId: string;
title: string;
icon: IconComponent;
workflowSelectedNode: string;
stepExecutionStatus: WorkflowRunStepStatus;
}) => {
const pageId = v4();
jotaiStore.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
jotaiStore.set(
commandMenuWorkflowRunIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowRunId,
);
jotaiStore.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowSelectedNode,
);
jotaiStore.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowRunId,
}),
workflowSelectedNode,
);
navigateCommandMenu({
page: CommandMenuPages.WorkflowRunStepView,
pageTitle: title,
pageIcon: icon,
pageId,
});
setInitialWorkflowRunRightDrawerTab({
workflowSelectedNode,
stepExecutionStatus,
});
},
[navigateCommandMenu, setInitialWorkflowRunRightDrawerTab],
);
@@ -7,12 +7,12 @@ import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graph
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { PageLayoutType } from '~/generated-metadata/graphql';
export const CommandMenuCalendarEventPage = () => {
const { upsertRecordsInStore } = useUpsertRecordsInStore();
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
);
@@ -1,7 +1,7 @@
import { Suspense, lazy } from 'react';
import { viewableFrontComponentIdComponentState } from '@/command-menu/pages/front-component/states/viewableFrontComponentIdComponentState';
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 FrontComponentRenderer = lazy(() =>
@@ -11,7 +11,7 @@ const FrontComponentRenderer = lazy(() =>
);
export const CommandMenuFrontComponentPage = () => {
const viewableFrontComponentId = useRecoilComponentValue(
const viewableFrontComponentId = useRecoilComponentValueV2(
viewableFrontComponentIdComponentState,
);
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const viewableFrontComponentIdComponentState = createComponentState<
export const viewableFrontComponentIdComponentState = createComponentStateV2<
string | null
>({
key: 'command-menu/viewable-front-component-id',
@@ -8,7 +8,7 @@ import { EmailThreadMessage } from '@/activities/emails/components/EmailThreadMe
import { CommandMenuMessageThreadIntermediaryMessages } from '@/command-menu/pages/message-thread/components/CommandMenuMessageThreadIntermediaryMessages';
import { useEmailThreadInCommandMenu } from '@/command-menu/pages/message-thread/hooks/useEmailThreadInCommandMenu';
import { messageThreadComponentState } from '@/command-menu/pages/message-thread/states/messageThreadComponentState';
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 { ConnectedAccountProvider } from 'twenty-shared/types';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
@@ -47,7 +47,7 @@ const ALLOWED_REPLY_PROVIDERS = [
];
export const CommandMenuMessageThreadPage = () => {
const setMessageThread = useSetRecoilComponentState(
const setMessageThread = useSetRecoilComponentStateV2(
messageThreadComponentState,
);
@@ -366,8 +366,8 @@ const mocks = [
const Wrapper = ({ children }: { children: React.ReactNode }) => {
const MetadataWrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks: mocks,
onInitializeRecoilSnapshot: ({ set }) => {
set(
onInitializeJotaiStore: (store) => {
store.set(
viewableRecordIdComponentState.atomFamily({
instanceId: 'test-instance',
}),
@@ -13,13 +13,13 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { MessageParticipantRole } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
// to improve - https://github.com/twentyhq/twenty/issues/12190
export const useEmailThreadInCommandMenu = () => {
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
);
const { upsertRecordsInStore } = useUpsertRecordsInStore();
@@ -1,9 +1,9 @@
import { type MessageThread } from '@/activities/emails/types/MessageThread';
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const messageThreadComponentState =
createComponentState<MessageThread | null>({
createComponentStateV2<MessageThread | null>({
key: 'messageThreadComponentState',
defaultValue: null,
componentInstanceContext: CommandMenuPageComponentInstanceContext,
@@ -14,11 +14,10 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useStore } from 'jotai';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useRecoilCallback } from 'recoil';
const StyledChartFiltersPageContainer = styled.div`
display: flex;
@@ -50,49 +49,33 @@ export const ChartFiltersSettings = ({
const { updateCurrentWidgetConfig } =
useUpdateCurrentWidgetConfig(pageLayoutId);
const currentRecordFiltersCallbackState = useRecoilComponentCallbackState(
const currentRecordFiltersAtom = useRecoilComponentStateCallbackStateV2(
currentRecordFiltersComponentState,
instanceId,
);
const currentRecordFilterGroupsCallbackState =
useRecoilComponentCallbackState(
currentRecordFilterGroupsComponentState,
instanceId,
);
const currentRecordFilterGroupsAtom = useRecoilComponentStateCallbackStateV2(
currentRecordFilterGroupsComponentState,
instanceId,
);
const store = useStore();
const chartWidgetConfiguration = widget.configuration;
const handleFiltersUpdate = useRecoilCallback(
({ snapshot }) =>
() => {
const currentRecordFilters = getSnapshotValue(
snapshot,
currentRecordFiltersCallbackState,
);
const handleFiltersUpdate = () => {
const currentRecordFilters = store.get(currentRecordFiltersAtom);
const currentRecordFilterGroups = store.get(currentRecordFilterGroupsAtom);
const currentRecordFilterGroups = getSnapshotValue(
snapshot,
currentRecordFilterGroupsCallbackState,
);
updateCurrentWidgetConfig({
objectMetadataId: objectMetadataItem.id,
configToUpdate: {
filter: {
recordFilters: currentRecordFilters,
recordFilterGroups: currentRecordFilterGroups,
},
} satisfies Partial<ChartWidgetConfiguration>,
});
},
[
currentRecordFiltersCallbackState,
currentRecordFilterGroupsCallbackState,
objectMetadataItem,
updateCurrentWidgetConfig,
],
);
updateCurrentWidgetConfig({
objectMetadataId: objectMetadataItem.id,
configToUpdate: {
filter: {
recordFilters: currentRecordFilters,
recordFilterGroups: currentRecordFilterGroups,
},
} satisfies Partial<ChartWidgetConfiguration>,
});
};
return (
<>
@@ -3,8 +3,8 @@ import { type ChartFilters } from '@/command-menu/pages/page-layout/types/ChartF
import { useSetAdvancedFilterDropdownStates } from '@/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates';
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -16,13 +16,13 @@ export const ChartFiltersSettingsInitializeStateEffect = ({
initialChartFilters,
}: ChartFiltersSettingsInitializeStateEffectProps) => {
const [hasInitializedChartFilters, setHasInitializedChartFilters] =
useRecoilComponentState(hasInitializedChartFiltersComponentState);
useRecoilComponentStateV2(hasInitializedChartFiltersComponentState);
const setCurrentRecordFilters = useSetRecoilComponentState(
const setCurrentRecordFilters = useSetRecoilComponentStateV2(
currentRecordFiltersComponentState,
);
const setCurrentRecordFilterGroups = useSetRecoilComponentState(
const setCurrentRecordFilterGroups = useSetRecoilComponentStateV2(
currentRecordFilterGroupsComponentState,
);
@@ -14,7 +14,7 @@ import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layou
import { shouldHideChartSetting } from '@/command-menu/pages/page-layout/utils/shouldHideChartSetting';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { hasWidgetTooManyGroupsComponentState } from '@/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState';
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 { t } from '@lingui/core/macro';
import { isFieldMetadataDateKind } from 'twenty-shared/utils';
@@ -58,7 +58,7 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
);
const [hasWidgetTooManyGroups, setHasWidgetTooManyGroups] =
useRecoilComponentState(hasWidgetTooManyGroupsComponentState);
useRecoilComponentStateV2(hasWidgetTooManyGroupsComponentState);
const handleGraphTypeChange = (graphType: GraphType) => {
const configToUpdate = getConfigToUpdateAfterGraphTypeChange(graphType);
@@ -5,7 +5,7 @@ import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWid
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext';
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 { isDefined } from 'twenty-shared/utils';
@@ -18,12 +18,12 @@ const StyledContainer = styled.div`
export const CommandMenuPageLayoutChartSettings = () => {
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const draftPageLayout = useRecoilComponentValue(
const draftPageLayout = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const pageLayoutEditingWidgetId = useRecoilComponentValue(
const pageLayoutEditingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
@@ -4,19 +4,19 @@ import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWid
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { isDefined } from 'twenty-shared/utils';
export const CommandMenuPageLayoutGraphFilter = () => {
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const draftPageLayout = useRecoilComponentValue(
const draftPageLayout = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const pageLayoutEditingWidgetId = useRecoilComponentValue(
const pageLayoutEditingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
@@ -11,8 +11,8 @@ import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDr
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
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 { isDefined } from 'twenty-shared/utils';
import {
@@ -26,12 +26,12 @@ export const CommandMenuPageLayoutTabSettings = () => {
const { closeCommandMenu } = useCommandMenu();
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const draft = useRecoilComponentValue(
const draft = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const [openTabId, setOpenTabId] = useRecoilComponentState(
const [openTabId, setOpenTabId] = useRecoilComponentStateV2(
pageLayoutTabSettingsOpenTabIdComponentState,
pageLayoutId,
);
@@ -16,8 +16,8 @@ import { useRemovePageLayoutWidgetAndPreservePosition } from '@/page-layout/hook
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useQuery } from '@apollo/client';
import { t } from '@lingui/core/macro';
@@ -79,12 +79,12 @@ export const CommandMenuPageLayoutWidgetTypeSelect = () => {
);
const [pageLayoutEditingWidgetId, setPageLayoutEditingWidgetId] =
useRecoilComponentState(
useRecoilComponentStateV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
const draftPageLayout = useRecoilComponentValue(
const draftPageLayout = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
@@ -6,7 +6,6 @@ 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 { useLingui } from '@lingui/react/macro';
import { useId } from 'react';
@@ -24,7 +23,7 @@ export const WidgetSettingsFooter = ({
const { closeDropdown } = useCloseDropdown();
const { duplicateWidget } = useDuplicatePageLayoutWidget(pageLayoutId);
const { deletePageLayoutWidget } = useDeletePageLayoutWidget(pageLayoutId);
const editingWidgetId = useRecoilComponentValue(
const editingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
@@ -2,7 +2,7 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandM
import { type PageLayoutCommandMenuPage } from '@/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage';
import { getPageLayoutIcon } from '@/command-menu/pages/page-layout/utils/getPageLayoutIcon';
import { getPageLayoutPageTitle } from '@/command-menu/pages/page-layout/utils/getPageLayoutPageTitle';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { type IconComponent } from 'twenty-ui/display';
@@ -17,8 +17,8 @@ type NavigatePageLayoutCommandMenuProps = {
export const useNavigatePageLayoutCommandMenu = () => {
const { navigateCommandMenu } = useNavigateCommandMenu();
const navigatePageLayoutCommandMenu = useRecoilCallback(() => {
return ({
const navigatePageLayoutCommandMenu = useCallback(
({
commandMenuPage,
pageTitle,
pageIcon,
@@ -36,8 +36,9 @@ export const useNavigatePageLayoutCommandMenu = () => {
focusTitleInput,
resetNavigationStack,
});
};
}, [navigateCommandMenu]);
},
[navigateCommandMenu],
);
return {
navigatePageLayoutCommandMenu,
@@ -1,10 +1,10 @@
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilValue } from 'recoil';
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
export const usePageLayoutIdFromContextStoreTargetedRecord = () => {
const targetedRecordsRule = useRecoilComponentValue(
const targetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
@@ -19,7 +19,7 @@ export const usePageLayoutIdFromContextStoreTargetedRecord = () => {
const recordId: string = targetedRecordsRule.selectedRecordIds[0];
const record = useRecoilValue(recordStoreFamilyState(recordId));
const record = useFamilyRecoilValueV2(recordStoreFamilyState, recordId);
return { pageLayoutId: record?.pageLayoutId };
};
@@ -2,16 +2,16 @@ import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
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';
export const usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord =
() => {
const targetedRecordsRule = useRecoilComponentValue(
const targetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
const objectMetadataId = useRecoilComponentValue(
const objectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataItemIdComponentState,
);
@@ -5,7 +5,8 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { hasInitializedCurrentRecordFilterGroupsComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordFilterGroupsComponentFamilyState';
import { hasInitializedCurrentRecordFiltersComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordFiltersComponentFamilyState';
import { useRecoilCallback } from 'recoil';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
export const useResetChartDraftFiltersSettings = () => {
@@ -13,42 +14,42 @@ export const useResetChartDraftFiltersSettings = () => {
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
const resetChartDraftFiltersSettings = useRecoilCallback(
({ set }) =>
(objectMetadataItemId: string) => {
if (!isDefined(widgetInEditMode)) {
return;
}
const resetChartDraftFiltersSettings = useCallback(
(objectMetadataItemId: string) => {
if (!isDefined(widgetInEditMode)) {
return;
}
const { instanceId } = getChartFiltersSettingsInstanceId({
widgetId: widgetInEditMode.id,
objectMetadataItemId: objectMetadataItemId,
});
const { instanceId } = getChartFiltersSettingsInstanceId({
widgetId: widgetInEditMode.id,
objectMetadataItemId: objectMetadataItemId,
});
set(
hasInitializedCurrentRecordFilterGroupsComponentFamilyState.atomFamily(
{
familyKey: {},
instanceId,
},
),
false,
);
jotaiStore.set(
hasInitializedCurrentRecordFilterGroupsComponentFamilyState.atomFamily({
familyKey: {},
instanceId,
}),
false,
);
set(
hasInitializedCurrentRecordFiltersComponentFamilyState.atomFamily({
familyKey: {},
instanceId,
}),
false,
);
jotaiStore.set(
hasInitializedCurrentRecordFiltersComponentFamilyState.atomFamily({
familyKey: {},
instanceId,
}),
false,
);
set(currentRecordFiltersComponentState.atomFamily({ instanceId }), []);
set(
currentRecordFilterGroupsComponentState.atomFamily({ instanceId }),
[],
);
},
jotaiStore.set(
currentRecordFiltersComponentState.atomFamily({ instanceId }),
[],
);
jotaiStore.set(
currentRecordFilterGroupsComponentState.atomFamily({ instanceId }),
[],
);
},
[widgetInEditMode],
);
@@ -1,51 +1,53 @@
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import type { PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilCallback } from 'recoil';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useStore } from 'jotai';
import { useCallback } from 'react';
export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
const pageLayoutDraftCallbackState = useRecoilComponentCallbackState(
const pageLayoutDraftAtom = useRecoilComponentStateCallbackStateV2(
pageLayoutDraftComponentState,
pageLayoutIdFromProps,
);
const currentlyEditingWidgetId = useRecoilComponentValue(
const currentlyEditingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutIdFromProps,
);
const updateCurrentWidgetConfig = useRecoilCallback(
({ set }) =>
({
objectMetadataId,
configToUpdate,
}: {
objectMetadataId?: string | null;
configToUpdate?: Partial<PageLayoutWidget['configuration']>;
}) => {
set(pageLayoutDraftCallbackState, (prev) => ({
...prev,
tabs: prev.tabs.map((tab) => ({
...tab,
widgets: tab.widgets.map((widget) =>
widget.id === currentlyEditingWidgetId
? {
...widget,
objectMetadataId:
objectMetadataId ?? widget.objectMetadataId,
configuration: {
...(widget.configuration ?? {}),
...configToUpdate,
} as PageLayoutWidget['configuration'],
}
: widget,
),
})),
}));
},
[pageLayoutDraftCallbackState, currentlyEditingWidgetId],
const store = useStore();
const updateCurrentWidgetConfig = useCallback(
({
objectMetadataId,
configToUpdate,
}: {
objectMetadataId?: string | null;
configToUpdate?: Partial<PageLayoutWidget['configuration']>;
}) => {
const prev = store.get(pageLayoutDraftAtom);
store.set(pageLayoutDraftAtom, {
...prev,
tabs: prev.tabs.map((tab) => ({
...tab,
widgets: tab.widgets.map((widget) =>
widget.id === currentlyEditingWidgetId
? {
...widget,
objectMetadataId: objectMetadataId ?? widget.objectMetadataId,
configuration: {
...(widget.configuration ?? {}),
...configToUpdate,
} as PageLayoutWidget['configuration'],
}
: widget,
),
})),
});
},
[pageLayoutDraftAtom, currentlyEditingWidgetId, store],
);
return { updateCurrentWidgetConfig };
@@ -15,9 +15,9 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
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 { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useStore } from 'jotai';
import { useRecoilCallback } from 'recoil';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { BarChartLayout } from '~/generated-metadata/graphql';
@@ -32,159 +32,148 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
const currentlyEditingWidgetIdState = useRecoilComponentCallbackState(
const currentlyEditingWidgetIdState = useRecoilComponentStateCallbackStateV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
pageLayoutCurrentLayoutsComponentState,
pageLayoutId,
);
const pageLayoutDraftState = useRecoilComponentCallbackState(
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const store = useStore();
const getConfigToUpdateAfterGraphTypeChange = useRecoilCallback(
({ set, snapshot }) =>
(graphType: GraphType) => {
const currentlyEditingWidgetId = snapshot
.getLoadable(currentlyEditingWidgetIdState)
.getValue();
const getConfigToUpdateAfterGraphTypeChange = useCallback(
(graphType: GraphType) => {
const currentlyEditingWidgetId = store.get(currentlyEditingWidgetIdState);
if (!isDefined(currentlyEditingWidgetId)) {
throw new Error('No widget is currently being edited');
}
if (!isDefined(currentlyEditingWidgetId)) {
throw new Error('No widget is currently being edited');
}
const draftPageLayout = snapshot
.getLoadable(pageLayoutDraftState)
.getValue();
const draftPageLayout = store.get(pageLayoutDraftState);
const widgetInDraft = draftPageLayout.tabs
.flatMap((tab) => tab.widgets)
.find((w) => w.id === currentlyEditingWidgetId);
const widgetInDraft = draftPageLayout.tabs
.flatMap((tab) => tab.widgets)
.find((w) => w.id === currentlyEditingWidgetId);
if (
!isDefined(widgetInDraft) ||
!isDefined(widgetInDraft.configuration)
) {
throw new Error('Widget configuration not found in draft state');
}
if (
!isDefined(widgetInDraft) ||
!isDefined(widgetInDraft.configuration)
) {
throw new Error('Widget configuration not found in draft state');
}
const currentConfiguration = widgetInDraft.configuration;
const currentConfiguration = widgetInDraft.configuration;
if (!isWidgetConfigurationOfTypeGraph(currentConfiguration)) {
throw new Error('Widget configuration is not a chart configuration');
}
if (!isWidgetConfigurationOfTypeGraph(currentConfiguration)) {
throw new Error('Widget configuration is not a chart configuration');
}
const newConfigurationType =
getConfigurationTypeFromGraphType(graphType);
const newConfigurationType = getConfigurationTypeFromGraphType(graphType);
let configToUpdate = {
__typename:
GRAPH_CONFIGURATION_TYPE_TO_CONFIG_TYPENAME[newConfigurationType],
configurationType: newConfigurationType,
} as Partial<ChartConfiguration>;
let configToUpdate = {
__typename:
GRAPH_CONFIGURATION_TYPE_TO_CONFIG_TYPENAME[newConfigurationType],
configurationType: newConfigurationType,
} as Partial<ChartConfiguration>;
if (
graphType !== GraphType.AGGREGATE &&
graphType !== GraphType.GAUGE
) {
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === widget.objectMetadataId,
if (graphType !== GraphType.AGGREGATE && graphType !== GraphType.GAUGE) {
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === widget.objectMetadataId,
);
const convertedAggregateOperation =
convertAggregateOperationForDateField(
currentConfiguration,
objectMetadataItem,
);
const convertedAggregateOperation =
convertAggregateOperationForDateField(
currentConfiguration,
objectMetadataItem,
);
if (isDefined(convertedAggregateOperation)) {
configToUpdate = {
...configToUpdate,
aggregateOperation: convertedAggregateOperation,
};
}
if (isDefined(convertedAggregateOperation)) {
configToUpdate = {
...configToUpdate,
aggregateOperation: convertedAggregateOperation,
};
}
}
const isPieChart = graphType === GraphType.PIE;
const isVerticalBarChart = graphType === GraphType.VERTICAL_BAR;
const isHorizontalBarChart = graphType === GraphType.HORIZONTAL_BAR;
const isLineChart = graphType === GraphType.LINE;
const isBarOrLineChart =
isVerticalBarChart || isHorizontalBarChart || isLineChart;
const isPieChart = graphType === GraphType.PIE;
const isVerticalBarChart = graphType === GraphType.VERTICAL_BAR;
const isHorizontalBarChart = graphType === GraphType.HORIZONTAL_BAR;
const isLineChart = graphType === GraphType.LINE;
const isBarOrLineChart =
isVerticalBarChart || isHorizontalBarChart || isLineChart;
const wasBarOrLineChart =
isWidgetConfigurationOfType(
currentConfiguration,
'BarChartConfiguration',
) ||
isWidgetConfigurationOfType(
currentConfiguration,
'LineChartConfiguration',
);
const wasPieChart = isWidgetConfigurationOfType(
const wasBarOrLineChart =
isWidgetConfigurationOfType(
currentConfiguration,
'PieChartConfiguration',
'BarChartConfiguration',
) ||
isWidgetConfigurationOfType(
currentConfiguration,
'LineChartConfiguration',
);
if (isPieChart && wasBarOrLineChart) {
configToUpdate = {
...configToUpdate,
...convertBarOrLineChartConfigToPieChart(currentConfiguration),
};
}
const wasPieChart = isWidgetConfigurationOfType(
currentConfiguration,
'PieChartConfiguration',
);
if (isBarOrLineChart && wasPieChart) {
configToUpdate = {
...configToUpdate,
...convertPieChartConfigToBarOrLineChart(currentConfiguration),
};
}
if (isPieChart && wasBarOrLineChart) {
configToUpdate = {
...configToUpdate,
...convertBarOrLineChartConfigToPieChart(currentConfiguration),
};
}
if (isHorizontalBarChart) {
configToUpdate = {
...configToUpdate,
layout: BarChartLayout.HORIZONTAL,
};
}
if (isBarOrLineChart && wasPieChart) {
configToUpdate = {
...configToUpdate,
...convertPieChartConfigToBarOrLineChart(currentConfiguration),
};
}
if (isVerticalBarChart) {
configToUpdate = {
...configToUpdate,
layout: BarChartLayout.VERTICAL,
};
}
if (isHorizontalBarChart) {
configToUpdate = {
...configToUpdate,
layout: BarChartLayout.HORIZONTAL,
};
}
const activeTabId = store.get(
activeTabIdComponentState.atomFamily({
instanceId: tabListInstanceId,
}),
);
if (isVerticalBarChart) {
configToUpdate = {
...configToUpdate,
layout: BarChartLayout.VERTICAL,
};
}
if (isDefined(activeTabId) && isDefined(currentlyEditingWidgetId)) {
const currentLayouts = snapshot
.getLoadable(pageLayoutCurrentLayoutsState)
.getValue();
const activeTabId = store.get(
activeTabIdComponentState.atomFamily({
instanceId: tabListInstanceId,
}),
);
const updatedLayouts = updateWidgetMinimumSizeForGraphType({
configurationType: newConfigurationType,
widgetId: currentlyEditingWidgetId,
tabId: activeTabId,
currentLayouts,
});
if (isDefined(activeTabId) && isDefined(currentlyEditingWidgetId)) {
const currentLayouts = store.get(pageLayoutCurrentLayoutsState);
set(pageLayoutCurrentLayoutsState, updatedLayouts);
}
const updatedLayouts = updateWidgetMinimumSizeForGraphType({
configurationType: newConfigurationType,
widgetId: currentlyEditingWidgetId,
tabId: activeTabId,
currentLayouts,
});
return configToUpdate;
},
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
}
return configToUpdate;
},
[
currentlyEditingWidgetIdState,
objectMetadataItems,
@@ -1,14 +1,14 @@
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
export const useWidgetInEditMode = (pageLayoutId: string) => {
const draftPageLayout = useRecoilComponentValue(
const draftPageLayout = useRecoilComponentValueV2(
pageLayoutDraftComponentState,
pageLayoutId,
);
const pageLayoutEditingWidgetId = useRecoilComponentValue(
const pageLayoutEditingWidgetId = useRecoilComponentValueV2(
pageLayoutEditingWidgetIdComponentState,
pageLayoutId,
);
@@ -1,8 +1,8 @@
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const hasInitializedChartFiltersComponentState =
createComponentState<boolean>({
createComponentStateV2<boolean>({
key: 'hasInitializedCurrentRecordFiltersComponentFamilyState',
defaultValue: false,
componentInstanceContext: RecordFiltersComponentInstanceContext,
@@ -8,11 +8,11 @@ import { INFORMATION_BANNER_HEIGHT } from '@/information-banner/constants/Inform
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { PageLayoutRecordPageRenderer } from '@/object-record/record-show/components/PageLayoutRecordPageRenderer';
import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { recordStoreFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreFamilySelectorV2';
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 styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
const StyledRightDrawerRecord = styled.div<{
hasDeletedRecordBanner: boolean;
@@ -26,11 +26,11 @@ const StyledRightDrawerRecord = styled.div<{
`;
export const CommandMenuRecordPage = () => {
const viewableRecordNameSingular = useRecoilComponentValue(
const viewableRecordNameSingular = useRecoilComponentValueV2(
viewableRecordNameSingularComponentState,
);
const viewableRecordId = useRecoilComponentValue(
const viewableRecordId = useRecoilComponentValueV2(
viewableRecordIdComponentState,
);
@@ -47,11 +47,9 @@ export const CommandMenuRecordPage = () => {
viewableRecordId,
);
const recordDeletedAt = useRecoilValue(
recordStoreFamilySelector({
recordId: objectRecordId,
fieldName: 'deletedAt',
}),
const recordDeletedAt = useFamilySelectorValueV2(
recordStoreFamilySelectorV2,
{ recordId: objectRecordId, fieldName: 'deletedAt' },
);
const commandMenuPageInstanceId = useComponentInstanceStateContext(
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const viewableRecordIdComponentState = createComponentState<
export const viewableRecordIdComponentState = createComponentStateV2<
string | null
>({
key: 'command-menu/viewable-record-id',
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const viewableRecordNameSingularComponentState = createComponentState<
export const viewableRecordNameSingularComponentState = createComponentStateV2<
string | null
>({
key: 'command-menu/viewable-record-name-singular',
@@ -11,8 +11,8 @@ import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/const
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useTheme } from '@emotion/react';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { IconFunction } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
@@ -35,7 +35,7 @@ export const CommandMenuWorkflowSelectAction = ({
const { t } = useLingui();
const logicFunctions = useRecoilValue(logicFunctionsState);
const logicFunctions = useRecoilValueV2(logicFunctionsState);
const toolFunctions = logicFunctions.filter((fn) => fn.isTool === true);
@@ -1,9 +1,9 @@
import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState';
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';
export const useCommandMenuWorkflowIdOrThrow = () => {
const workflowId = useRecoilComponentValue(
const workflowId = useRecoilComponentValueV2(
commandMenuWorkflowIdComponentState,
);
if (!isDefined(workflowId)) {
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const commandMenuWorkflowIdComponentState = createComponentState<
export const commandMenuWorkflowIdComponentState = createComponentStateV2<
string | undefined
>({
key: 'command-menu/workflow-id',
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const commandMenuWorkflowRunIdComponentState = createComponentState<
export const commandMenuWorkflowRunIdComponentState = createComponentStateV2<
string | undefined
>({
key: 'command-menu/workflow-run-id',
@@ -1,7 +1,7 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const commandMenuWorkflowStepIdComponentState = createComponentState<
export const commandMenuWorkflowStepIdComponentState = createComponentStateV2<
string | undefined
>({
key: 'command-menu/workflow-step-id',
@@ -1,10 +1,9 @@
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const commandMenuWorkflowVersionIdComponentState = createComponentState<
string | undefined
>({
key: 'command-menu/workflow-version-id',
defaultValue: undefined,
componentInstanceContext: CommandMenuPageComponentInstanceContext,
});
export const commandMenuWorkflowVersionIdComponentState =
createComponentStateV2<string | undefined>({
key: 'command-menu/workflow-version-id',
defaultValue: undefined,
componentInstanceContext: CommandMenuPageComponentInstanceContext,
});
@@ -4,8 +4,8 @@ import {
type WorkflowActionSelection,
} from '@/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import {
@@ -18,13 +18,13 @@ import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
import { prepareIfElseStepWithNewBranch } from '@/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { useSetRecoilState } from 'recoil';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
export const CommandMenuWorkflowCreateStepContent = () => {
const { getIcon } = useIcons();
const workflowVisualizerWorkflowId = useRecoilComponentValue(
const workflowVisualizerWorkflowId = useRecoilComponentValueV2(
workflowVisualizerWorkflowIdComponentState,
);
@@ -36,12 +36,12 @@ export const CommandMenuWorkflowCreateStepContent = () => {
const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu();
const { closeRightClickMenu } = useCloseRightClickMenu();
const setCommandMenuNavigationStack = useSetRecoilState(
const setCommandMenuNavigationStack = useSetRecoilStateV2(
commandMenuNavigationStackState,
);
const [workflowInsertStepIds, setWorkflowInsertStepIds] =
useRecoilComponentState(workflowInsertStepIdsComponentState);
useRecoilComponentStateV2(workflowInsertStepIdsComponentState);
const handleIfElseParentStep = async ({
parentStep,
@@ -1,4 +1,4 @@
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 { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
@@ -15,7 +15,7 @@ const StyledContainer = styled.div`
export const CommandMenuWorkflowEditStepContent = () => {
const flow = useFlowOrThrow();
const workflowSelectedNode = useRecoilComponentValue(
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
@@ -4,7 +4,7 @@ import {
type WorkflowActionSelection,
} from '@/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
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 { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import {
@@ -15,16 +15,16 @@ import { useCloseRightClickMenu } from '@/workflow/workflow-diagram/hooks/useClo
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { useSetRecoilState } from 'recoil';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
export const CommandMenuWorkflowEditStepTypeContent = () => {
const { getIcon } = useIcons();
const workflowSelectedNode = useRecoilComponentValue(
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
const workflowVisualizerWorkflowId = useRecoilComponentValue(
const workflowVisualizerWorkflowId = useRecoilComponentValueV2(
workflowVisualizerWorkflowIdComponentState,
);
const flow = useFlowOrThrow();
@@ -33,7 +33,7 @@ export const CommandMenuWorkflowEditStepTypeContent = () => {
const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu();
const { closeRightClickMenu } = useCloseRightClickMenu();
const setCommandMenuNavigationStack = useSetRecoilState(
const setCommandMenuNavigationStack = useSetRecoilStateV2(
commandMenuNavigationStackState,
);
@@ -7,7 +7,6 @@ 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 { 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';
@@ -44,7 +43,7 @@ type TabId = WorkflowRunTabIdType;
export const CommandMenuWorkflowRunViewStepContent = () => {
const flow = useFlowOrThrow();
const workflowSelectedNode = useRecoilComponentValue(
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
const workflowRunId = useWorkflowRunIdOrThrow();
@@ -1,9 +1,9 @@
import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState';
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';
export const useCommandMenuWorkflowRunIdOrThrow = () => {
const workflowRunId = useRecoilComponentValue(
const workflowRunId = useRecoilComponentValueV2(
commandMenuWorkflowRunIdComponentState,
);
if (!isDefined(workflowRunId)) {
@@ -1,8 +1,8 @@
import { CommandMenuWorkflowRunStepContentComponentInstanceContext } from '@/command-menu/pages/workflow/step/view-run/states/contexts/CommandMenuWorkflowRunStepContentComponentInstanceContext';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
export const workflowRunIteratorSubStepIterationIndexComponentState =
createComponentState<number>({
createComponentStateV2<number>({
key: 'workflowRunIteratorSubStepIterationIndexComponentState',
defaultValue: 0,
componentInstanceContext:
@@ -1,4 +1,4 @@
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 { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
@@ -13,7 +13,7 @@ const StyledContainer = styled.div`
export const CommandMenuWorkflowViewStepContent = () => {
const flow = useFlowOrThrow();
const workflowSelectedNode = useRecoilComponentValue(
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
@@ -1,9 +1,9 @@
import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState';
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';
export const useCommandMenuWorkflowVersionIdOrThrow = () => {
const workflowVersionId = useRecoilComponentValue(
const workflowVersionId = useRecoilComponentValueV2(
commandMenuWorkflowVersionIdComponentState,
);
if (!isDefined(workflowVersionId)) {
@@ -1,7 +1,7 @@
import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
import { useCommandMenuWorkflowIdOrThrow } from '@/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
import {
type WorkflowTrigger,
@@ -29,7 +29,7 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = () => {
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const setWorkflowSelectedNode = useSetRecoilComponentState(
const setWorkflowSelectedNode = useSetRecoilComponentStateV2(
workflowSelectedNodeComponentState,
);
const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu();
@@ -1,7 +1,7 @@
import { type MorphItem } from '@/object-record/multiple-objects/types/MorphItem';
import { createState } from '@/ui/utilities/state/utils/createState';
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
export const commandMenuNavigationMorphItemsByPageState = createState<
export const commandMenuNavigationMorphItemsByPageState = createStateV2<
Map<string, MorphItem[]>
>({
key: 'command-menu/commandMenuNavigationMorphItemsByPageState',
@@ -1,4 +1,4 @@
import { createState } from '@/ui/utilities/state/utils/createState';
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
import { type CommandMenuPages } from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
@@ -10,7 +10,7 @@ export type CommandMenuNavigationStackItem = {
pageId: string;
};
export const commandMenuNavigationStackState = createState<
export const commandMenuNavigationStackState = createStateV2<
CommandMenuNavigationStackItem[]
>({
key: 'command-menu/commandMenuNavigationStackState',
@@ -1,7 +1,7 @@
import { type IconComponent } from 'twenty-ui/display';
import { createState } from '@/ui/utilities/state/utils/createState';
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
export const commandMenuPageInfoState = createState<{
export const commandMenuPageInfoState = createStateV2<{
title?: string;
Icon?: IconComponent;
instanceId: string;
@@ -1,7 +1,7 @@
import { createState } from '@/ui/utilities/state/utils/createState';
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
import { CommandMenuPages } from 'twenty-shared/types';
export const commandMenuPageState = createState<CommandMenuPages>({
export const commandMenuPageState = createStateV2<CommandMenuPages>({
key: 'command-menu/commandMenuPageState',
defaultValue: CommandMenuPages.Root,
});
@@ -1,5 +1,5 @@
import { createState } from '@/ui/utilities/state/utils/createState';
export const commandMenuSearchState = createState<string>({
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
export const commandMenuSearchState = createStateV2<string>({
key: 'command-menu/commandMenuSearchState',
defaultValue: '',
});
@@ -1,9 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
import { CommandMenuPageComponentInstanceContext } from './contexts/CommandMenuPageComponentInstanceContext';
export const commandMenuShouldFocusTitleInputComponentState =
createComponentState<boolean>({
createComponentStateV2<boolean>({
key: 'commandMenuShouldFocusTitleInputComponentState',
defaultValue: false,
componentInstanceContext: CommandMenuPageComponentInstanceContext,
@@ -1,12 +1,10 @@
import { atom } from 'recoil';
import { COMMAND_MENU_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/CommandMenuConstraints';
import { localStorageEffect } from '~/utils/recoil/localStorageEffect';
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
export const COMMAND_MENU_WIDTH_VAR = '--command-menu-width';
export const commandMenuWidthState = atom<number>({
export const commandMenuWidthState = createStateV2<number>({
key: 'commandMenuWidth',
default: COMMAND_MENU_CONSTRAINTS.default,
effects: [localStorageEffect()],
defaultValue: COMMAND_MENU_CONSTRAINTS.default,
useLocalStorage: true,
});
@@ -1,5 +1,5 @@
import { createState } from '@/ui/utilities/state/utils/createState';
export const hasUserSelectedCommandState = createState({
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
export const hasUserSelectedCommandState = createStateV2({
key: 'hasUserSelectedCommandState',
defaultValue: false,
});

Some files were not shown because too many files have changed in this diff Show More