Jotai 13 (#18178)
## Recoil to Jotai Migration — PR 13: Workflow, Page Layout, AI, Activities, Settings, SSE & Remaining Modules ### Summary This is the 13th PR in the [14-PR migration series](packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md) to replace Recoil with Jotai as the state management library in `twenty-front`. This PR migrates the remaining consumer code across all modules — updating ~1,076 files with ~10k insertions/deletions. ### What changed **New Jotai infrastructure (13 new files):** - `createComponentSelectorV2` and `createComponentFamilySelectorV2` — instance-scoped derived atoms, replacing Recoil's component selectors - 6 new hooks: `useRecoilComponentSelectorValueV2`, `useRecoilComponentFamilySelectorValueV2`, `useRecoilComponentSelectorCallbackStateV2`, `useRecoilComponentFamilySelectorCallbackStateV2`, `useRecoilComponentStateCallbackStateV2`, `useRecoilComponentFamilyStateCallbackStateV2` - New types: `ComponentSelectorV2`, `ComponentFamilySelectorV2`, `SelectorCallbacksV2` - Extended `buildGetHelper` to support the new selector patterns **State definition migrations:** - `createState()` → `createStateV2()` (Jotai `atom()`) - `createFamilyState()` → `createFamilyStateV2()` (Jotai `atom()` with family cache) - Recoil `selector`/`selectorFamily` → Jotai derived `atom()` via V2 utilities **Hook migrations (~2,400 removed, ~1,545 added V2 equivalents):** - `useRecoilValue` → `useRecoilValueV2` - `useRecoilState` → `useRecoilStateV2` - `useSetRecoilState` → `useSetRecoilStateV2` - `useRecoilCallback` → `useCallback` + `jotaiStore.get/set` - `useRecoilComponentValue` → `useRecoilComponentValueV2` - `useSetRecoilComponentState` → `useSetRecoilComponentStateV2` - `useRecoilComponentFamilyValue` → `useRecoilComponentFamilyValueV2` / `useFamilySelectorValueV2` - ~397 direct `import from 'recoil'` removed **Deleted files (9 files):** - `dateLocaleState.ts` — consolidated - `currentAIChatThreadTitleStateV2.ts` — renamed to replace the original - `isCommandMenuOpenedState.ts` — removed - `filesFieldUploadState.ts` — replaced by V2 - `recordStoreFamilySelector.ts`, `recordStoreFieldValueSelector.ts`, `recordStoreRecordsSelector.ts` — replaced by V2 equivalents - `settingsAllRolesSelector.ts` — replaced by `useSettingsAllRoles` hook - `settingsRoleIdsStateV2.ts` — consolidated **Modules migrated:** - Workflow (diagram, steps, variables, filters) - Page Layout (widgets, fields, graph, tabs) - AI (chat, agents, browsing context) - Activities (rich text editor, targets, timeline) - Action Menu (single/multiple record actions) - Command Menu (navigation, history, pages, workflow) - Context Store - Record Board, Record Table, Record Calendar, Record Index - Record Field, Record Filter, Record Sort, Record Group - Record Drag, Record Picker, Record Store - Views (view bar, view picker, filters, sorts) - Settings (roles, permissions, SSO, security, data model, developers, domains) - SSE (event streams, subscriptions) - Spreadsheet Import - Auth, Favorites, Front Components, Information Banner
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import { type Locale } from 'date-fns';
|
||||
import { enUS } from 'date-fns/locale';
|
||||
import { type APP_LOCALES } from 'twenty-shared/translations';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
|
||||
type DateLocaleState = {
|
||||
locale?: keyof typeof APP_LOCALES;
|
||||
localeCatalog: Locale;
|
||||
};
|
||||
|
||||
export const dateLocaleState = createState<DateLocaleState>({
|
||||
key: 'dateLocaleState',
|
||||
defaultValue: {
|
||||
locale: undefined,
|
||||
localeCatalog: enUS,
|
||||
},
|
||||
});
|
||||
+2
-2
@@ -2,10 +2,10 @@ import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDi
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { type CommandMenuPages } from 'twenty-shared/types';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
|
||||
@@ -26,7 +26,7 @@ export const ActionOpenSidePanelPage = ({
|
||||
|
||||
const { navigateCommandMenu } = useNavigateCommandMenu();
|
||||
|
||||
const setCommandMenuSearchState = useSetRecoilState(commandMenuSearchState);
|
||||
const setCommandMenuSearchState = useSetRecoilStateV2(commandMenuSearchState);
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
|
||||
+4
-3
@@ -1,8 +1,8 @@
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
import { useCloseActionMenu } from '@/action-menu/hooks/useCloseActionMenu';
|
||||
import { isHeadlessFrontComponentMountedFamilySelector } from '@/front-components/selectors/isHeadlessFrontComponentMountedFamilySelector';
|
||||
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { ActionDisplay } from './ActionDisplay';
|
||||
|
||||
@@ -20,8 +20,9 @@ export const HeadlessFrontComponentAction = ({
|
||||
closeSidePanelOnCommandMenuListActionExecution: false,
|
||||
});
|
||||
|
||||
const isMounted = useRecoilValue(
|
||||
isHeadlessFrontComponentMountedFamilySelector(frontComponentId),
|
||||
const isMounted = useFamilySelectorValueV2(
|
||||
isHeadlessFrontComponentMountedFamilySelector,
|
||||
frontComponentId,
|
||||
);
|
||||
|
||||
if (!actionConfig) {
|
||||
|
||||
+11
-6
@@ -14,7 +14,7 @@ import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -22,8 +22,9 @@ export const DeleteMultipleRecordsAction = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
@@ -32,20 +33,24 @@ export const DeleteMultipleRecordsAction = () => {
|
||||
|
||||
const { resetTableRowSelection } = useResetTableRowSelection(recordIndexId);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValue(
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilterGroups = useRecoilComponentValue(
|
||||
const contextStoreFilterGroups = useRecoilComponentValueV2(
|
||||
contextStoreFilterGroupsComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const { removeSelectedRecordsFromRecordBoard } =
|
||||
|
||||
+11
-6
@@ -14,7 +14,7 @@ import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
@@ -24,8 +24,9 @@ export const DestroyMultipleRecordsAction = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
@@ -36,20 +37,24 @@ export const DestroyMultipleRecordsAction = () => {
|
||||
const { removeSelectedRecordsFromRecordBoard } =
|
||||
useRemoveSelectedRecordsFromRecordBoard(recordIndexId);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValue(
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilterGroups = useRecoilComponentValue(
|
||||
const contextStoreFilterGroups = useRecoilComponentValueV2(
|
||||
contextStoreFilterGroupsComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
+2
-2
@@ -7,14 +7,14 @@ import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useRecordIndexExportRecords } from '@/object-record/record-index/export/hooks/useRecordIndexExportRecords';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ExportMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -3,12 +3,12 @@ import { useSelectedRecordIds } from '@/action-menu/actions/record-actions/singl
|
||||
import { useOpenMergeRecordsPageInCommandMenu } from '@/command-menu/hooks/useOpenMergeRecordsPageInCommandMenu';
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const MergeMultipleRecordsAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
|
||||
+11
-6
@@ -12,7 +12,7 @@ import { useRemoveSelectedRecordsFromRecordBoard } from '@/object-record/record-
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
|
||||
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 { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
|
||||
@@ -20,8 +20,9 @@ export const RestoreMultipleRecordsAction = () => {
|
||||
const { recordIndexId, objectMetadataItem } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (!contextStoreCurrentViewId) {
|
||||
@@ -36,20 +37,24 @@ export const RestoreMultipleRecordsAction = () => {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValue(
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilterGroups = useRecoilComponentValue(
|
||||
const contextStoreFilterGroups = useRecoilComponentValueV2(
|
||||
contextStoreFilterGroupsComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
+4
-4
@@ -3,8 +3,8 @@ import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
|
||||
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
|
||||
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
|
||||
@@ -13,7 +13,7 @@ export const CreateNewViewNoSelectionRecord = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
const { openDropdown } = useOpenDropdown();
|
||||
|
||||
const currentViewId = useRecoilComponentValue(
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const CreateNewViewNoSelectionRecord = () => {
|
||||
currentViewId,
|
||||
);
|
||||
|
||||
const setViewPickerReferenceViewId = useSetRecoilComponentState(
|
||||
const setViewPickerReferenceViewId = useSetRecoilComponentStateV2(
|
||||
viewPickerReferenceViewIdComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
+3
-3
@@ -6,13 +6,13 @@ import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRe
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
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 HideDeletedRecordsNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const currentViewId = useRecoilComponentValue(
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ export const HideDeletedRecordsNoSelectionRecordAction = () => {
|
||||
|
||||
const { isRecordFilterAboutSoftDelete } = useCheckIsSoftDeleteFilter();
|
||||
|
||||
const currentRecordFilters = useRecoilComponentValue(
|
||||
const currentRecordFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
+2
-2
@@ -3,12 +3,12 @@ import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const SeeDeletedRecordsNoSelectionRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const currentViewId = useRecoilComponentValue(
|
||||
const currentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
|
||||
+63
-58
@@ -7,14 +7,16 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
|
||||
import { type WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
@@ -28,12 +30,16 @@ export const useRunWorkflowRecordActions = ({
|
||||
}) => {
|
||||
const { getIcon } = useIcons();
|
||||
const { enqueueWarningSnackBar } = useSnackBar();
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const isPageInEditMode = useRecoilComponentValue(
|
||||
const isPageInEditMode = useRecoilComponentValueV2(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
@@ -49,69 +55,68 @@ export const useRunWorkflowRecordActions = ({
|
||||
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const runWorkflowVersionOnSelectedRecords = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (
|
||||
selectedRecordIds: string[],
|
||||
activeWorkflowVersion: Pick<
|
||||
WorkflowVersion,
|
||||
'id' | 'workflowId' | 'trigger'
|
||||
>,
|
||||
) => {
|
||||
if (selectedRecordIds.length > QUERY_MAX_RECORDS) {
|
||||
const selectedCountFormatted =
|
||||
selectedRecordIds.length.toLocaleString();
|
||||
const limitFormatted = QUERY_MAX_RECORDS.toLocaleString();
|
||||
const runWorkflowVersionOnSelectedRecords = useCallback(
|
||||
async (
|
||||
selectedRecordIds: string[],
|
||||
activeWorkflowVersion: Pick<
|
||||
WorkflowVersion,
|
||||
'id' | 'workflowId' | 'trigger'
|
||||
>,
|
||||
) => {
|
||||
if (selectedRecordIds.length > QUERY_MAX_RECORDS) {
|
||||
const selectedCountFormatted =
|
||||
selectedRecordIds.length.toLocaleString();
|
||||
const limitFormatted = QUERY_MAX_RECORDS.toLocaleString();
|
||||
|
||||
enqueueWarningSnackBar({
|
||||
message: t`You selected ${selectedCountFormatted} records but manual triggers can run on at most ${limitFormatted} records at once. Only the first ${limitFormatted} records will be processed.`,
|
||||
options: {
|
||||
dedupeKey: 'workflow-manual-trigger-selection-limit',
|
||||
},
|
||||
});
|
||||
}
|
||||
enqueueWarningSnackBar({
|
||||
message: t`You selected ${selectedCountFormatted} records but manual triggers can run on at most ${limitFormatted} records at once. Only the first ${limitFormatted} records will be processed.`,
|
||||
options: {
|
||||
dedupeKey: 'workflow-manual-trigger-selection-limit',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const limitedSelectedRecordIds = selectedRecordIds.slice(
|
||||
0,
|
||||
QUERY_MAX_RECORDS,
|
||||
);
|
||||
const limitedSelectedRecordIds = selectedRecordIds.slice(
|
||||
0,
|
||||
QUERY_MAX_RECORDS,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(activeWorkflowVersion?.trigger) &&
|
||||
isBulkRecordsManualTrigger(activeWorkflowVersion.trigger)
|
||||
) {
|
||||
const objectNamePlural = objectMetadataItem.namePlural;
|
||||
const selectedRecords = limitedSelectedRecordIds
|
||||
.map((recordId) =>
|
||||
snapshot.getLoadable(recordStoreFamilyState(recordId)).getValue(),
|
||||
)
|
||||
.filter(isDefined);
|
||||
if (
|
||||
isDefined(activeWorkflowVersion?.trigger) &&
|
||||
isBulkRecordsManualTrigger(activeWorkflowVersion.trigger)
|
||||
) {
|
||||
const objectNamePlural = objectMetadataItem.namePlural;
|
||||
const selectedRecords = limitedSelectedRecordIds
|
||||
.map((recordId) =>
|
||||
jotaiStore.get(recordStoreFamilyState.atomFamily(recordId)),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowId: activeWorkflowVersion.workflowId,
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
payload: {
|
||||
[objectNamePlural]: selectedRecords,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
for (const selectedRecordId of limitedSelectedRecordIds) {
|
||||
const selectedRecord = jotaiStore.get(
|
||||
recordStoreFamilyState.atomFamily(selectedRecordId),
|
||||
);
|
||||
|
||||
if (!isDefined(selectedRecord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowId: activeWorkflowVersion.workflowId,
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
payload: {
|
||||
[objectNamePlural]: selectedRecords,
|
||||
},
|
||||
payload: selectedRecord,
|
||||
});
|
||||
} else {
|
||||
for (const selectedRecordId of limitedSelectedRecordIds) {
|
||||
const selectedRecord = snapshot
|
||||
.getLoadable(recordStoreFamilyState(selectedRecordId))
|
||||
.getValue();
|
||||
|
||||
if (!isDefined(selectedRecord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowId: activeWorkflowVersion.workflowId,
|
||||
workflowVersionId: activeWorkflowVersion.id,
|
||||
payload: selectedRecord,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
[runWorkflowVersion, objectMetadataItem, enqueueWarningSnackBar],
|
||||
);
|
||||
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const AddToFavoritesSingleRecordAction = () => {
|
||||
@@ -13,7 +13,10 @@ export const AddToFavoritesSingleRecordAction = () => {
|
||||
|
||||
const { createFavorite } = useCreateFavorite();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const selectedRecord = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(selectedRecord)) {
|
||||
|
||||
+5
-2
@@ -1,13 +1,16 @@
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ExportNoteActionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const selectedRecord = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const filename = `${(selectedRecord?.title || 'Untitled Note').replace(/[<>:"/\\|?*]/g, '-')}`;
|
||||
|
||||
|
||||
+2
-2
@@ -2,14 +2,14 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
|
||||
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { useExportSingleRecord } from '@/object-record/record-show/hooks/useExportSingleRecord';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
import { Action } from '@/action-menu/actions/components/Action';
|
||||
|
||||
export const ExportSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
);
|
||||
|
||||
|
||||
+5
-2
@@ -4,12 +4,15 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
|
||||
export const CancelDashboardSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const selectedRecord = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const pageLayoutId = selectedRecord?.pageLayoutId;
|
||||
|
||||
|
||||
+5
-2
@@ -2,13 +2,16 @@ import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useResetLocationHash } from 'twenty-ui/utilities';
|
||||
|
||||
export const EditDashboardSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const selectedRecord = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const pageLayoutId = selectedRecord?.pageLayoutId;
|
||||
|
||||
|
||||
+5
-2
@@ -4,12 +4,15 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSavePageLayout } from '@/page-layout/hooks/useSavePageLayout';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
|
||||
export const SaveDashboardSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const selectedRecord = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const selectedRecord = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const pageLayoutId = selectedRecord?.pageLayoutId;
|
||||
|
||||
|
||||
+6
-2
@@ -1,9 +1,13 @@
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useSelectedRecordIdOrThrow = () => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
+6
-2
@@ -1,9 +1,13 @@
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useSelectedRecordIds = () => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
+12
-18
@@ -4,7 +4,7 @@ import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUp
|
||||
import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId';
|
||||
import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
|
||||
import { useTidyUpWorkflowVersion } from '@/workflow/workflow-version/hooks/useTidyUpWorkflowVersion';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const TidyUpWorkflowSingleRecordAction = () => {
|
||||
@@ -16,26 +16,20 @@ export const TidyUpWorkflowSingleRecordAction = () => {
|
||||
const { getUpdatableWorkflowVersion } =
|
||||
useGetUpdatableWorkflowVersionOrThrow(instanceId);
|
||||
|
||||
const onClick = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const workflowDiagramState = workflowDiagramComponentState.atomFamily({
|
||||
instanceId,
|
||||
});
|
||||
const workflowDiagram = snapshot
|
||||
.getLoadable(workflowDiagramState)
|
||||
.getValue();
|
||||
const onClick = async () => {
|
||||
const workflowDiagramAtom = workflowDiagramComponentState.atomFamily({
|
||||
instanceId,
|
||||
});
|
||||
const workflowDiagram = jotaiStore.get(workflowDiagramAtom);
|
||||
|
||||
if (!isDefined(workflowDiagram)) {
|
||||
return;
|
||||
}
|
||||
if (!isDefined(workflowDiagram)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workflowVersionId = await getUpdatableWorkflowVersion();
|
||||
const workflowVersionId = await getUpdatableWorkflowVersion();
|
||||
|
||||
await tidyUpWorkflowVersion(workflowVersionId, workflowDiagram);
|
||||
},
|
||||
[getUpdatableWorkflowVersion, tidyUpWorkflowVersion, instanceId],
|
||||
);
|
||||
await tidyUpWorkflowVersion(workflowVersionId, workflowDiagram);
|
||||
};
|
||||
|
||||
return <Action onClick={onClick} />;
|
||||
};
|
||||
|
||||
+2
-2
@@ -2,13 +2,13 @@ import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SeeVersionWorkflowRunSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowRun = useFamilyRecoilValueV2(recordStoreFamilyState, recordId);
|
||||
|
||||
if (!isDefined(workflowRun) || !isDefined(workflowRun?.workflowVersion?.id)) {
|
||||
return null;
|
||||
|
||||
+2
-2
@@ -2,13 +2,13 @@ import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SeeWorkflowWorkflowRunSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowRun = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowRun = useFamilyRecoilValueV2(recordStoreFamilyState, recordId);
|
||||
|
||||
if (!isDefined(workflowRun) || !isDefined(workflowRun?.workflow?.id)) {
|
||||
return null;
|
||||
|
||||
+11
-6
@@ -9,26 +9,31 @@ import { DEFAULT_QUERY_PAGE_SIZE } from '@/object-record/constants/DefaultQueryP
|
||||
import { useLazyFetchAllRecords } from '@/object-record/hooks/useLazyFetchAllRecords';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useStopWorkflowRun } from '@/workflow/hooks/useStopWorkflowRun';
|
||||
|
||||
export const StopWorkflowRunSingleRecordAction = () => {
|
||||
const { objectMetadataItem } = useRecordIndexIdFromCurrentContextStore();
|
||||
const { objectMetadataItem, recordIndexId } =
|
||||
useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilters = useRecoilComponentValue(
|
||||
const contextStoreFilters = useRecoilComponentValueV2(
|
||||
contextStoreFiltersComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreFilterGroups = useRecoilComponentValue(
|
||||
const contextStoreFilterGroups = useRecoilComponentValueV2(
|
||||
contextStoreFilterGroupsComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
|
||||
const contextStoreAnyFieldFilterValue = useRecoilComponentValueV2(
|
||||
contextStoreAnyFieldFilterValueComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
+5
-2
@@ -2,8 +2,8 @@ import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { AppPath, ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -40,7 +40,10 @@ const SeeRunsWorkflowVersionSingleRecordActionContent = ({
|
||||
|
||||
export const SeeRunsWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowVersion = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const workflowId = workflowVersion?.workflow?.id;
|
||||
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions
|
||||
import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { AppPath, ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -33,7 +33,10 @@ const SeeVersionsWorkflowVersionSingleRecordActionContent = ({
|
||||
|
||||
export const SeeVersionsWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowVersion = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
if (!isDefined(workflowVersion) || !isDefined(workflowVersion.workflowId)) {
|
||||
return null;
|
||||
|
||||
+5
-2
@@ -2,12 +2,15 @@ import { ActionLink } from '@/action-menu/actions/components/ActionLink';
|
||||
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
export const SeeWorkflowWorkflowVersionSingleRecordAction = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
const workflowVersion = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const workflowVersion = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return (
|
||||
<ActionLink
|
||||
|
||||
+4
-2
@@ -2,8 +2,9 @@ import { Action } from '@/action-menu/actions/components/Action';
|
||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||
import { ActionType } from '@/action-menu/actions/types/ActionType';
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
@@ -14,8 +15,9 @@ import { useIcons } from 'twenty-ui/display';
|
||||
export const useRunWorkflowRecordAgnosticActions = () => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const isPageInEditMode = useRecoilComponentValue(
|
||||
const isPageInEditMode = useRecoilComponentValueV2(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const forceRegisteredActionsByKeyState = createState<
|
||||
export const forceRegisteredActionsByKeyState = createStateV2<
|
||||
Record<string, boolean | undefined>
|
||||
>({
|
||||
key: 'forceRegisteredActionsByKeyComponentState',
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { PageHeaderActionMenuButtons } from '@/action-menu/components/PageHeaderActionMenuButtons';
|
||||
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
export const RecordIndexActionMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValue(
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
+1
-2
@@ -15,7 +15,6 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
@@ -50,7 +49,7 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
const dropdownId = getActionMenuDropdownIdFromActionMenuId(actionMenuId);
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const actionMenuDropdownPosition = useRecoilComponentValue(
|
||||
const actionMenuDropdownPosition = useRecoilComponentValueV2(
|
||||
recordIndexActionMenuDropdownPositionComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { PageHeaderActionMenuButtons } from '@/action-menu/components/PageHeaderActionMenuButtons';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
export const RecordShowActionMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValue(
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const hasSelectedRecord =
|
||||
|
||||
+4
-2
@@ -1,11 +1,13 @@
|
||||
import { CommandMenuActionMenuDropdown } from '@/action-menu/components/CommandMenuActionMenuDropdown';
|
||||
import { ActionMenuContextProvider } from '@/action-menu/contexts/ActionMenuContextProvider';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RecordShowRightDrawerActionMenu = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValue(
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
+45
-43
@@ -14,11 +14,13 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconBrowserMaximize } from 'twenty-ui/display';
|
||||
@@ -35,9 +37,10 @@ export const RecordShowRightDrawerOpenRecordButton = ({
|
||||
objectNameSingular,
|
||||
recordId,
|
||||
}: RecordShowRightDrawerOpenRecordButtonProps) => {
|
||||
const record = useRecoilValue<ObjectRecord | null | undefined>(
|
||||
recordStoreFamilyState(recordId),
|
||||
);
|
||||
const record = useFamilyRecoilValueV2(recordStoreFamilyState, recordId) as
|
||||
| ObjectRecord
|
||||
| null
|
||||
| undefined;
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
|
||||
const commandMenuPageComponentInstance = useComponentInstanceStateContext(
|
||||
@@ -63,11 +66,13 @@ export const RecordShowRightDrawerOpenRecordButton = ({
|
||||
tabListComponentIdInRecordPage,
|
||||
);
|
||||
|
||||
const parentViewState = useRecoilComponentCallbackState(
|
||||
const parentViewState = useRecoilComponentStateCallbackStateV2(
|
||||
contextStoreRecordShowParentViewComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const navigate = useNavigateApp();
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -76,48 +81,45 @@ export const RecordShowRightDrawerOpenRecordButton = ({
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const handleOpenRecord = useRecoilCallback(
|
||||
({ snapshot, reset }) =>
|
||||
() => {
|
||||
const tabIdToOpen =
|
||||
activeTabIdInRightDrawer === 'home'
|
||||
? objectNameSingular === CoreObjectNameSingular.Note ||
|
||||
objectNameSingular === CoreObjectNameSingular.Task
|
||||
? 'richText'
|
||||
: 'timeline'
|
||||
: activeTabIdInRightDrawer;
|
||||
const handleOpenRecord = useCallback(() => {
|
||||
const tabIdToOpen =
|
||||
activeTabIdInRightDrawer === 'home'
|
||||
? objectNameSingular === CoreObjectNameSingular.Note ||
|
||||
objectNameSingular === CoreObjectNameSingular.Task
|
||||
? 'richText'
|
||||
: 'timeline'
|
||||
: activeTabIdInRightDrawer;
|
||||
|
||||
setActiveTabIdInRecordPage(tabIdToOpen);
|
||||
setActiveTabIdInRecordPage(tabIdToOpen);
|
||||
|
||||
const parentView = snapshot.getLoadable(parentViewState).getValue();
|
||||
const parentView = store.get(parentViewState);
|
||||
|
||||
if (parentView?.parentViewObjectNameSingular !== objectNameSingular) {
|
||||
reset(parentViewState);
|
||||
}
|
||||
if (parentView?.parentViewObjectNameSingular !== objectNameSingular) {
|
||||
store.set(parentViewState, undefined);
|
||||
}
|
||||
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular,
|
||||
objectRecordId: recordId,
|
||||
});
|
||||
|
||||
closeDropdown(
|
||||
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId),
|
||||
);
|
||||
|
||||
closeCommandMenu();
|
||||
},
|
||||
[
|
||||
actionMenuId,
|
||||
activeTabIdInRightDrawer,
|
||||
closeCommandMenu,
|
||||
closeDropdown,
|
||||
navigate,
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular,
|
||||
parentViewState,
|
||||
recordId,
|
||||
setActiveTabIdInRecordPage,
|
||||
],
|
||||
);
|
||||
objectRecordId: recordId,
|
||||
});
|
||||
|
||||
closeDropdown(
|
||||
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId),
|
||||
);
|
||||
|
||||
closeCommandMenu();
|
||||
}, [
|
||||
actionMenuId,
|
||||
activeTabIdInRightDrawer,
|
||||
closeCommandMenu,
|
||||
closeDropdown,
|
||||
navigate,
|
||||
objectNameSingular,
|
||||
parentViewState,
|
||||
recordId,
|
||||
setActiveTabIdInRecordPage,
|
||||
store,
|
||||
]);
|
||||
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: ['ctrl+Enter,meta+Enter'],
|
||||
|
||||
+37
-37
@@ -1,5 +1,5 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import * as test from 'storybook/test';
|
||||
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
@@ -7,10 +7,14 @@ import { CommandMenuActionMenuDropdown } from '@/action-menu/components/CommandM
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { createMockActionMenuActions } from '@/action-menu/mock/action-menu-actions.mock';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
|
||||
const deleteMock = test.fn();
|
||||
const addToFavoritesMock = test.fn();
|
||||
const exportMock = test.fn();
|
||||
@@ -20,47 +24,43 @@ const meta: Meta<typeof CommandMenuActionMenuDropdown> = {
|
||||
component: CommandMenuActionMenuDropdown,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
{
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID }}
|
||||
>
|
||||
<JestContextStoreSetter
|
||||
contextStoreTargetedRecordsRule={{
|
||||
mode: 'selection',
|
||||
selectedRecordIds: ['1'],
|
||||
},
|
||||
);
|
||||
set(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
1,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInRightDrawer: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'command-menu-show-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
}),
|
||||
}}
|
||||
contextStoreNumberOfSelectedRecords={1}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInRightDrawer: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'command-menu-show-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</JestContextStoreSetter>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</JotaiProvider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
ContextStoreDecorator,
|
||||
ObjectMetadataItemsDecorator,
|
||||
SnackBarDecorator,
|
||||
RouterDecorator,
|
||||
],
|
||||
args: {
|
||||
|
||||
+22
-28
@@ -1,6 +1,5 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import * as test from 'storybook/test';
|
||||
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
||||
|
||||
@@ -30,38 +29,33 @@ const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
|
||||
}),
|
||||
true,
|
||||
);
|
||||
jotaiStore.set(
|
||||
recordIndexActionMenuDropdownPositionComponentState.atomFamily({
|
||||
instanceId: 'action-menu-dropdown-story',
|
||||
}),
|
||||
{ x: 10, y: 10 },
|
||||
);
|
||||
|
||||
return (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
recordIndexActionMenuDropdownPositionComponentState.atomFamily({
|
||||
instanceId: 'action-menu-dropdown-story',
|
||||
}),
|
||||
{ x: 10, y: 10 },
|
||||
);
|
||||
}}
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInRightDrawer: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'index-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<ActionMenuContext.Provider
|
||||
value={{
|
||||
isInRightDrawer: true,
|
||||
displayType: 'dropdownItem',
|
||||
actionMenuType: 'index-page-action-menu-dropdown',
|
||||
actions: createMockActionMenuActions({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
exportMock,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
<Story />
|
||||
</ActionMenuContext.Provider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</JotaiProvider>
|
||||
);
|
||||
},
|
||||
|
||||
+4
-2
@@ -1,11 +1,12 @@
|
||||
import { type ActionMenuContextType } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { ActionMenuContextProviderDefault } from '@/action-menu/contexts/ActionMenuContextProviderDefault';
|
||||
import { ActionMenuContextProviderWorkflowObjects } from '@/action-menu/contexts/ActionMenuContextProviderWorkflowObjects';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
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';
|
||||
|
||||
@@ -19,8 +20,9 @@ export const ActionMenuContextProvider = ({
|
||||
children: React.ReactNode;
|
||||
objectMetadataItemOverride?: ObjectMetadataItem;
|
||||
}) => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValue(
|
||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
|
||||
+4
-2
@@ -7,9 +7,10 @@ import {
|
||||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const ActionMenuContextProviderDefault = ({
|
||||
objectMetadataItem,
|
||||
@@ -34,8 +35,9 @@ export const ActionMenuContextProviderDefault = ({
|
||||
|
||||
const actions = useRegisteredActions(shouldBeRegisteredParams);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const isRecordSelection =
|
||||
|
||||
+6
-4
@@ -6,13 +6,14 @@ import {
|
||||
import { useRegisteredActions } from '@/action-menu/hooks/useRegisteredActions';
|
||||
import { useShouldActionBeRegisteredParams } from '@/action-menu/hooks/useShouldActionBeRegisteredParams';
|
||||
import { useCommandMenuItemFrontComponentActions } from '@/command-menu-item/hooks/useCommandMenuItemFrontComponentActions';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ActionMenuContextProviderWorkflowObjectsProps = {
|
||||
@@ -122,8 +123,9 @@ export const ActionMenuContextProviderWorkflowObjects = ({
|
||||
actionMenuType,
|
||||
children,
|
||||
}: ActionMenuContextProviderWorkflowObjectsProps) => {
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const recordId =
|
||||
@@ -133,7 +135,7 @@ export const ActionMenuContextProviderWorkflowObjects = ({
|
||||
: undefined;
|
||||
|
||||
const selectedRecord =
|
||||
useRecoilValue(recordStoreFamilyState(recordId ?? '')) || undefined;
|
||||
useFamilyRecoilValueV2(recordStoreFamilyState, recordId ?? '') || undefined;
|
||||
|
||||
if (isDefined(selectedRecord?.id)) {
|
||||
return (
|
||||
|
||||
@@ -4,11 +4,13 @@ import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/types/ShouldBeRegisteredFunctionParams';
|
||||
import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
|
||||
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
@@ -20,16 +22,21 @@ export const useRegisteredActions = (
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewType = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const isFullTabWidgetInEditMode = useRecoilComponentValue(
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const isFullTabWidgetInEditMode = useRecoilComponentValueV2(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
|
||||
+21
-10
@@ -4,6 +4,7 @@ import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectPermissionsFamilySelector } from '@/auth/states/objectPermissionsFamilySelector';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
@@ -12,15 +13,17 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { usePrefetchedNavigationMenuItemsData } from '@/navigation-menu-item/hooks/usePrefetchedNavigationMenuItemsData';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useCallback, useContext, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -35,8 +38,9 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValue(
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const recordId =
|
||||
@@ -71,7 +75,7 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
]);
|
||||
|
||||
const selectedRecord =
|
||||
useRecoilValue(recordStoreFamilyState(recordId ?? '')) || undefined;
|
||||
useFamilyRecoilValueV2(recordStoreFamilyState, recordId ?? '') || undefined;
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem?.id ?? '',
|
||||
@@ -83,20 +87,27 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
|
||||
const { isInRightDrawer } = useContext(ActionMenuContext);
|
||||
|
||||
const hasAnySoftDeleteFilterOnView = useRecoilComponentValue(
|
||||
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
|
||||
|
||||
const hasAnySoftDeleteFilterOnView = useRecoilComponentSelectorValueV2(
|
||||
hasAnySoftDeleteFilterOnViewComponentSelector,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const isShowPage =
|
||||
useRecoilComponentValue(contextStoreCurrentViewTypeComponentState) ===
|
||||
ContextStoreViewType.ShowPage;
|
||||
useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
) === ContextStoreViewType.ShowPage;
|
||||
|
||||
const numberOfSelectedRecords = useRecoilComponentValue(
|
||||
const numberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewType = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const viewType = getActionViewType(
|
||||
@@ -128,7 +139,7 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
[],
|
||||
);
|
||||
|
||||
const forceRegisteredActionsByKey = useRecoilValue(
|
||||
const forceRegisteredActionsByKey = useRecoilValueV2(
|
||||
forceRegisteredActionsByKeyState,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { type PositionType } from '@/action-menu/types/PositionType';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const recordIndexActionMenuDropdownPositionComponentState =
|
||||
createComponentState<PositionType>({
|
||||
createComponentStateV2<PositionType>({
|
||||
key: 'recordIndexActionMenuDropdownPositionComponentState',
|
||||
defaultValue: {
|
||||
x: null,
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { format, getYear } from 'date-fns';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard';
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from '@/activities/calendar/constants/Calendar';
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
Section,
|
||||
} from 'twenty-ui/layout';
|
||||
import { type TimelineCalendarEventsWithTotal } from '~/generated/graphql';
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
@@ -49,7 +49,7 @@ const StyledTitleContainer = styled.div`
|
||||
export const CalendarEventsCard = () => {
|
||||
const { t } = useLingui();
|
||||
const targetRecord = useTargetRecord();
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
|
||||
|
||||
const [query, queryName] =
|
||||
targetRecord.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
|
||||
+70
-84
@@ -1,10 +1,11 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { useAtom, useStore } from 'jotai';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttachmentFile';
|
||||
import { useUpsertActivity } from '@/activities/hooks/useUpsertActivity';
|
||||
import { canCreateActivityState } from '@/activities/states/canCreateActivityState';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@@ -56,7 +57,10 @@ export const ActivityRichTextEditor = ({
|
||||
activityId,
|
||||
activityObjectNameSingular,
|
||||
}: ActivityRichTextEditorProps) => {
|
||||
const [activityInStore] = useRecoilState(recordStoreFamilyState(activityId));
|
||||
const store = useStore();
|
||||
const [activityInStore] = useAtom(
|
||||
recordStoreFamilyState.atomFamily(activityId),
|
||||
);
|
||||
|
||||
const cache = useApolloCoreClient().cache;
|
||||
const activity = activityInStore as Task | Note | null;
|
||||
@@ -122,7 +126,7 @@ export const ActivityRichTextEditor = ({
|
||||
}
|
||||
}, 300);
|
||||
|
||||
const [canCreateActivity, setCanCreateActivity] = useRecoilState(
|
||||
const [canCreateActivity, setCanCreateActivity] = useRecoilStateV2(
|
||||
canCreateActivityState,
|
||||
);
|
||||
|
||||
@@ -145,47 +149,45 @@ export const ActivityRichTextEditor = ({
|
||||
[canCreateActivity, persistBodyDebounced, setCanCreateActivity],
|
||||
);
|
||||
|
||||
const handleBodyChange = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (newStringifiedBody: string) => {
|
||||
const oldActivity = snapshot
|
||||
.getLoadable(recordStoreFamilyState(activityId))
|
||||
.getValue();
|
||||
const handleBodyChange = useCallback(
|
||||
async (newStringifiedBody: string) => {
|
||||
const oldActivity = store.get(
|
||||
recordStoreFamilyState.atomFamily(activityId),
|
||||
);
|
||||
|
||||
set(recordStoreFamilyState(activityId), (oldActivity) => {
|
||||
return {
|
||||
...oldActivity,
|
||||
id: activityId,
|
||||
bodyV2: {
|
||||
store.set(
|
||||
recordStoreFamilyState.atomFamily(activityId),
|
||||
(prev: typeof oldActivity) => ({
|
||||
...prev,
|
||||
id: activityId,
|
||||
bodyV2: {
|
||||
blocknote: newStringifiedBody,
|
||||
markdown: null,
|
||||
},
|
||||
__typename: 'Activity',
|
||||
}),
|
||||
);
|
||||
|
||||
modifyRecordFromCache({
|
||||
recordId: activityId,
|
||||
fieldModifiers: {
|
||||
bodyV2: () => {
|
||||
return {
|
||||
blocknote: newStringifiedBody,
|
||||
markdown: null,
|
||||
},
|
||||
__typename: 'Activity',
|
||||
};
|
||||
});
|
||||
|
||||
modifyRecordFromCache({
|
||||
recordId: activityId,
|
||||
fieldModifiers: {
|
||||
bodyV2: () => {
|
||||
return {
|
||||
blocknote: newStringifiedBody,
|
||||
markdown: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
cache,
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
},
|
||||
cache,
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
handlePersistBody(newStringifiedBody);
|
||||
handlePersistBody(newStringifiedBody);
|
||||
|
||||
await syncAttachments(
|
||||
newStringifiedBody,
|
||||
oldActivity?.bodyV2.blocknote,
|
||||
);
|
||||
},
|
||||
await syncAttachments(newStringifiedBody, oldActivity?.bodyV2.blocknote);
|
||||
},
|
||||
[
|
||||
store,
|
||||
activityId,
|
||||
cache,
|
||||
objectMetadataItemActivity,
|
||||
@@ -299,59 +301,43 @@ export const ActivityRichTextEditor = ({
|
||||
// but this leaves the door open for unpredicted behavior with click handlers conflicts,
|
||||
// we recently had a bug which was deleting what the user typed and closed the right drawer if he used backspace key.
|
||||
// We could maybe use the types of components in the focus stack.
|
||||
const handleBlockEditorFocus = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
// TODO: Here we want to detect anything that is open to avoid conflicts with the library click event
|
||||
// that is not prevented and propagate to other click handlers in the app.
|
||||
// Because that is how we do in the app, for example with stacked dropdowns, we always close what's open before
|
||||
// letting the click being captured by a button or input that can capture it.
|
||||
const isRecordTitleCellOpen = snapshot
|
||||
.getLoadable(
|
||||
isTitleCellInEditModeComponentState.atomFamily({
|
||||
instanceId: recordTitleCellId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const handleBlockEditorFocus = useCallback(() => {
|
||||
const isRecordTitleCellOpen = store.get(
|
||||
isTitleCellInEditModeComponentState.atomFamily({
|
||||
instanceId: recordTitleCellId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isRecordTitleCellOpen) {
|
||||
editor.domElement?.blur();
|
||||
return;
|
||||
}
|
||||
if (isRecordTitleCellOpen) {
|
||||
editor.domElement?.blur();
|
||||
return;
|
||||
}
|
||||
|
||||
pushFocusItemToFocusStack({
|
||||
component: {
|
||||
instanceId: activityId,
|
||||
type: FocusComponentType.ACTIVITY_RICH_TEXT_EDITOR,
|
||||
},
|
||||
focusId: activityId,
|
||||
globalHotkeysConfig: BLOCK_EDITOR_GLOBAL_HOTKEYS_CONFIG,
|
||||
});
|
||||
pushFocusItemToFocusStack({
|
||||
component: {
|
||||
instanceId: activityId,
|
||||
type: FocusComponentType.ACTIVITY_RICH_TEXT_EDITOR,
|
||||
},
|
||||
[recordTitleCellId, activityId, editor, pushFocusItemToFocusStack],
|
||||
);
|
||||
focusId: activityId,
|
||||
globalHotkeysConfig: BLOCK_EDITOR_GLOBAL_HOTKEYS_CONFIG,
|
||||
});
|
||||
}, [recordTitleCellId, activityId, editor, pushFocusItemToFocusStack, store]);
|
||||
|
||||
const handlerBlockEditorBlur = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const isRecordTitleCellOpen = snapshot
|
||||
.getLoadable(
|
||||
isTitleCellInEditModeComponentState.atomFamily({
|
||||
instanceId: recordTitleCellId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const handlerBlockEditorBlur = useCallback(() => {
|
||||
const isRecordTitleCellOpen = store.get(
|
||||
isTitleCellInEditModeComponentState.atomFamily({
|
||||
instanceId: recordTitleCellId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isRecordTitleCellOpen) {
|
||||
return;
|
||||
}
|
||||
if (isRecordTitleCellOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: activityId,
|
||||
});
|
||||
},
|
||||
[activityId, recordTitleCellId, removeFocusItemFromFocusStackById],
|
||||
);
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: activityId,
|
||||
});
|
||||
}, [activityId, recordTitleCellId, removeFocusItemFromFocusStackById, store]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||
|
||||
type EmailThreadHeaderProps = {
|
||||
@@ -43,7 +43,7 @@ export const EmailThreadHeader = ({
|
||||
subject,
|
||||
lastMessageSentAt,
|
||||
}: EmailThreadHeaderProps) => {
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
|
||||
const lastMessageSentAtFormatted = beautifyPastDateRelativeToNow(
|
||||
lastMessageSentAt,
|
||||
localeCatalog,
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { ParticipantChip } from '@/activities/components/ParticipantChip';
|
||||
import { type EmailThreadMessageParticipant } from '@/activities/emails/types/EmailThreadMessageParticipant';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { AppTooltip, TooltipPosition } from 'twenty-ui/display';
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
|
||||
import {
|
||||
beautifyPastDateRelativeToNow,
|
||||
formatToHumanReadableDate,
|
||||
@@ -31,7 +31,7 @@ export const EmailThreadMessageSender = ({
|
||||
sender,
|
||||
sentAt,
|
||||
}: EmailThreadMessageSenderProps) => {
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
|
||||
const tooltipId = `date-tooltip-${sentAt.replace(/[^a-zA-Z0-9]/g, '-')}`;
|
||||
|
||||
return (
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const emailThreadIdWhenEmailThreadWasClosedState = createState<
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const emailThreadIdWhenEmailThreadWasClosedState = createStateV2<
|
||||
string | null
|
||||
>({
|
||||
key: 'emailThreadIdWhenEmailThreadWasClosedState',
|
||||
|
||||
+5
-3
@@ -3,12 +3,13 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { SnackBarComponentInstanceContext } from '@/ui/feedback/snack-bar-manager/contexts/SnackBarComponentInstanceContext';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
@@ -139,8 +140,9 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setRecordFromStore = useSetRecoilState(
|
||||
recordStoreFamilyState(task.id),
|
||||
const setRecordFromStore = useSetFamilyRecoilStateV2(
|
||||
recordStoreFamilyState,
|
||||
task.id,
|
||||
);
|
||||
|
||||
const { activityTargetObjectRecords } = useActivityTargetObjectRecords(
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import gql from 'graphql-tag';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
@@ -83,7 +83,7 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
const openActivityRightDrawer = useOpenCreateActivityDrawer({
|
||||
activityObjectNameSingular: CoreObjectNameSingular.Note,
|
||||
});
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const viewableRecordId = useRecoilValueV2(viewableRecordIdState);
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
viewableRecordId,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
import { useActivityTargetsForTargetableObjects } from '@/activities/hooks/useActivityTargetsForTargetableObjects';
|
||||
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
@@ -7,7 +10,6 @@ import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { type RecordGqlOperationOrderBy } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -24,15 +26,15 @@ export const useActivities = <T extends Task | Note>({
|
||||
skip?: boolean;
|
||||
limit: number;
|
||||
}) => {
|
||||
const updateActivitiesInStore = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(activityTargets: (TaskTarget | NoteTarget)[]) => {
|
||||
for (const activityTarget of activityTargets) {
|
||||
const activity = activityTarget[objectNameSingular];
|
||||
set(recordStoreFamilyState(activity.id), activity);
|
||||
}
|
||||
},
|
||||
[objectNameSingular],
|
||||
const store = useStore();
|
||||
const updateActivitiesInStore = useCallback(
|
||||
(activityTargets: (TaskTarget | NoteTarget)[]) => {
|
||||
for (const activityTarget of activityTargets) {
|
||||
const activity = activityTarget[objectNameSingular];
|
||||
store.set(recordStoreFamilyState.atomFamily(activity.id), activity);
|
||||
}
|
||||
},
|
||||
[store, objectNameSingular],
|
||||
);
|
||||
|
||||
const {
|
||||
|
||||
+4
-4
@@ -1,5 +1,3 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
import { type NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { type Task } from '@/activities/types/Task';
|
||||
@@ -7,6 +5,7 @@ import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getActivityTargetObjectRecords } from '@/activities/utils/getActivityTargetObjectRecords';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { type Nullable } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -17,8 +16,9 @@ export const useActivityTargetObjectRecords = (
|
||||
) => {
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
|
||||
const activity = useRecoilValue(
|
||||
recordStoreFamilyState(activityRecordId ?? ''),
|
||||
const activity = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
activityRecordId ?? '',
|
||||
) as Note | Task | null;
|
||||
|
||||
if (!isDefined(activity) && !isDefined(activityTargets)) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
|
||||
import { type ActivityForEditor } from '@/activities/types/ActivityForEditor';
|
||||
@@ -15,7 +17,6 @@ import { createOneActivityOperationSignatureFactory } from '@/activities/graphql
|
||||
import { type NoteTarget } from '@/activities/types/NoteTarget';
|
||||
import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
export const useCreateActivityInDB = ({
|
||||
@@ -56,53 +57,52 @@ export const useCreateActivityInDB = ({
|
||||
});
|
||||
|
||||
const cache = useApolloCoreClient().cache;
|
||||
const store = useStore();
|
||||
|
||||
const createActivityInDB = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (activityToCreate: ActivityForEditor) => {
|
||||
const createdActivity = await createOneActivity?.({
|
||||
...activityToCreate,
|
||||
updatedAt: new Date().toISOString(),
|
||||
const createActivityInDB = useCallback(
|
||||
async (activityToCreate: ActivityForEditor) => {
|
||||
const createdActivity = await createOneActivity?.({
|
||||
...activityToCreate,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const activityTargetsToCreate =
|
||||
activityToCreate.noteTargets ?? activityToCreate.taskTargets ?? [];
|
||||
|
||||
if (isNonEmptyArray(activityTargetsToCreate)) {
|
||||
await createManyActivityTargets({
|
||||
recordsToCreate: activityTargetsToCreate,
|
||||
});
|
||||
}
|
||||
|
||||
const activityTargetsToCreate =
|
||||
activityToCreate.noteTargets ?? activityToCreate.taskTargets ?? [];
|
||||
const activityTargetsConnection = getRecordConnectionFromRecords({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
records: activityTargetsToCreate.map((activityTarget) => ({
|
||||
...activityTarget,
|
||||
__typename: capitalize(objectMetadataItemActivityTarget.nameSingular),
|
||||
})),
|
||||
withPageInfo: false,
|
||||
computeReferences: true,
|
||||
isRootLevel: false,
|
||||
});
|
||||
|
||||
if (isNonEmptyArray(activityTargetsToCreate)) {
|
||||
await createManyActivityTargets({
|
||||
recordsToCreate: activityTargetsToCreate,
|
||||
});
|
||||
}
|
||||
modifyRecordFromCache({
|
||||
recordId: createdActivity.id,
|
||||
cache,
|
||||
fieldModifiers: {
|
||||
activityTargets: () => activityTargetsConnection,
|
||||
},
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
const activityTargetsConnection = getRecordConnectionFromRecords({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem: objectMetadataItemActivityTarget,
|
||||
records: activityTargetsToCreate.map((activityTarget) => ({
|
||||
...activityTarget,
|
||||
__typename: capitalize(
|
||||
objectMetadataItemActivityTarget.nameSingular,
|
||||
),
|
||||
})),
|
||||
withPageInfo: false,
|
||||
computeReferences: true,
|
||||
isRootLevel: false,
|
||||
});
|
||||
|
||||
modifyRecordFromCache({
|
||||
recordId: createdActivity.id,
|
||||
cache,
|
||||
fieldModifiers: {
|
||||
activityTargets: () => activityTargetsConnection,
|
||||
},
|
||||
objectMetadataItem: objectMetadataItemActivity,
|
||||
});
|
||||
|
||||
set(recordStoreFamilyState(createdActivity.id), {
|
||||
...createdActivity,
|
||||
activityTargets: activityTargetsToCreate,
|
||||
});
|
||||
},
|
||||
store.set(recordStoreFamilyState.atomFamily(createdActivity.id), {
|
||||
...createdActivity,
|
||||
activityTargets: activityTargetsToCreate,
|
||||
});
|
||||
},
|
||||
[
|
||||
store,
|
||||
cache,
|
||||
createManyActivityTargets,
|
||||
createOneActivity,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { activityTargetableEntityArrayState } from '@/activities/states/activityTargetableEntityArrayState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
@@ -7,6 +5,7 @@ import { viewableRecordNameSingularState } from '@/object-record/record-right-dr
|
||||
import { type WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { type Note } from '@/activities/types/Note';
|
||||
import { type NoteTarget } from '@/activities/types/NoteTarget';
|
||||
@@ -40,15 +39,15 @@ export const useOpenCreateActivityDrawer = ({
|
||||
shouldMatchRootQueryFilter: true,
|
||||
});
|
||||
|
||||
const setActivityTargetableEntityArray = useSetRecoilState(
|
||||
const setActivityTargetableEntityArray = useSetRecoilStateV2(
|
||||
activityTargetableEntityArrayState,
|
||||
);
|
||||
const setViewableRecordId = useSetRecoilState(viewableRecordIdState);
|
||||
const setViewableRecordNameSingular = useSetRecoilState(
|
||||
const setViewableRecordId = useSetRecoilStateV2(viewableRecordIdState);
|
||||
const setViewableRecordNameSingular = useSetRecoilStateV2(
|
||||
viewableRecordNameSingularState,
|
||||
);
|
||||
|
||||
const setIsUpsertingActivityInDB = useSetRecoilState(
|
||||
const setIsUpsertingActivityInDB = useSetRecoilStateV2(
|
||||
isUpsertingActivityInDBState,
|
||||
);
|
||||
|
||||
|
||||
+18
-16
@@ -1,31 +1,33 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
|
||||
import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
export const useReplaceActivityBlockEditorContent = (
|
||||
editor: typeof BLOCK_SCHEMA.BlockNoteEditor,
|
||||
) => {
|
||||
const replaceBlockEditorContent = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(activityId: string) => {
|
||||
if (isDefined(editor)) {
|
||||
const activityInStore = snapshot
|
||||
.getLoadable(recordStoreFamilyState(activityId))
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
const replaceBlockEditorContent = useCallback(
|
||||
(activityId: string) => {
|
||||
if (isDefined(editor)) {
|
||||
const activityInStore = store.get(
|
||||
recordStoreFamilyState.atomFamily(activityId),
|
||||
);
|
||||
|
||||
const content = isNonEmptyString(activityInStore?.bodyV2.blocknote)
|
||||
? JSON.parse(activityInStore?.bodyV2.blocknote)
|
||||
: [{ type: 'paragraph', content: '' }];
|
||||
const content = isNonEmptyString(activityInStore?.bodyV2.blocknote)
|
||||
? JSON.parse(activityInStore?.bodyV2.blocknote)
|
||||
: [{ type: 'paragraph', content: '' }];
|
||||
|
||||
if (!isDeeplyEqual(editor.document, content)) {
|
||||
editor.replaceBlocks(editor.document, content);
|
||||
}
|
||||
if (!isDeeplyEqual(editor.document, content)) {
|
||||
editor.replaceBlocks(editor.document, content);
|
||||
}
|
||||
},
|
||||
[editor],
|
||||
}
|
||||
},
|
||||
[store, editor],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB';
|
||||
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
@@ -30,7 +28,7 @@ export const useUpsertActivity = ({
|
||||
activityObjectNameSingular,
|
||||
});
|
||||
|
||||
const [, setIsUpsertingActivityInDB] = useRecoilState(
|
||||
const [, setIsUpsertingActivityInDB] = useRecoilStateV2(
|
||||
isUpsertingActivityInDBState,
|
||||
);
|
||||
|
||||
|
||||
+66
-67
@@ -9,7 +9,7 @@ import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/
|
||||
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
type OpenActivityTargetCellEditModeProps = {
|
||||
recordPickerInstanceId: string;
|
||||
@@ -24,81 +24,80 @@ export const useOpenActivityTargetCellEditMode = () => {
|
||||
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const openActivityTargetCellEditMode = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({
|
||||
recordPickerInstanceId,
|
||||
activityTargetObjectRecords,
|
||||
}: OpenActivityTargetCellEditModeProps) => {
|
||||
const objectMetadataItems = jotaiStore
|
||||
.get(objectMetadataItemsState.atom)
|
||||
.filter(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.isSearchable &&
|
||||
objectMetadataItem.isActive &&
|
||||
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task &&
|
||||
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note &&
|
||||
objectMetadataItem.nameSingular !==
|
||||
CoreObjectNameSingular.WorkspaceMember,
|
||||
);
|
||||
const openActivityTargetCellEditMode = useCallback(
|
||||
({
|
||||
recordPickerInstanceId,
|
||||
activityTargetObjectRecords,
|
||||
}: OpenActivityTargetCellEditModeProps) => {
|
||||
const objectMetadataItems = jotaiStore
|
||||
.get(objectMetadataItemsState.atom)
|
||||
.filter(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.isSearchable &&
|
||||
objectMetadataItem.isActive &&
|
||||
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Task &&
|
||||
objectMetadataItem.nameSingular !== CoreObjectNameSingular.Note &&
|
||||
objectMetadataItem.nameSingular !==
|
||||
CoreObjectNameSingular.WorkspaceMember,
|
||||
);
|
||||
|
||||
set(
|
||||
multipleRecordPickerPickableMorphItemsComponentState.atomFamily({
|
||||
jotaiStore.set(
|
||||
multipleRecordPickerPickableMorphItemsComponentState.atomFamily({
|
||||
instanceId: recordPickerInstanceId,
|
||||
}),
|
||||
activityTargetObjectRecords.map((activityTargetObjectRecord) => ({
|
||||
recordId: activityTargetObjectRecord.targetObject.id,
|
||||
objectMetadataId:
|
||||
activityTargetObjectRecord.targetObjectMetadataItem.id,
|
||||
isSelected: true,
|
||||
isMatchingSearchFilter: true,
|
||||
})),
|
||||
);
|
||||
|
||||
jotaiStore.set(
|
||||
multipleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
|
||||
{
|
||||
instanceId: recordPickerInstanceId,
|
||||
}),
|
||||
activityTargetObjectRecords.map((activityTargetObjectRecord) => ({
|
||||
},
|
||||
),
|
||||
objectMetadataItems,
|
||||
);
|
||||
|
||||
jotaiStore.set(
|
||||
multipleRecordPickerSearchFilterComponentState.atomFamily({
|
||||
instanceId: recordPickerInstanceId,
|
||||
}),
|
||||
'',
|
||||
);
|
||||
|
||||
openMultipleRecordPicker(recordPickerInstanceId);
|
||||
|
||||
multipleRecordPickerPerformSearch({
|
||||
multipleRecordPickerInstanceId: recordPickerInstanceId,
|
||||
forceSearchFilter: '',
|
||||
forceSearchableObjectMetadataItems: objectMetadataItems,
|
||||
forcePickableMorphItems: activityTargetObjectRecords.map(
|
||||
(activityTargetObjectRecord) => ({
|
||||
recordId: activityTargetObjectRecord.targetObject.id,
|
||||
objectMetadataId:
|
||||
activityTargetObjectRecord.targetObjectMetadataItem.id,
|
||||
isSelected: true,
|
||||
isMatchingSearchFilter: true,
|
||||
})),
|
||||
);
|
||||
|
||||
set(
|
||||
multipleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
|
||||
{
|
||||
instanceId: recordPickerInstanceId,
|
||||
},
|
||||
),
|
||||
objectMetadataItems,
|
||||
);
|
||||
|
||||
set(
|
||||
multipleRecordPickerSearchFilterComponentState.atomFamily({
|
||||
instanceId: recordPickerInstanceId,
|
||||
}),
|
||||
'',
|
||||
);
|
||||
),
|
||||
});
|
||||
|
||||
openMultipleRecordPicker(recordPickerInstanceId);
|
||||
|
||||
multipleRecordPickerPerformSearch({
|
||||
multipleRecordPickerInstanceId: recordPickerInstanceId,
|
||||
forceSearchFilter: '',
|
||||
forceSearchableObjectMetadataItems: objectMetadataItems,
|
||||
forcePickableMorphItems: activityTargetObjectRecords.map(
|
||||
(activityTargetObjectRecord) => ({
|
||||
recordId: activityTargetObjectRecord.targetObject.id,
|
||||
objectMetadataId:
|
||||
activityTargetObjectRecord.targetObjectMetadataItem.id,
|
||||
isSelected: true,
|
||||
isMatchingSearchFilter: true,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: recordPickerInstanceId,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: recordPickerInstanceId,
|
||||
},
|
||||
globalHotkeysConfig: {
|
||||
enableGlobalHotkeysConflictingWithKeyboard: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: recordPickerInstanceId,
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: recordPickerInstanceId,
|
||||
},
|
||||
globalHotkeysConfig: {
|
||||
enableGlobalHotkeysConflictingWithKeyboard: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
multipleRecordPickerPerformSearch,
|
||||
openMultipleRecordPicker,
|
||||
|
||||
+129
-128
@@ -10,12 +10,13 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { searchRecordStoreFamilyState } from '@/object-record/record-picker/multiple-record-picker/states/searchRecordStoreComponentFamilyState';
|
||||
import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { useStore } from 'jotai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
type UpdateActivityTargetFromCellProps = {
|
||||
recordPickerInstanceId: string;
|
||||
@@ -64,148 +65,148 @@ export const useUpdateActivityTargetFromCell = ({
|
||||
? isTaskTargetMigrated
|
||||
: isNoteTargetMigrated;
|
||||
|
||||
const setActivityFromStore = useSetRecoilState(
|
||||
recordStoreFamilyState(activityId),
|
||||
const store = useStore();
|
||||
const setActivityFromStore = useSetFamilyRecoilStateV2(
|
||||
recordStoreFamilyState,
|
||||
activityId,
|
||||
);
|
||||
|
||||
const updateActivityTargetFromCell = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async ({
|
||||
morphItem,
|
||||
activityTargetWithTargetRecords,
|
||||
}: UpdateActivityTargetFromCellProps) => {
|
||||
const targetObjectName =
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? 'task'
|
||||
: 'note';
|
||||
const updateActivityTargetFromCell = useCallback(
|
||||
async ({
|
||||
morphItem,
|
||||
activityTargetWithTargetRecords,
|
||||
}: UpdateActivityTargetFromCellProps) => {
|
||||
const targetObjectName =
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? 'task'
|
||||
: 'note';
|
||||
|
||||
const objectMetadataItems = jotaiStore.get(
|
||||
objectMetadataItemsState.atom,
|
||||
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
|
||||
|
||||
const pickedObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === morphItem.objectMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(pickedObjectMetadataItem)) {
|
||||
throw new Error('Could not find object metadata item');
|
||||
}
|
||||
|
||||
const targetFieldName = getActivityTargetFieldNameForObject({
|
||||
activityObjectNameSingular,
|
||||
targetObjectMetadataId: morphItem.objectMetadataId,
|
||||
objectMetadataItems,
|
||||
isMorphRelation,
|
||||
});
|
||||
|
||||
if (!isDefined(targetFieldName)) {
|
||||
throw new Error(
|
||||
`Could not find field on activity target for object ${pickedObjectMetadataItem.nameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
const pickedObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === morphItem.objectMetadataId,
|
||||
);
|
||||
let activityTargetsAfterUpdate: (TaskTarget | NoteTarget)[] = [];
|
||||
|
||||
if (!isDefined(pickedObjectMetadataItem)) {
|
||||
throw new Error('Could not find object metadata item');
|
||||
}
|
||||
const existingActivityTarget = activityTargetWithTargetRecords.find(
|
||||
(activityTarget) =>
|
||||
activityTarget.targetObject.id === morphItem.recordId,
|
||||
);
|
||||
|
||||
const targetFieldName = getActivityTargetFieldNameForObject({
|
||||
activityObjectNameSingular,
|
||||
targetObjectMetadataId: morphItem.objectMetadataId,
|
||||
objectMetadataItems,
|
||||
isMorphRelation,
|
||||
});
|
||||
if (isDefined(existingActivityTarget)) {
|
||||
activityTargetsAfterUpdate = activityTargetWithTargetRecords
|
||||
.map((activityTarget) => {
|
||||
if (
|
||||
activityTarget.targetObject.id === morphItem.recordId &&
|
||||
!morphItem.isSelected
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!isDefined(targetFieldName)) {
|
||||
throw new Error(
|
||||
`Could not find field on activity target for object ${pickedObjectMetadataItem.nameSingular}`,
|
||||
return activityTarget.activityTarget;
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
if (!morphItem.isSelected) {
|
||||
await deleteOneActivityTarget(
|
||||
existingActivityTarget.activityTarget.id,
|
||||
);
|
||||
}
|
||||
|
||||
let activityTargetsAfterUpdate: (TaskTarget | NoteTarget)[] = [];
|
||||
|
||||
const existingActivityTarget = activityTargetWithTargetRecords.find(
|
||||
(activityTarget) =>
|
||||
activityTarget.targetObject.id === morphItem.recordId,
|
||||
} else {
|
||||
const searchRecord = store.get(
|
||||
searchRecordStoreFamilyState.atomFamily(morphItem.recordId),
|
||||
);
|
||||
|
||||
if (isDefined(existingActivityTarget)) {
|
||||
activityTargetsAfterUpdate = activityTargetWithTargetRecords
|
||||
.map((activityTarget) => {
|
||||
if (
|
||||
activityTarget.targetObject.id === morphItem.recordId &&
|
||||
!morphItem.isSelected
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return activityTarget.activityTarget;
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
if (!morphItem.isSelected) {
|
||||
await deleteOneActivityTarget(
|
||||
existingActivityTarget.activityTarget.id,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const searchRecord = snapshot
|
||||
.getLoadable(searchRecordStoreFamilyState(morphItem.recordId))
|
||||
.getValue();
|
||||
|
||||
if (!isDefined(searchRecord) || !isDefined(searchRecord?.record)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!morphItem.isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetRecord = searchRecord.record;
|
||||
|
||||
const activityTarget: NoteTarget | TaskTarget =
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? {
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
__typename: 'TaskTarget',
|
||||
taskId: activityId,
|
||||
task: {
|
||||
id: activityId,
|
||||
__typename: 'Task',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
[targetFieldName]: targetRecord,
|
||||
}
|
||||
: {
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
__typename: 'NoteTarget',
|
||||
noteId: activityId,
|
||||
note: {
|
||||
id: activityId,
|
||||
__typename: 'Note',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
[targetFieldName]: targetRecord,
|
||||
};
|
||||
|
||||
activityTargetsAfterUpdate = [
|
||||
...activityTargetWithTargetRecords.map((activityTarget) => {
|
||||
return activityTarget.activityTarget;
|
||||
}),
|
||||
activityTarget,
|
||||
];
|
||||
|
||||
const activityTargetInput: Partial<NoteTarget | TaskTarget> = {
|
||||
...activityTarget,
|
||||
[targetObjectName]: undefined,
|
||||
[targetFieldName]: undefined,
|
||||
[`${targetFieldName}Id`]: morphItem.recordId,
|
||||
};
|
||||
|
||||
await createOneActivityTarget(activityTargetInput);
|
||||
if (!isDefined(searchRecord) || !isDefined(searchRecord?.record)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActivityFromStore((currentActivity) => {
|
||||
if (!isDefined(currentActivity)) {
|
||||
return null;
|
||||
}
|
||||
if (!morphItem.isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
...currentActivity,
|
||||
[`${targetObjectName}Targets`]: activityTargetsAfterUpdate,
|
||||
};
|
||||
});
|
||||
},
|
||||
const targetRecord = searchRecord.record;
|
||||
|
||||
const activityTarget: NoteTarget | TaskTarget =
|
||||
activityObjectNameSingular === CoreObjectNameSingular.Task
|
||||
? {
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
__typename: 'TaskTarget',
|
||||
taskId: activityId,
|
||||
task: {
|
||||
id: activityId,
|
||||
__typename: 'Task',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
[targetFieldName]: targetRecord,
|
||||
}
|
||||
: {
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
__typename: 'NoteTarget',
|
||||
noteId: activityId,
|
||||
note: {
|
||||
id: activityId,
|
||||
__typename: 'Note',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
[targetFieldName]: targetRecord,
|
||||
};
|
||||
|
||||
activityTargetsAfterUpdate = [
|
||||
...activityTargetWithTargetRecords.map((activityTarget) => {
|
||||
return activityTarget.activityTarget;
|
||||
}),
|
||||
activityTarget,
|
||||
];
|
||||
|
||||
const activityTargetInput: Partial<NoteTarget | TaskTarget> = {
|
||||
...activityTarget,
|
||||
[targetObjectName]: undefined,
|
||||
[targetFieldName]: undefined,
|
||||
[`${targetFieldName}Id`]: morphItem.recordId,
|
||||
};
|
||||
|
||||
await createOneActivityTarget(activityTargetInput);
|
||||
}
|
||||
|
||||
setActivityFromStore((currentActivity) => {
|
||||
if (!isDefined(currentActivity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...currentActivity,
|
||||
[`${targetObjectName}Targets`]: activityTargetsAfterUpdate,
|
||||
};
|
||||
});
|
||||
},
|
||||
[
|
||||
store,
|
||||
activityId,
|
||||
activityObjectNameSingular,
|
||||
createOneActivityTarget,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState';
|
||||
import { createFamilyStateV2 } from '@/ui/utilities/state/jotai/utils/createFamilyStateV2';
|
||||
|
||||
export const activityBodyFamilyState = createFamilyState<
|
||||
export const activityBodyFamilyState = createFamilyStateV2<
|
||||
string,
|
||||
{ activityId: string }
|
||||
>({
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const activityTargetableEntityArrayState = createState<
|
||||
export const activityTargetableEntityArrayState = createStateV2<
|
||||
ActivityTargetableObject[]
|
||||
>({
|
||||
key: 'activities/targetable-entity-array',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const canCreateActivityState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const canCreateActivityState = createStateV2<boolean>({
|
||||
key: 'canCreateActivityState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isUpsertingActivityInDBState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isUpsertingActivityInDBState = createStateV2<boolean>({
|
||||
key: 'isUpsertingActivityInDBState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
+7
-4
@@ -1,7 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext';
|
||||
|
||||
@@ -14,7 +13,8 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
|
||||
@@ -99,11 +99,14 @@ export const EventRow = ({
|
||||
allowRequestsToTwentyIconsState,
|
||||
);
|
||||
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
|
||||
|
||||
const { recordId } = useContext(TimelineActivityContext);
|
||||
|
||||
const recordFromStore = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const recordFromStore = useFamilyRecoilValueV2(
|
||||
recordStoreFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(
|
||||
event.createdAt,
|
||||
|
||||
+4
-3
@@ -1,9 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const EventFieldDiffValueEffect = ({
|
||||
@@ -17,8 +17,9 @@ export const EventFieldDiffValueEffect = ({
|
||||
mainObjectMetadataItem: ObjectMetadataItem;
|
||||
fieldMetadataItem: FieldMetadataItem;
|
||||
}) => {
|
||||
const setEntity = useSetRecoilState(
|
||||
recordStoreFamilyState(diffArtificialRecordStoreId),
|
||||
const setEntity = useSetFamilyRecoilStateV2(
|
||||
recordStoreFamilyState,
|
||||
diffArtificialRecordStoreId,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { AgentChatFilePreview } from '@/ai/components/internal/AgentChatFilePreview';
|
||||
import { AgentMessageRole } from '@/ai/constants/AgentMessageRole';
|
||||
@@ -7,9 +6,10 @@ import { AgentMessageRole } from '@/ai/constants/AgentMessageRole';
|
||||
import { AIChatAssistantMessageRenderer } from '@/ai/components/AIChatAssistantMessageRenderer';
|
||||
import { AIChatErrorRenderer } from '@/ai/components/AIChatErrorRenderer';
|
||||
import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { type ExtendedUIMessage } from 'twenty-shared/ai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||
|
||||
const StyledMessageBubble = styled.div<{ isUser?: boolean }>`
|
||||
@@ -141,7 +141,7 @@ export const AIChatMessage = ({
|
||||
isLastMessageStreaming: boolean;
|
||||
error?: Error | null;
|
||||
}) => {
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
|
||||
|
||||
const isUser = message.role === AgentMessageRole.USER;
|
||||
const showError =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { currentAIChatThreadTitleStateV2 } from '@/ai/states/currentAIChatThreadTitleStateV2';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
@@ -83,7 +83,7 @@ export const AIChatThreadGroup = ({
|
||||
currentAIChatThreadStateV2,
|
||||
);
|
||||
const setCurrentAIChatThreadTitle = useSetRecoilStateV2(
|
||||
currentAIChatThreadTitleStateV2,
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
const setAgentChatUsage = useSetRecoilStateV2(agentChatUsageStateV2);
|
||||
const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFile
|
||||
import { agentChatUploadedFilesStateV2 } from '@/ai/states/agentChatUploadedFilesStateV2';
|
||||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { currentAIChatThreadTitleStateV2 } from '@/ai/states/currentAIChatThreadTitleStateV2';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
|
||||
import { agentChatInputStateV2 } from '@/ai/states/agentChatInputStateV2';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
@@ -26,7 +26,7 @@ export const useAgentChat = (uiMessages: ExtendedUIMessage[]) => {
|
||||
|
||||
const { getBrowsingContext } = useGetBrowsingContext();
|
||||
const setCurrentAIChatThreadTitle = useSetRecoilStateV2(
|
||||
currentAIChatThreadTitleStateV2,
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
|
||||
const agentChatSelectedFiles = useRecoilValueV2(
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
type AgentChatUsageState,
|
||||
} from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { currentAIChatThreadTitleStateV2 } from '@/ai/states/currentAIChatThreadTitleStateV2';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { isCreatingChatThreadStateV2 } from '@/ai/states/isCreatingChatThreadStateV2';
|
||||
import { mapDBMessagesToUIMessages } from '@/ai/utils/mapDBMessagesToUIMessages';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
@@ -46,7 +46,7 @@ export const useAgentChatData = () => {
|
||||
);
|
||||
const setAgentChatUsage = useSetRecoilStateV2(agentChatUsageStateV2);
|
||||
const setCurrentAIChatThreadTitle = useSetRecoilStateV2(
|
||||
currentAIChatThreadTitleStateV2,
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
const [isCreatingChatThread, setIsCreatingChatThread] = useRecoilStateV2(
|
||||
isCreatingChatThreadStateV2,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type BrowsingContext } from '@/ai/types/BrowsingContext';
|
||||
@@ -11,7 +11,7 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { recordStoreFamilySelectorV2 } from '@/object-record/record-store/states/selectors/recordStoreFamilySelectorV2';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useStore } from 'jotai';
|
||||
@@ -20,139 +20,129 @@ import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreV
|
||||
export const useGetBrowsingContext = () => {
|
||||
const store = useStore();
|
||||
|
||||
const getBrowsingContext = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(): BrowsingContext | null => {
|
||||
const instanceId = MAIN_CONTEXT_STORE_INSTANCE_ID;
|
||||
const getBrowsingContext = useCallback((): BrowsingContext | null => {
|
||||
const instanceId = MAIN_CONTEXT_STORE_INSTANCE_ID;
|
||||
|
||||
const viewType = snapshot
|
||||
.getLoadable(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const viewType = store.get(
|
||||
contextStoreCurrentViewTypeComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const objectMetadataItemId = snapshot
|
||||
.getLoadable(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const objectMetadataItemId = store.get(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
|
||||
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataItemId,
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataItemId,
|
||||
);
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (viewType === ContextStoreViewType.ShowPage) {
|
||||
const targetedRecordsRule = store.get(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
targetedRecordsRule.mode !== 'selection' ||
|
||||
targetedRecordsRule.selectedRecordIds.length !== 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const recordContext: BrowsingContext = {
|
||||
type: 'recordPage',
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
recordId: targetedRecordsRule.selectedRecordIds[0],
|
||||
};
|
||||
|
||||
const pageLayoutId = store.get(
|
||||
recordStoreFamilySelectorV2.selectorFamily({
|
||||
recordId: targetedRecordsRule.selectedRecordIds[0],
|
||||
fieldName: 'pageLayoutId',
|
||||
}),
|
||||
) as string | null | undefined;
|
||||
|
||||
if (isDefined(pageLayoutId)) {
|
||||
const tabListInstanceId =
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...recordContext,
|
||||
pageLayoutId,
|
||||
activeTabId,
|
||||
};
|
||||
}
|
||||
|
||||
if (viewType === ContextStoreViewType.ShowPage) {
|
||||
const targetedRecordsRule = snapshot
|
||||
.getLoadable(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
return recordContext;
|
||||
}
|
||||
|
||||
if (
|
||||
targetedRecordsRule.mode !== 'selection' ||
|
||||
targetedRecordsRule.selectedRecordIds.length !== 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
viewType === ContextStoreViewType.Table ||
|
||||
viewType === ContextStoreViewType.Kanban
|
||||
) {
|
||||
const currentViewId = store.get(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const recordContext: BrowsingContext = {
|
||||
type: 'recordPage',
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
recordId: targetedRecordsRule.selectedRecordIds[0],
|
||||
};
|
||||
|
||||
const pageLayoutId = snapshot
|
||||
.getLoadable(
|
||||
recordStoreFamilySelector<string | null | undefined>({
|
||||
recordId: targetedRecordsRule.selectedRecordIds[0],
|
||||
fieldName: 'pageLayoutId',
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
if (isDefined(pageLayoutId)) {
|
||||
const tabListInstanceId =
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
...recordContext,
|
||||
pageLayoutId,
|
||||
activeTabId,
|
||||
};
|
||||
}
|
||||
|
||||
return recordContext;
|
||||
}
|
||||
|
||||
if (
|
||||
viewType === ContextStoreViewType.Table ||
|
||||
viewType === ContextStoreViewType.Kanban
|
||||
) {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(
|
||||
contextStoreCurrentViewIdComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const currentView = store.get(
|
||||
coreViewFromViewIdFamilySelector.selectorFamily({
|
||||
viewId: currentViewId ?? '',
|
||||
}),
|
||||
);
|
||||
|
||||
if (!currentView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contextStoreFilters = snapshot
|
||||
.getLoadable(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const filterDescriptions = contextStoreFilters.map((filter) => {
|
||||
const fieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === filter.fieldMetadataId,
|
||||
);
|
||||
const fieldLabel = fieldMetadataItem?.label ?? t`Unknown field`;
|
||||
|
||||
return `${fieldLabel} ${filter.operand} "${filter.displayValue}"`;
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'listView',
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewId: currentView.id,
|
||||
viewName: currentView.name,
|
||||
filterDescriptions,
|
||||
};
|
||||
}
|
||||
const currentView = store.get(
|
||||
coreViewFromViewIdFamilySelector.selectorFamily({
|
||||
viewId: currentViewId ?? '',
|
||||
}),
|
||||
);
|
||||
|
||||
if (!currentView) {
|
||||
return null;
|
||||
},
|
||||
[store],
|
||||
);
|
||||
}
|
||||
|
||||
const contextStoreFilters = store.get(
|
||||
contextStoreFiltersComponentState.atomFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const filterDescriptions = contextStoreFilters.map(
|
||||
(filter: {
|
||||
fieldMetadataId: string;
|
||||
operand: string;
|
||||
displayValue: string;
|
||||
}) => {
|
||||
const fieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === filter.fieldMetadataId,
|
||||
);
|
||||
const fieldLabel = fieldMetadataItem?.label ?? t`Unknown field`;
|
||||
|
||||
return `${fieldLabel} ${filter.operand} "${filter.displayValue}"`;
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'listView',
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
viewId: currentView.id,
|
||||
viewName: currentView.name,
|
||||
filterDescriptions,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [store]);
|
||||
|
||||
return { getBrowsingContext };
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { agentChatUsageStateV2 } from '@/ai/states/agentChatUsageStateV2';
|
||||
import { currentAIChatThreadStateV2 } from '@/ai/states/currentAIChatThreadStateV2';
|
||||
import { currentAIChatThreadTitleStateV2 } from '@/ai/states/currentAIChatThreadTitleStateV2';
|
||||
import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
|
||||
import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
@@ -12,7 +12,7 @@ export const useCreateNewAIChatThread = () => {
|
||||
);
|
||||
const setAgentChatUsage = useSetRecoilStateV2(agentChatUsageStateV2);
|
||||
const setCurrentAIChatThreadTitle = useSetRecoilStateV2(
|
||||
currentAIChatThreadTitleStateV2,
|
||||
currentAIChatThreadTitleState,
|
||||
);
|
||||
|
||||
const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
import { type AgentMessage } from '~/generated-metadata/graphql';
|
||||
|
||||
export const AgentChatMessagesComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
|
||||
export const agentChatMessagesComponentState = createComponentState<
|
||||
export const agentChatMessagesComponentState = createComponentStateV2<
|
||||
AgentMessage[]
|
||||
>({
|
||||
key: 'agentChatMessagesComponentState',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const currentAIChatThreadTitleState = atom<string | null>({
|
||||
export const currentAIChatThreadTitleState = createStateV2<string | null>({
|
||||
key: 'ai/currentAIChatThreadTitleState',
|
||||
default: null,
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const currentAIChatThreadTitleStateV2 = createStateV2<string | null>({
|
||||
key: 'ai/currentAIChatThreadTitleStateV2',
|
||||
defaultValue: null,
|
||||
});
|
||||
@@ -7,14 +7,13 @@ import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirec
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
matchPath,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
@@ -35,7 +34,8 @@ import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/u
|
||||
import { PageFocusId } from '@/types/PageFocusId';
|
||||
import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useResetFocusStackToFocusItem';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { AppBasePath, AppPath, CommandMenuPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { AnalyticsType } from '~/generated-metadata/graphql';
|
||||
@@ -65,12 +65,12 @@ export const PageChangeEffect = () => {
|
||||
const objectNamePlural =
|
||||
useParams().objectNamePlural ?? CoreObjectNamePlural.Person;
|
||||
|
||||
const contextStoreCurrentViewId = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const contextStoreCurrentViewType = useRecoilComponentValue(
|
||||
const contextStoreCurrentViewType = useRecoilComponentValueV2(
|
||||
contextStoreCurrentViewTypeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
@@ -98,19 +98,13 @@ export const PageChangeEffect = () => {
|
||||
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
|
||||
const closeCommandMenuUnlessOnEditPage = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const currentPage = snapshot
|
||||
.getLoadable(commandMenuPageState)
|
||||
.getValue();
|
||||
if (currentPage === CommandMenuPages.NavigationMenuItemEdit) {
|
||||
return;
|
||||
}
|
||||
closeCommandMenu();
|
||||
},
|
||||
[closeCommandMenu],
|
||||
);
|
||||
const closeCommandMenuUnlessOnEditPage = useCallback(() => {
|
||||
const currentPage = jotaiStore.get(commandMenuPageState.atom);
|
||||
if (currentPage === CommandMenuPages.NavigationMenuItemEdit) {
|
||||
return;
|
||||
}
|
||||
closeCommandMenu();
|
||||
}, [closeCommandMenu]);
|
||||
|
||||
const { resetFocusStackToFocusItem } = useResetFocusStackToFocusItem();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { hasInitializedFieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/hasInitializedFieldsWidgetGroupsDraftComponentState';
|
||||
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
@@ -11,84 +10,76 @@ import { pageLayoutIsInitializedComponentState } from '@/page-layout/states/page
|
||||
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
|
||||
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
|
||||
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useExecuteTasksOnAnyLocationChange = () => {
|
||||
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
|
||||
|
||||
const resetPageLayoutEditMode = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
() => {
|
||||
const pageLayoutId = snapshot
|
||||
.getLoadable(currentPageLayoutIdState)
|
||||
.getValue();
|
||||
const resetPageLayoutEditMode = useCallback(() => {
|
||||
const pageLayoutId = jotaiStore.get(currentPageLayoutIdState.atom);
|
||||
|
||||
if (isDefined(pageLayoutId)) {
|
||||
const pageLayoutPersisted = snapshot
|
||||
.getLoadable(
|
||||
pageLayoutPersistedComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
if (isDefined(pageLayoutId)) {
|
||||
const pageLayoutPersisted = jotaiStore.get(
|
||||
pageLayoutPersistedComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDefined(pageLayoutPersisted)) {
|
||||
set(
|
||||
pageLayoutDraftComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
{
|
||||
id: pageLayoutPersisted.id,
|
||||
name: pageLayoutPersisted.name,
|
||||
type: pageLayoutPersisted.type,
|
||||
objectMetadataId: pageLayoutPersisted.objectMetadataId,
|
||||
tabs: pageLayoutPersisted.tabs,
|
||||
},
|
||||
);
|
||||
|
||||
const tabLayouts =
|
||||
convertPageLayoutToTabLayouts(pageLayoutPersisted);
|
||||
set(
|
||||
pageLayoutCurrentLayoutsComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
tabLayouts,
|
||||
);
|
||||
}
|
||||
|
||||
set(
|
||||
isPageLayoutInEditModeComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
set(
|
||||
pageLayoutIsInitializedComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
set(
|
||||
hasInitializedFieldsWidgetGroupsDraftComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
set(currentPageLayoutIdState, null);
|
||||
}
|
||||
|
||||
set(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
if (isDefined(pageLayoutPersisted)) {
|
||||
jotaiStore.set(
|
||||
pageLayoutDraftComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
false,
|
||||
{
|
||||
id: pageLayoutPersisted.id,
|
||||
name: pageLayoutPersisted.name,
|
||||
type: pageLayoutPersisted.type,
|
||||
objectMetadataId: pageLayoutPersisted.objectMetadataId,
|
||||
tabs: pageLayoutPersisted.tabs,
|
||||
},
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const tabLayouts = convertPageLayoutToTabLayouts(pageLayoutPersisted);
|
||||
jotaiStore.set(
|
||||
pageLayoutCurrentLayoutsComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
tabLayouts,
|
||||
);
|
||||
}
|
||||
|
||||
jotaiStore.set(
|
||||
isPageLayoutInEditModeComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
jotaiStore.set(
|
||||
pageLayoutIsInitializedComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
jotaiStore.set(
|
||||
hasInitializedFieldsWidgetGroupsDraftComponentState.atomFamily({
|
||||
instanceId: pageLayoutId,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
||||
jotaiStore.set(currentPageLayoutIdState.atom, null);
|
||||
}
|
||||
|
||||
jotaiStore.set(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Be careful to put idempotent tasks here.
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { ApolloError, useApolloClient } from '@apollo/client';
|
||||
import { useCallback } from 'react';
|
||||
import {
|
||||
snapshot_UNSTABLE,
|
||||
useGotoRecoilSnapshot,
|
||||
useRecoilCallback,
|
||||
} from 'recoil';
|
||||
import { snapshot_UNSTABLE, useGotoRecoilSnapshot } from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
@@ -59,7 +55,6 @@ import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { sseClientState } from '@/sse-db-event/states/sseClientState';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { i18n } from '@lingui/core';
|
||||
@@ -123,81 +118,69 @@ export const useAuth = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const clearSession = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const sseClient = getSnapshotValue(snapshot, sseClientState);
|
||||
const clearSession = useCallback(async () => {
|
||||
const sseClient = jotaiStore.get(sseClientState.atom);
|
||||
|
||||
sseClient?.dispose();
|
||||
sseClient?.dispose();
|
||||
|
||||
const emptySnapshot = snapshot_UNSTABLE();
|
||||
const emptySnapshot = snapshot_UNSTABLE();
|
||||
|
||||
const authProvidersValue = jotaiStore.get(
|
||||
workspaceAuthProvidersState.atom,
|
||||
);
|
||||
const domainConfigurationValue = jotaiStore.get(
|
||||
domainConfigurationState.atom,
|
||||
);
|
||||
const workspacePublicDataValue = jotaiStore.get(
|
||||
workspacePublicDataState.atom,
|
||||
);
|
||||
const lastAuthenticatedMethod = jotaiStore.get(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
);
|
||||
const isCaptchaScriptLoadedValue = jotaiStore.get(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
);
|
||||
const authProvidersValue = jotaiStore.get(workspaceAuthProvidersState.atom);
|
||||
const domainConfigurationValue = jotaiStore.get(
|
||||
domainConfigurationState.atom,
|
||||
);
|
||||
const workspacePublicDataValue = jotaiStore.get(
|
||||
workspacePublicDataState.atom,
|
||||
);
|
||||
const lastAuthenticatedMethod = jotaiStore.get(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
);
|
||||
const isCaptchaScriptLoadedValue = jotaiStore.get(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
);
|
||||
|
||||
const initialSnapshot = emptySnapshot.map(() => {
|
||||
return undefined;
|
||||
});
|
||||
const initialSnapshot = emptySnapshot.map(() => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
|
||||
goToRecoilSnapshot(initialSnapshot);
|
||||
goToRecoilSnapshot(initialSnapshot);
|
||||
|
||||
jotaiStore.set(workspaceAuthProvidersState.atom, authProvidersValue);
|
||||
jotaiStore.set(workspacePublicDataState.atom, workspacePublicDataValue);
|
||||
jotaiStore.set(domainConfigurationState.atom, domainConfigurationValue);
|
||||
jotaiStore.set(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
isCaptchaScriptLoadedValue,
|
||||
);
|
||||
jotaiStore.set(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
lastAuthenticatedMethod,
|
||||
);
|
||||
jotaiStore.set(workspaceAuthProvidersState.atom, authProvidersValue);
|
||||
jotaiStore.set(workspacePublicDataState.atom, workspacePublicDataValue);
|
||||
jotaiStore.set(domainConfigurationState.atom, domainConfigurationValue);
|
||||
jotaiStore.set(isCaptchaScriptLoadedState.atom, isCaptchaScriptLoadedValue);
|
||||
jotaiStore.set(lastAuthenticatedMethodState.atom, lastAuthenticatedMethod);
|
||||
|
||||
// Reset user-data Jotai states that were migrated from Recoil
|
||||
// (Recoil snapshot reset no longer handles these since they are Jotai V2)
|
||||
jotaiStore.set(tokenPairState.atom, null);
|
||||
jotaiStore.set(currentUserState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceState.atom, null);
|
||||
jotaiStore.set(currentUserWorkspaceState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceMembersState.atom, []);
|
||||
jotaiStore.set(availableWorkspacesState.atom, {
|
||||
availableWorkspacesForSignIn: [],
|
||||
availableWorkspacesForSignUp: [],
|
||||
});
|
||||
jotaiStore.set(loginTokenState.atom, null);
|
||||
jotaiStore.set(signInUpStepState.atom, SignInUpStep.Init);
|
||||
jotaiStore.set(coreViewsState.atom, []);
|
||||
// Reset user-data Jotai states that were migrated from Recoil
|
||||
// (Recoil snapshot reset no longer handles these since they are Jotai V2)
|
||||
jotaiStore.set(tokenPairState.atom, null);
|
||||
jotaiStore.set(currentUserState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceState.atom, null);
|
||||
jotaiStore.set(currentUserWorkspaceState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, null);
|
||||
jotaiStore.set(currentWorkspaceMembersState.atom, []);
|
||||
jotaiStore.set(availableWorkspacesState.atom, {
|
||||
availableWorkspacesForSignIn: [],
|
||||
availableWorkspacesForSignUp: [],
|
||||
});
|
||||
jotaiStore.set(loginTokenState.atom, null);
|
||||
jotaiStore.set(signInUpStepState.atom, SignInUpStep.Init);
|
||||
jotaiStore.set(coreViewsState.atom, []);
|
||||
|
||||
await client.clearStore();
|
||||
setLastAuthenticateWorkspaceDomain(null);
|
||||
await resetToMockedMetadata();
|
||||
navigate(AppPath.SignInUp);
|
||||
},
|
||||
[
|
||||
goToRecoilSnapshot,
|
||||
client,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
resetToMockedMetadata,
|
||||
navigate,
|
||||
],
|
||||
);
|
||||
await client.clearStore();
|
||||
setLastAuthenticateWorkspaceDomain(null);
|
||||
await resetToMockedMetadata();
|
||||
navigate(AppPath.SignInUp);
|
||||
}, [
|
||||
goToRecoilSnapshot,
|
||||
client,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
resetToMockedMetadata,
|
||||
navigate,
|
||||
]);
|
||||
|
||||
const handleSetAuthTokens = useCallback(
|
||||
(tokens: AuthTokenPair) => {
|
||||
|
||||
+26
-27
@@ -2,8 +2,9 @@ import { type BLOCK_SCHEMA } from '@/blocknote-editor/blocks/Schema';
|
||||
import { isSlashMenuOpenComponentState } from '@/blocknote-editor/states/isSlashMenuOpenComponentState';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export type BlockEditorDropdownFocusEffectProps = {
|
||||
editor: typeof BLOCK_SCHEMA.BlockNoteEditor;
|
||||
@@ -12,7 +13,7 @@ export type BlockEditorDropdownFocusEffectProps = {
|
||||
export const BlockEditorDropdownFocusEffect = ({
|
||||
editor,
|
||||
}: BlockEditorDropdownFocusEffectProps) => {
|
||||
const isSlashMenuOpenState = useRecoilComponentCallbackState(
|
||||
const isSlashMenuOpenState = useRecoilComponentStateCallbackStateV2(
|
||||
isSlashMenuOpenComponentState,
|
||||
);
|
||||
|
||||
@@ -22,41 +23,39 @@ export const BlockEditorDropdownFocusEffect = ({
|
||||
const { goBackToPreviousDropdownFocusId } =
|
||||
useGoBackToPreviousDropdownFocusId();
|
||||
|
||||
const updateCallBack = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(event: any) => {
|
||||
// TODO: This triggers before the onClick event of the slash menu item, so the click outside of the editor dropdown is triggered and everything closes.
|
||||
// This is due to useRecoilCallback being executed before the onClick event of the slash menu item.
|
||||
const eventWantsToOpen = event.show === true;
|
||||
const store = useStore();
|
||||
|
||||
const isAlreadyOpen = snapshot
|
||||
.getLoadable(isSlashMenuOpenState)
|
||||
.getValue();
|
||||
const updateCallBack = useCallback(
|
||||
(event: any) => {
|
||||
const eventWantsToOpen = event.show === true;
|
||||
|
||||
const shouldOpen = eventWantsToOpen && !isAlreadyOpen;
|
||||
const isAlreadyOpen = store.get(isSlashMenuOpenState);
|
||||
|
||||
if (shouldOpen) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious('custom-slash-menu');
|
||||
set(isSlashMenuOpenState, true);
|
||||
return;
|
||||
}
|
||||
const shouldOpen = eventWantsToOpen && !isAlreadyOpen;
|
||||
|
||||
const eventWantsToClose = event.show === false;
|
||||
if (shouldOpen) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious('custom-slash-menu');
|
||||
store.set(isSlashMenuOpenState, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const isAlreadyClosed = !isAlreadyOpen;
|
||||
const eventWantsToClose = event.show === false;
|
||||
|
||||
const shouldClose = eventWantsToClose && !isAlreadyClosed;
|
||||
const isAlreadyClosed = !isAlreadyOpen;
|
||||
|
||||
if (shouldClose) {
|
||||
goBackToPreviousDropdownFocusId();
|
||||
set(isSlashMenuOpenState, false);
|
||||
return;
|
||||
}
|
||||
},
|
||||
const shouldClose = eventWantsToClose && !isAlreadyClosed;
|
||||
|
||||
if (shouldClose) {
|
||||
goBackToPreviousDropdownFocusId();
|
||||
store.set(isSlashMenuOpenState, false);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[
|
||||
isSlashMenuOpenState,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
goBackToPreviousDropdownFocusId,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { BlockEditorComponentInstanceContext } from '@/blocknote-editor/contexts/BlockEditorCompoponeInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/jotai/utils/createComponentStateV2';
|
||||
|
||||
export const isSlashMenuOpenComponentState = createComponentState<boolean>({
|
||||
export const isSlashMenuOpenComponentState = createComponentStateV2<boolean>({
|
||||
key: 'isSlashMenuOpenComponentState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: BlockEditorComponentInstanceContext,
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@ import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useMountHeadlessFrontComponent } from '@/front-components/hooks/useMountHeadlessFrontComponent';
|
||||
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 { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -94,17 +94,17 @@ export const useCommandMenuItemFrontComponentActions = () => {
|
||||
useOpenFrontComponentInCommandMenu();
|
||||
const mountHeadlessFrontComponent = useMountHeadlessFrontComponent();
|
||||
|
||||
const isPageInEditMode = useRecoilComponentValue(
|
||||
const isPageInEditMode = useRecoilComponentValueV2(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const { actionMenuType } = useContext(ActionMenuContext);
|
||||
|
||||
const currentObjectMetadataItemId = useRecoilComponentValue(
|
||||
const currentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const targetedRecordsRule = useRecoilComponentValue(
|
||||
const targetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
+2
-2
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -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
-9
@@ -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,
|
||||
);
|
||||
|
||||
+16
-18
@@ -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)
|
||||
|
||||
+5
-4
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
+2
-3
@@ -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 (
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
|
||||
+2
-2
@@ -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(
|
||||
|
||||
+4
-4
@@ -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,
|
||||
);
|
||||
|
||||
+3
-3
@@ -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',
|
||||
);
|
||||
|
||||
+37
-25
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
+6
-2
@@ -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,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
+29
-24
@@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+32
-75
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user