Improve Jotai work (#18205)

This commit is contained in:
Charles Bochet
2026-02-24 20:38:30 +01:00
committed by GitHub
parent b339c93699
commit eaf9fe27b2
210 changed files with 1714 additions and 1662 deletions
@@ -9,8 +9,8 @@ type DateLocaleState = {
localeCatalog: Locale;
};
export const dateLocaleStateV2 = createStateV2<DateLocaleState>({
key: 'dateLocaleStateV2',
export const dateLocaleState = createStateV2<DateLocaleState>({
key: 'dateLocaleState',
defaultValue: {
locale: undefined,
localeCatalog: enUS,
@@ -7,7 +7,6 @@ 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 { 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';
@@ -20,6 +19,7 @@ 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';
import { useStore } from 'jotai';
export const useRunWorkflowRecordActions = ({
objectMetadataItem,
@@ -28,6 +28,7 @@ export const useRunWorkflowRecordActions = ({
objectMetadataItem: ObjectMetadataItem;
skip?: boolean;
}) => {
const store = useStore();
const { getIcon } = useIcons();
const { enqueueWarningSnackBar } = useSnackBar();
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
@@ -88,7 +89,7 @@ export const useRunWorkflowRecordActions = ({
const objectNamePlural = objectMetadataItem.namePlural;
const selectedRecords = limitedSelectedRecordIds
.map((recordId) =>
jotaiStore.get(recordStoreFamilyState.atomFamily(recordId)),
store.get(recordStoreFamilyState.atomFamily(recordId)),
)
.filter(isDefined);
@@ -101,7 +102,7 @@ export const useRunWorkflowRecordActions = ({
});
} else {
for (const selectedRecordId of limitedSelectedRecordIds) {
const selectedRecord = jotaiStore.get(
const selectedRecord = store.get(
recordStoreFamilyState.atomFamily(selectedRecordId),
);
@@ -117,7 +118,7 @@ export const useRunWorkflowRecordActions = ({
}
}
},
[runWorkflowVersion, objectMetadataItem, enqueueWarningSnackBar],
[runWorkflowVersion, objectMetadataItem, enqueueWarningSnackBar, store],
);
return activeWorkflowVersions
@@ -1,13 +1,9 @@
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
export const useSelectedRecordIdOrThrow = () => {
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
recordIndexId,
);
if (
@@ -1,13 +1,9 @@
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
export const useSelectedRecordIds = () => {
const { recordIndexId } = useRecordIndexIdFromCurrentContextStore();
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
recordIndexId,
);
if (
@@ -4,10 +4,11 @@ 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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const TidyUpWorkflowSingleRecordAction = () => {
const store = useStore();
const recordId = useSelectedRecordIdOrThrow();
const { tidyUpWorkflowVersion } = useTidyUpWorkflowVersion();
const instanceId = getWorkflowVisualizerComponentInstanceId({
@@ -20,7 +21,7 @@ export const TidyUpWorkflowSingleRecordAction = () => {
const workflowDiagramAtom = workflowDiagramComponentState.atomFamily({
instanceId,
});
const workflowDiagram = jotaiStore.get(workflowDiagramAtom);
const workflowDiagram = store.get(workflowDiagramAtom);
if (!isDefined(workflowDiagram)) {
return;
@@ -21,17 +21,18 @@ import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/ho
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 { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { useStore } from 'jotai';
export const useShouldActionBeRegisteredParams = ({
objectMetadataItem,
}: {
objectMetadataItem?: ObjectMetadataItem;
}): ShouldBeRegisteredFunctionParams => {
const store = useStore();
const { sortedFavorites: favorites } = useFavorites();
const { navigationMenuItems } = usePrefetchedNavigationMenuItemsData();
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
@@ -119,24 +120,24 @@ export const useShouldActionBeRegisteredParams = ({
const getObjectReadPermission = useCallback(
(objectMetadataNameSingular: string) => {
return jotaiStore.get(
return store.get(
objectPermissionsFamilySelector.selectorFamily({
objectNameSingular: objectMetadataNameSingular,
}),
).canRead;
},
[],
[store],
);
const getObjectWritePermission = useCallback(
(objectMetadataNameSingular: string) => {
return jotaiStore.get(
return store.get(
objectPermissionsFamilySelector.selectorFamily({
objectNameSingular: objectMetadataNameSingular,
}),
).canUpdate;
},
[],
[store],
);
const forceRegisteredActionsByKey = useRecoilValueV2(
@@ -26,7 +26,7 @@ import {
Section,
} from 'twenty-ui/layout';
import { type TimelineCalendarEventsWithTotal } from '~/generated/graphql';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
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 } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const [query, queryName] =
targetRecord.targetObjectNameSingular === CoreObjectNameSingular.Person
@@ -2,7 +2,7 @@ import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValu
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
type EmailThreadHeaderProps = {
@@ -43,7 +43,7 @@ export const EmailThreadHeader = ({
subject,
lastMessageSentAt,
}: EmailThreadHeaderProps) => {
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const lastMessageSentAtFormatted = beautifyPastDateRelativeToNow(
lastMessageSentAt,
localeCatalog,
@@ -4,7 +4,7 @@ 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 { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import {
beautifyPastDateRelativeToNow,
formatToHumanReadableDate,
@@ -31,7 +31,7 @@ export const EmailThreadMessageSender = ({
sender,
sentAt,
}: EmailThreadMessageSenderProps) => {
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const tooltipId = `date-tooltip-${sentAt.replace(/[^a-zA-Z0-9]/g, '-')}`;
return (
@@ -1,6 +1,5 @@
import { type ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useMultipleRecordPickerOpen } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerOpen';
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
@@ -10,6 +9,7 @@ import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { useCallback } from 'react';
import { useStore } from 'jotai';
type OpenActivityTargetCellEditModeProps = {
recordPickerInstanceId: string;
@@ -18,6 +18,7 @@ type OpenActivityTargetCellEditModeProps = {
// TODO: deprecate this once we are supporting one to many through relations
export const useOpenActivityTargetCellEditMode = () => {
const store = useStore();
const { performSearch: multipleRecordPickerPerformSearch } =
useMultipleRecordPickerPerformSearch();
const { openMultipleRecordPicker } = useMultipleRecordPickerOpen();
@@ -29,7 +30,7 @@ export const useOpenActivityTargetCellEditMode = () => {
recordPickerInstanceId,
activityTargetObjectRecords,
}: OpenActivityTargetCellEditModeProps) => {
const objectMetadataItems = jotaiStore
const objectMetadataItems = store
.get(objectMetadataItemsState.atom)
.filter(
(objectMetadataItem) =>
@@ -41,7 +42,7 @@ export const useOpenActivityTargetCellEditMode = () => {
CoreObjectNameSingular.WorkspaceMember,
);
jotaiStore.set(
store.set(
multipleRecordPickerPickableMorphItemsComponentState.atomFamily({
instanceId: recordPickerInstanceId,
}),
@@ -54,7 +55,7 @@ export const useOpenActivityTargetCellEditMode = () => {
})),
);
jotaiStore.set(
store.set(
multipleRecordPickerSearchableObjectMetadataItemsComponentState.atomFamily(
{
instanceId: recordPickerInstanceId,
@@ -63,7 +64,7 @@ export const useOpenActivityTargetCellEditMode = () => {
objectMetadataItems,
);
jotaiStore.set(
store.set(
multipleRecordPickerSearchFilterComponentState.atomFamily({
instanceId: recordPickerInstanceId,
}),
@@ -102,6 +103,7 @@ export const useOpenActivityTargetCellEditMode = () => {
multipleRecordPickerPerformSearch,
openMultipleRecordPicker,
pushFocusItemToFocusStack,
store,
],
);
@@ -14,7 +14,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
@@ -99,7 +99,7 @@ export const EventRow = ({
allowRequestsToTwentyIconsState,
);
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const { recordId } = useContext(TimelineActivityContext);
@@ -9,7 +9,7 @@ import { LightCopyIconButton } from '@/object-record/record-field/ui/components/
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { type ExtendedUIMessage } from 'twenty-shared/ai';
import { isDefined } from 'twenty-shared/utils';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
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 } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const isUser = message.role === AgentMessageRole.USER;
const showError =
@@ -6,9 +6,9 @@ import { AppRootErrorFallback } from '@/error-handler/components/AppRootErrorFal
import { ExceptionHandlerProvider } from '@/error-handler/components/ExceptionHandlerProvider';
import { SnackBarComponentInstanceContext } from '@/ui/feedback/snack-bar-manager/contexts/SnackBarComponentInstanceContext';
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { i18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { Provider as JotaiProvider } from 'jotai';
import { HelmetProvider } from 'react-helmet-async';
import { RecoilRoot } from 'recoil';
@@ -35,7 +35,6 @@ import { PageFocusId } from '@/types/PageFocusId';
import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useResetFocusStackToFocusItem';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
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';
@@ -43,10 +42,12 @@ import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffect
import { useInitializeQueryParamState } from '~/modules/app/hooks/useInitializeQueryParamState';
import { isMatchingLocation } from '~/utils/isMatchingLocation';
import { getPageTitleFromPath } from '~/utils/title-utils';
import { useStore } from 'jotai';
// TODO: break down into smaller functions and / or hooks
// - moved usePageChangeEffectNavigateLocation into dedicated hook
export const PageChangeEffect = () => {
const store = useStore();
const navigate = useNavigate();
const [previousLocation, setPreviousLocation] = useState('');
@@ -99,12 +100,12 @@ export const PageChangeEffect = () => {
const { closeCommandMenu } = useCommandMenu();
const closeCommandMenuUnlessOnEditPage = useCallback(() => {
const currentPage = jotaiStore.get(commandMenuPageState.atom);
const currentPage = store.get(commandMenuPageState.atom);
if (currentPage === CommandMenuPages.NavigationMenuItemEdit) {
return;
}
closeCommandMenu();
}, [closeCommandMenu]);
}, [closeCommandMenu, store]);
const { resetFocusStackToFocusItem } = useResetFocusStackToFocusItem();
@@ -1,7 +1,6 @@
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';
@@ -12,22 +11,24 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useExecuteTasksOnAnyLocationChange = () => {
const store = useStore();
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
const resetPageLayoutEditMode = useCallback(() => {
const pageLayoutId = jotaiStore.get(currentPageLayoutIdState.atom);
const pageLayoutId = store.get(currentPageLayoutIdState.atom);
if (isDefined(pageLayoutId)) {
const pageLayoutPersisted = jotaiStore.get(
const pageLayoutPersisted = store.get(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (isDefined(pageLayoutPersisted)) {
jotaiStore.set(
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
@@ -41,7 +42,7 @@ export const useExecuteTasksOnAnyLocationChange = () => {
);
const tabLayouts = convertPageLayoutToTabLayouts(pageLayoutPersisted);
jotaiStore.set(
store.set(
pageLayoutCurrentLayoutsComponentState.atomFamily({
instanceId: pageLayoutId,
}),
@@ -49,37 +50,37 @@ export const useExecuteTasksOnAnyLocationChange = () => {
);
}
jotaiStore.set(
store.set(
isPageLayoutInEditModeComponentState.atomFamily({
instanceId: pageLayoutId,
}),
false,
);
jotaiStore.set(
store.set(
pageLayoutIsInitializedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
false,
);
jotaiStore.set(
store.set(
hasInitializedFieldsWidgetGroupsDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
{},
);
jotaiStore.set(currentPageLayoutIdState.atom, null);
store.set(currentPageLayoutIdState.atom, null);
}
jotaiStore.set(
store.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
false,
);
}, []);
}, [store]);
/**
* Be careful to put idempotent tasks here.
@@ -3,16 +3,17 @@ import { useCallback } from 'react';
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
import { type BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import deepEqual from 'deep-equal';
import { useStore } from 'jotai';
// Initialize state that are hydrated from query parameters
// We used to use recoil-sync to do this, but it was causing issues with Firefox
export const useInitializeQueryParamState = () => {
const store = useStore();
const initializeQueryParamState = useCallback(() => {
const handlers = {
billingCheckoutSession: (value: string) => {
const billingCheckoutSession = jotaiStore.get(
const billingCheckoutSession = store.get(
billingCheckoutSessionState.atom,
);
@@ -27,7 +28,7 @@ export const useInitializeQueryParamState = () => {
'requirePaymentMethod' in parsedValue &&
!deepEqual(billingCheckoutSession, parsedValue)
) {
jotaiStore.set(
store.set(
billingCheckoutSessionState.atom,
parsedValue as BillingCheckoutSession,
);
@@ -38,7 +39,7 @@ export const useInitializeQueryParamState = () => {
'Failed to parse billingCheckoutSession from URL',
error,
);
jotaiStore.set(
store.set(
billingCheckoutSessionState.atom,
BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
);
@@ -54,7 +55,7 @@ export const useInitializeQueryParamState = () => {
handler(value);
}
}
}, []);
}, [store]);
return { initializeQueryParamState };
};
@@ -22,7 +22,9 @@ export const VerifyLoginTokenEffect = () => {
);
useEffect(() => {
if (!clientConfigLoaded) return;
if (!clientConfigLoaded) {
return;
}
if (isDefined(loginToken)) {
verifyLoginToken(loginToken);
@@ -21,7 +21,6 @@ import {
import { tokenPairState } from '@/auth/states/tokenPairState';
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 { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
@@ -63,8 +62,10 @@ import { SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
import { cookieStorage } from '~/utils/cookie-storage';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
import { useStore } from 'jotai';
export const useAuth = () => {
const store = useStore();
const setTokenPair = useSetRecoilStateV2(tokenPairState);
const setLoginToken = useSetRecoilStateV2(loginTokenState);
const setIsAppEffectRedirectEnabled = useSetRecoilStateV2(
@@ -119,23 +120,19 @@ export const useAuth = () => {
const navigate = useNavigate();
const clearSession = useCallback(async () => {
const sseClient = jotaiStore.get(sseClientState.atom);
const sseClient = store.get(sseClientState.atom);
sseClient?.dispose();
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(
const authProvidersValue = store.get(workspaceAuthProvidersState.atom);
const domainConfigurationValue = store.get(domainConfigurationState.atom);
const workspacePublicDataValue = store.get(workspacePublicDataState.atom);
const lastAuthenticatedMethod = store.get(
lastAuthenticatedMethodState.atom,
);
const isCaptchaScriptLoadedValue = jotaiStore.get(
const isCaptchaScriptLoadedValue = store.get(
isCaptchaScriptLoadedState.atom,
);
@@ -148,27 +145,27 @@ export const useAuth = () => {
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);
store.set(workspaceAuthProvidersState.atom, authProvidersValue);
store.set(workspacePublicDataState.atom, workspacePublicDataValue);
store.set(domainConfigurationState.atom, domainConfigurationValue);
store.set(isCaptchaScriptLoadedState.atom, isCaptchaScriptLoadedValue);
store.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, {
store.set(tokenPairState.atom, null);
store.set(currentUserState.atom, null);
store.set(currentWorkspaceState.atom, null);
store.set(currentUserWorkspaceState.atom, null);
store.set(currentWorkspaceMemberState.atom, null);
store.set(currentWorkspaceMembersState.atom, []);
store.set(availableWorkspacesState.atom, {
availableWorkspacesForSignIn: [],
availableWorkspacesForSignUp: [],
});
jotaiStore.set(loginTokenState.atom, null);
jotaiStore.set(signInUpStepState.atom, SignInUpStep.Init);
jotaiStore.set(coreViewsState.atom, []);
store.set(loginTokenState.atom, null);
store.set(signInUpStepState.atom, SignInUpStep.Init);
store.set(coreViewsState.atom, []);
await client.clearStore();
setLastAuthenticateWorkspaceDomain(null);
@@ -180,6 +177,7 @@ export const useAuth = () => {
setLastAuthenticateWorkspaceDomain,
resetToMockedMetadata,
navigate,
store,
]);
const handleSetAuthTokens = useCallback(
@@ -1,16 +1,17 @@
import { captchaTokenState } from '@/captcha/states/captchaTokenState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useReadCaptchaToken = () => {
const store = useStore();
const readCaptchaToken = useCallback(() => {
const existingCaptchaToken = jotaiStore.get(captchaTokenState.atom);
const existingCaptchaToken = store.get(captchaTokenState.atom);
if (isDefined(existingCaptchaToken)) {
return existingCaptchaToken;
}
}, []);
}, [store]);
return { readCaptchaToken };
};
@@ -3,12 +3,13 @@ import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCapt
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
import { captchaState } from '@/client-config/states/captchaState';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils';
import { CaptchaDriverType } from '~/generated-metadata/graphql';
import { useStore } from 'jotai';
export const useRequestFreshCaptchaToken = () => {
const store = useStore();
const setCaptchaToken = useSetRecoilStateV2(captchaTokenState);
const setIsRequestingCaptchaToken = useSetRecoilStateV2(
isRequestingCaptchaTokenState,
@@ -19,7 +20,7 @@ export const useRequestFreshCaptchaToken = () => {
return;
}
const captcha = jotaiStore.get(captchaState.atom);
const captcha = store.get(captchaState.atom);
if (!isDefined(captcha)) {
return;
@@ -52,7 +53,7 @@ export const useRequestFreshCaptchaToken = () => {
},
});
}
}, [setCaptchaToken, setIsRequestingCaptchaToken]);
}, [setCaptchaToken, setIsRequestingCaptchaToken, store]);
return { requestFreshCaptchaToken };
};
@@ -33,7 +33,7 @@ import { getClientConfig } from '@/client-config/utils/getClientConfig';
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useStore } from 'jotai';
type UseClientConfigResult = {
data: { clientConfig: ClientConfig } | undefined;
@@ -44,6 +44,7 @@ type UseClientConfigResult = {
};
export const useClientConfig = (): UseClientConfigResult => {
const store = useStore();
const setIsAnalyticsEnabled = useSetRecoilStateV2(isAnalyticsEnabledState);
const setDomainConfiguration = useSetRecoilStateV2(domainConfigurationState);
const setAuthProviders = useSetRecoilStateV2(authProvidersState);
@@ -191,7 +192,7 @@ export const useClientConfig = (): UseClientConfigResult => {
setGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled);
setGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled);
setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled);
jotaiStore.set(
store.set(
isAttachmentPreviewEnabledStateV2.atom,
clientConfig?.isAttachmentPreviewEnabled,
);
@@ -252,6 +253,7 @@ export const useClientConfig = (): UseClientConfigResult => {
setSentryConfig,
setSupportChat,
setAllowRequestsToTwentyIcons,
store,
]);
return {
@@ -22,7 +22,7 @@ import {
FeatureFlagKey,
FieldMetadataType,
} from '~/generated-metadata/graphql';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout';
@@ -67,7 +67,7 @@ export const CommandMenuRecordInfo = ({
},
);
const { localeCatalog } = useRecoilValueV2(dateLocaleStateV2);
const { localeCatalog } = useRecoilValueV2(dateLocaleState);
const beautifiedCreatedAt = isNonEmptyString(recordCreatedAt)
? beautifyPastDateRelativeToNow(recordCreatedAt, localeCatalog)
: '';
@@ -1,4 +1,5 @@
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
@@ -13,14 +14,16 @@ import { CommandMenuPages } from 'twenty-shared/types';
import { IconDotsVertical } from 'twenty-ui/display';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
{children}
</MemoryRouter>
</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
{children}
</MemoryRouter>
</RecoilRoot>
</JotaiProvider>
);
const renderHooks = () => {
@@ -1,4 +1,5 @@
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
@@ -51,9 +52,11 @@ jest.mock('@/ui/layout/right-drawer/utils/emitSidePanelCloseEvent', () => ({
}));
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>
<MemoryRouter>{children}</MemoryRouter>
</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>
<MemoryRouter>{children}</MemoryRouter>
</RecoilRoot>
</JotaiProvider>
);
describe('useCommandMenuCloseAnimationCompleteCleanup', () => {
@@ -1,4 +1,5 @@
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
@@ -15,9 +16,11 @@ import { CommandMenuPages } from 'twenty-shared/types';
import { IconList, IconSearch } from 'twenty-ui/display';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>
<MemoryRouter>{children}</MemoryRouter>
</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>
<MemoryRouter>{children}</MemoryRouter>
</RecoilRoot>
</JotaiProvider>
);
const renderHooks = () => {
@@ -2,6 +2,7 @@ import { useCommandMenuUpdateNavigationMorphItemsByPage } from '@/command-menu/h
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { RecoilRoot } from 'recoil';
@@ -9,7 +10,9 @@ const pageId = 'merge-page-id';
const objectMetadataId = 'company-metadata-id';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>{children}</RecoilRoot>
</JotaiProvider>
);
const renderHooks = (initialRecordIds: string[]) => {
@@ -3,6 +3,7 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { RecoilRoot } from 'recoil';
import { CommandMenuPages } from 'twenty-shared/types';
@@ -24,7 +25,9 @@ const mockedNavigationStack = [
];
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>{children}</RecoilRoot>
</JotaiProvider>
);
describe('useUpdateCommandMenuPageInfo', () => {
@@ -7,13 +7,14 @@ import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/ad
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconDotsVertical } from 'twenty-ui/display';
import { useStore } from 'jotai';
export const useCommandMenu = () => {
const store = useStore();
const { navigateCommandMenu } = useNavigateCommandMenu();
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
@@ -21,18 +22,18 @@ export const useCommandMenu = () => {
useRemoveFocusItemFromFocusStackById();
const closeCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
if (isCommandMenuOpened) {
jotaiStore.set(addToNavPayloadRegistryStateV2.atom, new Map());
jotaiStore.set(isCommandMenuOpenedStateV2.atom, false);
jotaiStore.set(isCommandMenuClosingState.atom, true);
store.set(addToNavPayloadRegistryStateV2.atom, new Map());
store.set(isCommandMenuOpenedStateV2.atom, false);
store.set(isCommandMenuClosingState.atom, true);
closeAnyOpenDropdown();
removeFocusItemFromFocusStackById({
focusId: SIDE_PANEL_FOCUS_ID,
});
}
}, [closeAnyOpenDropdown, removeFocusItemFromFocusStackById]);
}, [closeAnyOpenDropdown, removeFocusItemFromFocusStackById, store]);
const openCommandMenu = useCallback(() => {
emitSidePanelOpenEvent();
@@ -46,16 +47,16 @@ export const useCommandMenu = () => {
}, [closeAnyOpenDropdown, navigateCommandMenu]);
const toggleCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
jotaiStore.set(commandMenuSearchState.atom, '');
store.set(commandMenuSearchState.atom, '');
if (isCommandMenuOpened) {
closeCommandMenu();
} else {
openCommandMenu();
}
}, [closeCommandMenu, openCommandMenu]);
}, [closeCommandMenu, openCommandMenu, store]);
return {
openCommandMenu,
@@ -22,14 +22,15 @@ import { emitSidePanelCloseEvent } from '@/ui/layout/right-drawer/utils/emitSide
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowLogicFunctionTabListComponentId';
import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useCommandMenuCloseAnimationCompleteCleanup = () => {
const store = useStore();
const { resetSelectedItem } = useSelectableList(
COMMAND_MENU_LIST_SELECTABLE_LIST_ID,
);
@@ -44,7 +45,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
resetContextStoreStates(COMMAND_MENU_COMPONENT_INSTANCE_ID);
resetContextStoreStates(COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID);
const currentPage = jotaiStore.get(commandMenuPageState.atom);
const currentPage = store.get(commandMenuPageState.atom);
const isPageLayoutEditingPage =
currentPage === CommandMenuPages.PageLayoutWidgetTypeSelect ||
@@ -53,7 +54,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
currentPage === CommandMenuPages.PageLayoutTabSettings;
if (isPageLayoutEditingPage) {
const targetedRecordsRule = jotaiStore.get(
const targetedRecordsRule = store.get(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
@@ -64,24 +65,22 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
targetedRecordsRule.selectedRecordIds.length === 1
) {
const recordId = targetedRecordsRule.selectedRecordIds[0];
const record = jotaiStore.get(
recordStoreFamilyState.atomFamily(recordId),
);
const record = store.get(recordStoreFamilyState.atomFamily(recordId));
if (isDefined(record) && isDefined(record.pageLayoutId)) {
jotaiStore.set(
store.set(
pageLayoutEditingWidgetIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
jotaiStore.set(
store.set(
pageLayoutTabSettingsOpenTabIdComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
null,
);
jotaiStore.set(
store.set(
pageLayoutDraggedAreaComponentState.atomFamily({
instanceId: record.pageLayoutId,
}),
@@ -91,35 +90,35 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
}
}
jotaiStore.set(viewableRecordIdState.atom, null);
jotaiStore.set(commandMenuPageState.atom, CommandMenuPages.Root);
jotaiStore.set(commandMenuPageInfoState.atom, {
store.set(viewableRecordIdState.atom, null);
store.set(commandMenuPageState.atom, CommandMenuPages.Root);
store.set(commandMenuPageInfoState.atom, {
title: undefined,
Icon: undefined,
instanceId: '',
});
jotaiStore.set(isCommandMenuOpenedStateV2.atom, false);
jotaiStore.set(commandMenuSearchState.atom, '');
jotaiStore.set(commandMenuNavigationMorphItemsByPageState.atom, new Map());
jotaiStore.set(commandMenuNavigationStackState.atom, []);
store.set(isCommandMenuOpenedStateV2.atom, false);
store.set(commandMenuSearchState.atom, '');
store.set(commandMenuNavigationMorphItemsByPageState.atom, new Map());
store.set(commandMenuNavigationStackState.atom, []);
resetSelectedItem();
jotaiStore.set(hasUserSelectedCommandState.atom, false);
store.set(hasUserSelectedCommandState.atom, false);
emitSidePanelCloseEvent();
jotaiStore.set(isCommandMenuClosingState.atom, false);
jotaiStore.set(
store.set(isCommandMenuClosingState.atom, false);
store.set(
activeTabIdComponentState.atomFamily({
instanceId: WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID,
}),
WorkflowLogicFunctionTabId.CODE,
);
const morphItemsByPage = jotaiStore.get(
const morphItemsByPage = store.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
for (const [pageId, morphItems] of morphItemsByPage) {
jotaiStore.set(
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
@@ -129,7 +128,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
null,
);
}
}, [closeDropdown, resetContextStoreStates, resetSelectedItem]);
}, [closeDropdown, resetContextStoreStates, resetSelectedItem, store]);
return {
commandMenuCloseAnimationCompleteCleanup,
@@ -8,15 +8,16 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { isNonEmptyArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useCommandMenuHistory = () => {
const store = useStore();
const { closeCommandMenu } = useCommandMenu();
const goBackFromCommandMenu = useCallback(() => {
const currentNavigationStack = jotaiStore.get(
const currentNavigationStack = store.get(
commandMenuNavigationStackState.atom,
);
@@ -28,17 +29,17 @@ export const useCommandMenuHistory = () => {
return;
}
jotaiStore.set(commandMenuPageState.atom, lastNavigationStackItem.page);
store.set(commandMenuPageState.atom, lastNavigationStackItem.page);
jotaiStore.set(commandMenuPageInfoState.atom, {
store.set(commandMenuPageInfoState.atom, {
title: lastNavigationStackItem.pageTitle,
Icon: lastNavigationStackItem.pageIcon,
instanceId: lastNavigationStackItem.pageId,
});
jotaiStore.set(commandMenuNavigationStackState.atom, newNavigationStack);
store.set(commandMenuNavigationStackState.atom, newNavigationStack);
const currentMorphItems = jotaiStore.get(
const currentMorphItems = store.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
@@ -48,14 +49,14 @@ export const useCommandMenuHistory = () => {
if (isDefined(removedItem)) {
const newMorphItems = new Map(currentMorphItems);
newMorphItems.delete(removedItem.pageId);
jotaiStore.set(
store.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItems,
);
const morphItems = currentMorphItems.get(removedItem.pageId);
if (isNonEmptyArray(morphItems)) {
jotaiStore.set(
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId: removedItem.pageId,
@@ -68,63 +69,63 @@ export const useCommandMenuHistory = () => {
}
}
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, [closeCommandMenu]);
store.set(hasUserSelectedCommandState.atom, false);
}, [closeCommandMenu, store]);
const navigateCommandMenuHistory = useCallback((pageIndex: number) => {
const currentNavigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const newNavigationStack = currentNavigationStack.slice(0, pageIndex + 1);
jotaiStore.set(commandMenuNavigationStackState.atom, newNavigationStack);
const newNavigationStackItem = newNavigationStack.at(-1);
if (!isDefined(newNavigationStackItem)) {
throw new Error(
`No command menu navigation stack item found for index ${pageIndex}`,
const navigateCommandMenuHistory = useCallback(
(pageIndex: number) => {
const currentNavigationStack = store.get(
commandMenuNavigationStackState.atom,
);
}
jotaiStore.set(commandMenuPageState.atom, newNavigationStackItem.page);
jotaiStore.set(commandMenuPageInfoState.atom, {
title: newNavigationStackItem.pageTitle,
Icon: newNavigationStackItem.pageIcon,
instanceId: newNavigationStackItem.pageId,
});
const currentMorphItems = jotaiStore.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
const newNavigationStack = currentNavigationStack.slice(0, pageIndex + 1);
for (const [pageId, morphItems] of currentMorphItems.entries()) {
if (!newNavigationStack.some((item) => item.pageId === pageId)) {
jotaiStore.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
store.set(commandMenuNavigationStackState.atom, newNavigationStack);
const newNavigationStackItem = newNavigationStack.at(-1);
if (!isDefined(newNavigationStackItem)) {
throw new Error(
`No command menu navigation stack item found for index ${pageIndex}`,
);
}
}
const newMorphItems = new Map(
Array.from(currentMorphItems.entries()).filter(([pageId]) =>
newNavigationStack.some((item) => item.pageId === pageId),
),
);
store.set(commandMenuPageState.atom, newNavigationStackItem.page);
store.set(commandMenuPageInfoState.atom, {
title: newNavigationStackItem.pageTitle,
Icon: newNavigationStackItem.pageIcon,
instanceId: newNavigationStackItem.pageId,
});
const currentMorphItems = store.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItems,
);
for (const [pageId, morphItems] of currentMorphItems.entries()) {
if (!newNavigationStack.some((item) => item.pageId === pageId)) {
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, []);
const newMorphItems = new Map(
Array.from(currentMorphItems.entries()).filter(([pageId]) =>
newNavigationStack.some((item) => item.pageId === pageId),
),
);
store.set(commandMenuNavigationMorphItemsByPageState.atom, newMorphItems);
store.set(hasUserSelectedCommandState.atom, false);
},
[store],
);
return {
goBackFromCommandMenu,
@@ -1,6 +1,6 @@
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
type UpdateNavigationMorphItemsByPageParams = {
pageId: string;
@@ -9,13 +9,14 @@ type UpdateNavigationMorphItemsByPageParams = {
};
export const useCommandMenuUpdateNavigationMorphItemsByPage = () => {
const store = useStore();
const updateCommandMenuNavigationMorphItemsByPage = useCallback(
async ({
pageId,
objectMetadataId,
objectRecordIds,
}: UpdateNavigationMorphItemsByPageParams) => {
const currentMorphItems = jotaiStore.get(
const currentMorphItems = store.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
@@ -26,12 +27,12 @@ export const useCommandMenuUpdateNavigationMorphItemsByPage = () => {
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageId, newMorphItems);
jotaiStore.set(
store.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItemsMap,
);
},
[],
[store],
);
return {
@@ -7,10 +7,11 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useCopyContextStoreStates = () => {
const store = useStore();
const copyContextStoreStates = useCallback(
({
instanceIdToCopyFrom,
@@ -19,124 +20,124 @@ export const useCopyContextStoreStates = () => {
instanceIdToCopyFrom: string;
instanceIdToCopyTo: string;
}) => {
const contextStoreCurrentObjectMetadataItemId = jotaiStore.get(
const contextStoreCurrentObjectMetadataItemId = store.get(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentObjectMetadataItemId,
);
const contextStoreTargetedRecordsRule = jotaiStore.get(
const contextStoreTargetedRecordsRule = store.get(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreTargetedRecordsRule,
);
const contextStoreNumberOfSelectedRecords = jotaiStore.get(
const contextStoreNumberOfSelectedRecords = store.get(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreNumberOfSelectedRecords,
);
const contextStoreFilters = jotaiStore.get(
const contextStoreFilters = store.get(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilters,
);
const contextStoreFilterGroups = jotaiStore.get(
const contextStoreFilterGroups = store.get(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilterGroups,
);
const contextStoreAnyFieldFilterValue = jotaiStore.get(
const contextStoreAnyFieldFilterValue = store.get(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreAnyFieldFilterValue,
);
const contextStoreCurrentViewId = jotaiStore.get(
const contextStoreCurrentViewId = store.get(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewId,
);
const contextStoreCurrentViewType = jotaiStore.get(
const contextStoreCurrentViewType = store.get(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewType,
);
const contextStoreIsFullTabWidgetInEditMode = jotaiStore.get(
const contextStoreIsFullTabWidgetInEditMode = store.get(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
);
jotaiStore.set(
store.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreIsFullTabWidgetInEditMode,
);
},
[],
[store],
);
return { copyContextStoreStates };
@@ -13,11 +13,11 @@ import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuO
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { type CommandMenuPages } from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export type CommandMenuNavigationStackItem = {
page: CommandMenuPages;
@@ -28,6 +28,7 @@ export type CommandMenuNavigationStackItem = {
};
export const useNavigateCommandMenu = () => {
const store = useStore();
const { copyContextStoreStates } = useCopyContextStoreStates();
const { commandMenuCloseAnimationCompleteCleanup } =
@@ -36,9 +37,9 @@ export const useNavigateCommandMenu = () => {
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const openCommandMenu = useCallback(() => {
const isCommandMenuOpened = jotaiStore.get(isCommandMenuOpenedStateV2.atom);
const isCommandMenuOpened = store.get(isCommandMenuOpenedStateV2.atom);
const isCommandMenuClosing = jotaiStore.get(isCommandMenuClosingState.atom);
const isCommandMenuClosing = store.get(isCommandMenuClosingState.atom);
if (isCommandMenuClosing) {
commandMenuCloseAnimationCompleteCleanup();
@@ -64,12 +65,13 @@ export const useNavigateCommandMenu = () => {
instanceIdToCopyTo: COMMAND_MENU_COMPONENT_INSTANCE_ID,
});
jotaiStore.set(isCommandMenuOpenedStateV2.atom, true);
jotaiStore.set(hasUserSelectedCommandState.atom, false);
store.set(isCommandMenuOpenedStateV2.atom, true);
store.set(hasUserSelectedCommandState.atom, false);
}, [
copyContextStoreStates,
commandMenuCloseAnimationCompleteCleanup,
pushFocusItemToFocusStack,
store,
]);
const navigateCommandMenu = useCallback(
@@ -88,15 +90,15 @@ export const useNavigateCommandMenu = () => {
const computedPageId = pageId || v4();
openCommandMenu();
jotaiStore.set(commandMenuPageState.atom, page);
jotaiStore.set(commandMenuPageInfoState.atom, {
store.set(commandMenuPageState.atom, page);
store.set(commandMenuPageInfoState.atom, {
title: pageTitle,
Icon: pageIcon,
instanceId: computedPageId,
});
if (focusTitleInput) {
jotaiStore.set(
store.set(
commandMenuShouldFocusTitleInputComponentState.atomFamily({
instanceId: computedPageId,
}),
@@ -104,16 +106,14 @@ export const useNavigateCommandMenu = () => {
);
}
const isCommandMenuClosing = jotaiStore.get(
isCommandMenuClosingState.atom,
);
const isCommandMenuClosing = store.get(isCommandMenuClosingState.atom);
const currentNavigationStack = isCommandMenuClosing
? []
: jotaiStore.get(commandMenuNavigationStackState.atom);
: store.get(commandMenuNavigationStackState.atom);
if (resetNavigationStack) {
jotaiStore.set(commandMenuNavigationStackState.atom, [
store.set(commandMenuNavigationStackState.atom, [
{
page,
pageTitle,
@@ -123,12 +123,9 @@ export const useNavigateCommandMenu = () => {
},
]);
jotaiStore.set(
commandMenuNavigationMorphItemsByPageState.atom,
new Map(),
);
store.set(commandMenuNavigationMorphItemsByPageState.atom, new Map());
} else {
jotaiStore.set(commandMenuNavigationStackState.atom, [
store.set(commandMenuNavigationStackState.atom, [
...currentNavigationStack,
{
page,
@@ -140,7 +137,7 @@ export const useNavigateCommandMenu = () => {
]);
}
},
[openCommandMenu],
[openCommandMenu, store],
);
return {
@@ -1,20 +1,21 @@
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconCalendarEvent } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export const useOpenCalendarEventInCommandMenu = () => {
const store = useStore();
const { navigateCommandMenu } = useNavigateCommandMenu();
const openCalendarEventInCommandMenu = useCallback(
(calendarEventId: string) => {
const pageComponentInstanceId = v4();
jotaiStore.set(
store.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
@@ -53,7 +54,7 @@ export const useOpenCalendarEventInCommandMenu = () => {
pageId: pageComponentInstanceId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
return {
@@ -1,20 +1,21 @@
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { t } from '@lingui/core/macro';
import { useCallback } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { IconMail } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export const useOpenEmailThreadInCommandMenu = () => {
const store = useStore();
const { navigateCommandMenu } = useNavigateCommandMenu();
const openEmailThreadInCommandMenu = useCallback(
(emailThreadId: string) => {
const pageComponentInstanceId = v4();
jotaiStore.set(
store.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
@@ -53,7 +54,7 @@ export const useOpenEmailThreadInCommandMenu = () => {
pageId: pageComponentInstanceId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
return {
@@ -1,11 +1,12 @@
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { viewableFrontComponentIdComponentState } from '@/command-menu/pages/front-component/states/viewableFrontComponentIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export const useOpenFrontComponentInCommandMenu = () => {
const store = useStore();
const { navigateCommandMenu } = useCommandMenu();
const openFrontComponentInCommandMenu = ({
@@ -21,7 +22,7 @@ export const useOpenFrontComponentInCommandMenu = () => {
}) => {
const pageComponentInstanceId = v4();
jotaiStore.set(
store.set(
viewableFrontComponentIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
@@ -6,11 +6,11 @@ import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRec
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { msg, t } from '@lingui/core/macro';
import { useCallback } from 'react';
import { IconArrowMerge } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
type UseOpenMergeRecordsPageInCommandMenuProps = {
objectNameSingular: string;
@@ -21,6 +21,7 @@ export const useOpenMergeRecordsPageInCommandMenu = ({
objectNameSingular,
objectRecordIds,
}: UseOpenMergeRecordsPageInCommandMenuProps) => {
const store = useStore();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
@@ -43,7 +44,7 @@ export const useOpenMergeRecordsPageInCommandMenu = ({
const openMergeRecordsPageInCommandMenu = useCallback(async () => {
const pageId = v4();
jotaiStore.set(
store.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -71,6 +72,7 @@ export const useOpenMergeRecordsPageInCommandMenu = ({
upsertRecordsInStore,
navigateCommandMenu,
updateCommandMenuNavigationMorphItemsByPage,
store,
]);
return {
@@ -15,7 +15,6 @@ import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objec
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { getIconColorForObjectType } from '@/object-metadata/utils/getIconColorForObjectType';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CommandMenuPages } from 'twenty-shared/types';
import { useRunWorkflowRunOpeningInCommandMenuSideEffects } from '@/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects';
@@ -25,8 +24,10 @@ import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export const useOpenRecordInCommandMenu = () => {
const store = useStore();
const theme = useTheme();
const { getIcon } = useIcons();
@@ -46,14 +47,12 @@ export const useOpenRecordInCommandMenu = () => {
isNewRecord?: boolean;
resetNavigationStack?: boolean;
}) => {
const navigationStack = jotaiStore.get(
commandMenuNavigationStackState.atom,
);
const navigationStack = store.get(commandMenuNavigationStackState.atom);
const currentNavigationStackItem = navigationStack.at(-1);
if (isDefined(currentNavigationStackItem)) {
const currentRecordId = jotaiStore.get(
const currentRecordId = store.get(
viewableRecordIdComponentState.atomFamily({
instanceId: currentNavigationStackItem.pageId,
}),
@@ -66,21 +65,21 @@ export const useOpenRecordInCommandMenu = () => {
const pageComponentInstanceId = v4();
jotaiStore.set(
store.set(
viewableRecordNameSingularComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectNameSingular,
);
jotaiStore.set(
store.set(
viewableRecordIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
recordId,
);
jotaiStore.set(viewableRecordIdState.atom, recordId);
store.set(viewableRecordIdState.atom, recordId);
const objectMetadataItem = jotaiStore.get(
const objectMetadataItem = store.get(
objectMetadataItemFamilySelector.selectorFamily({
objectName: objectNameSingular,
objectNameType: 'singular',
@@ -93,14 +92,14 @@ export const useOpenRecordInCommandMenu = () => {
);
}
jotaiStore.set(
store.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
objectMetadataItem.id,
);
jotaiStore.set(
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
@@ -110,43 +109,43 @@ export const useOpenRecordInCommandMenu = () => {
},
);
jotaiStore.set(
store.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
1,
);
jotaiStore.set(
store.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
ContextStoreViewType.ShowPage,
);
jotaiStore.set(
store.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
jotaiStore.get(
store.get(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
),
);
jotaiStore.set(
store.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: pageComponentInstanceId,
}),
jotaiStore.get(
store.get(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
}),
),
);
const currentMorphItems = jotaiStore.get(
const currentMorphItems = store.get(
commandMenuNavigationMorphItemsByPageState.atom,
);
@@ -158,7 +157,7 @@ export const useOpenRecordInCommandMenu = () => {
const newMorphItemsMap = new Map(currentMorphItems);
newMorphItemsMap.set(pageComponentInstanceId, [morphItemToAdd]);
jotaiStore.set(
store.set(
commandMenuNavigationMorphItemsByPageState.atom,
newMorphItemsMap,
);
@@ -197,6 +196,7 @@ export const useOpenRecordInCommandMenu = () => {
navigateCommandMenu,
runWorkflowRunOpeningInCommandMenuSideEffects,
theme,
store,
],
);
@@ -5,63 +5,67 @@ import { contextStoreFilterGroupsComponentState } from '@/context-store/states/c
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useResetContextStoreStates = () => {
const resetContextStoreStates = useCallback((instanceId: string) => {
jotaiStore.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
const store = useStore();
const resetContextStoreStates = useCallback(
(instanceId: string) => {
store.set(
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
jotaiStore.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
jotaiStore.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId,
}),
0,
);
store.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId,
}),
0,
);
jotaiStore.set(
contextStoreFiltersComponentState.atomFamily({
instanceId,
}),
[],
);
store.set(
contextStoreFiltersComponentState.atomFamily({
instanceId,
}),
[],
);
jotaiStore.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId,
}),
[],
);
store.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId,
}),
[],
);
jotaiStore.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId,
}),
'',
);
store.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId,
}),
'',
);
jotaiStore.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
}, []);
store.set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId,
}),
undefined,
);
},
[store],
);
return { resetContextStoreStates };
};
@@ -10,10 +10,11 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useSetGlobalCommandMenuContext = () => {
const store = useStore();
const { copyContextStoreStates } = useCopyContextStoreStates();
const setGlobalCommandMenuContext = useCallback(() => {
@@ -22,7 +23,7 @@ export const useSetGlobalCommandMenuContext = () => {
instanceIdToCopyTo: COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID,
});
jotaiStore.set(
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
@@ -32,49 +33,49 @@ export const useSetGlobalCommandMenuContext = () => {
},
);
jotaiStore.set(
store.set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
0,
);
jotaiStore.set(
store.set(
contextStoreFiltersComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
jotaiStore.set(
store.set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
jotaiStore.set(
store.set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
'',
);
jotaiStore.set(
store.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
ContextStoreViewType.Table,
);
jotaiStore.set(commandMenuPageInfoState.atom, {
store.set(commandMenuPageInfoState.atom, {
title: undefined,
Icon: undefined,
instanceId: '',
});
jotaiStore.set(hasUserSelectedCommandState.atom, false);
}, [copyContextStoreStates]);
store.set(hasUserSelectedCommandState.atom, false);
}, [copyContextStoreStates, store]);
return {
setGlobalCommandMenuContext,
@@ -1,10 +1,11 @@
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { type IconComponent, IconDotsVertical } from 'twenty-ui/display';
import { useStore } from 'jotai';
export const useUpdateCommandMenuPageInfo = () => {
const store = useStore();
const updateCommandMenuPageInfo = useCallback(
({
pageTitle,
@@ -13,7 +14,7 @@ export const useUpdateCommandMenuPageInfo = () => {
pageTitle?: string;
pageIcon?: IconComponent;
}) => {
const commandMenuPageInfo = jotaiStore.get(commandMenuPageInfoState.atom);
const commandMenuPageInfo = store.get(commandMenuPageInfoState.atom);
const newCommandMenuPageInfo = {
...commandMenuPageInfo,
@@ -21,9 +22,9 @@ export const useUpdateCommandMenuPageInfo = () => {
Icon: pageIcon ?? commandMenuPageInfo.Icon ?? IconDotsVertical,
};
jotaiStore.set(commandMenuPageInfoState.atom, newCommandMenuPageInfo);
store.set(commandMenuPageInfoState.atom, newCommandMenuPageInfo);
const commandMenuNavigationStack = jotaiStore.get(
const commandMenuNavigationStack = store.get(
commandMenuNavigationStackState.atom,
);
@@ -44,12 +45,12 @@ export const useUpdateCommandMenuPageInfo = () => {
},
];
jotaiStore.set(
store.set(
commandMenuNavigationStackState.atom,
newCommandMenuNavigationStack,
);
},
[],
[store],
);
return {
@@ -3,7 +3,6 @@ import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workfl
import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState';
import { commandMenuWorkflowStepIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState';
import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { useSetInitialWorkflowRunRightDrawerTab } from '@/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
@@ -17,8 +16,10 @@ import {
IconSettingsAutomation,
} from 'twenty-ui/display';
import { v4 } from 'uuid';
import { useStore } from 'jotai';
export const useWorkflowCommandMenu = () => {
const store = useStore();
const { navigateCommandMenu } = useNavigateCommandMenu();
const { setInitialWorkflowRunRightDrawerTab } =
useSetInitialWorkflowRunRightDrawerTab();
@@ -27,7 +28,7 @@ export const useWorkflowCommandMenu = () => {
(workflowId: string) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -41,14 +42,14 @@ export const useWorkflowCommandMenu = () => {
pageId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
const openWorkflowCreateStepInCommandMenu = useCallback(
(workflowId: string) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -62,7 +63,7 @@ export const useWorkflowCommandMenu = () => {
pageId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
const openWorkflowEditStepInCommandMenu = useCallback(
@@ -74,7 +75,7 @@ export const useWorkflowCommandMenu = () => {
) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -82,14 +83,14 @@ export const useWorkflowCommandMenu = () => {
);
if (isDefined(stepId)) {
jotaiStore.set(
store.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
stepId,
);
jotaiStore.set(
store.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowId,
}),
@@ -104,14 +105,14 @@ export const useWorkflowCommandMenu = () => {
pageId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
const openWorkflowEditStepTypeInCommandMenu = useCallback(
(workflowId: string) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -125,7 +126,7 @@ export const useWorkflowCommandMenu = () => {
pageId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
const openWorkflowViewStepInCommandMenu = useCallback(
@@ -144,13 +145,13 @@ export const useWorkflowCommandMenu = () => {
}) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
jotaiStore.set(
store.set(
commandMenuWorkflowVersionIdComponentState.atomFamily({
instanceId: pageId,
}),
@@ -158,14 +159,14 @@ export const useWorkflowCommandMenu = () => {
);
if (isDefined(stepId)) {
jotaiStore.set(
store.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
stepId,
);
jotaiStore.set(
store.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowVersionId,
}),
@@ -180,7 +181,7 @@ export const useWorkflowCommandMenu = () => {
pageId,
});
},
[navigateCommandMenu],
[navigateCommandMenu, store],
);
const openWorkflowRunViewStepInCommandMenu = useCallback(
@@ -201,26 +202,26 @@ export const useWorkflowCommandMenu = () => {
}) => {
const pageId = v4();
jotaiStore.set(
store.set(
commandMenuWorkflowIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowId,
);
jotaiStore.set(
store.set(
commandMenuWorkflowRunIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowRunId,
);
jotaiStore.set(
store.set(
commandMenuWorkflowStepIdComponentState.atomFamily({
instanceId: pageId,
}),
workflowSelectedNode,
);
jotaiStore.set(
store.set(
workflowSelectedNodeComponentState.atomFamily({
instanceId: workflowRunId,
}),
@@ -239,7 +240,7 @@ export const useWorkflowCommandMenu = () => {
stepExecutionStatus,
});
},
[navigateCommandMenu, setInitialWorkflowRunRightDrawerTab],
[navigateCommandMenu, setInitialWorkflowRunRightDrawerTab, store],
);
return {
@@ -49,12 +49,12 @@ export const ChartFiltersSettings = ({
const { updateCurrentWidgetConfig } =
useUpdateCurrentWidgetConfig(pageLayoutId);
const currentRecordFiltersAtom = useRecoilComponentStateCallbackStateV2(
const currentRecordFilters = useRecoilComponentStateCallbackStateV2(
currentRecordFiltersComponentState,
instanceId,
);
const currentRecordFilterGroupsAtom = useRecoilComponentStateCallbackStateV2(
const currentRecordFilterGroups = useRecoilComponentStateCallbackStateV2(
currentRecordFilterGroupsComponentState,
instanceId,
);
@@ -63,15 +63,15 @@ export const ChartFiltersSettings = ({
const chartWidgetConfiguration = widget.configuration;
const handleFiltersUpdate = () => {
const currentRecordFilters = store.get(currentRecordFiltersAtom);
const currentRecordFilterGroups = store.get(currentRecordFilterGroupsAtom);
const existingRecordFilters = store.get(currentRecordFilters);
const existingRecordFilterGroups = store.get(currentRecordFilterGroups);
updateCurrentWidgetConfig({
objectMetadataId: objectMetadataItem.id,
configToUpdate: {
filter: {
recordFilters: currentRecordFilters,
recordFilterGroups: currentRecordFilterGroups,
recordFilters: existingRecordFilters,
recordFilterGroups: existingRecordFilterGroups,
},
} satisfies Partial<ChartWidgetConfiguration>,
});
@@ -5,11 +5,12 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { hasInitializedCurrentRecordFilterGroupsComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordFilterGroupsComponentFamilyState';
import { hasInitializedCurrentRecordFiltersComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordFiltersComponentFamilyState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useResetChartDraftFiltersSettings = () => {
const store = useStore();
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
@@ -25,7 +26,7 @@ export const useResetChartDraftFiltersSettings = () => {
objectMetadataItemId: objectMetadataItemId,
});
jotaiStore.set(
store.set(
hasInitializedCurrentRecordFilterGroupsComponentFamilyState.atomFamily({
familyKey: {},
instanceId,
@@ -33,7 +34,7 @@ export const useResetChartDraftFiltersSettings = () => {
false,
);
jotaiStore.set(
store.set(
hasInitializedCurrentRecordFiltersComponentFamilyState.atomFamily({
familyKey: {},
instanceId,
@@ -41,16 +42,16 @@ export const useResetChartDraftFiltersSettings = () => {
false,
);
jotaiStore.set(
store.set(
currentRecordFiltersComponentState.atomFamily({ instanceId }),
[],
);
jotaiStore.set(
store.set(
currentRecordFilterGroupsComponentState.atomFamily({ instanceId }),
[],
);
},
[widgetInEditMode],
[widgetInEditMode, store],
);
return {
@@ -7,7 +7,7 @@ import { useStore } from 'jotai';
import { useCallback } from 'react';
export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
const pageLayoutDraftAtom = useRecoilComponentStateCallbackStateV2(
const pageLayoutDraft = useRecoilComponentStateCallbackStateV2(
pageLayoutDraftComponentState,
pageLayoutIdFromProps,
);
@@ -27,8 +27,8 @@ export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
objectMetadataId?: string | null;
configToUpdate?: Partial<PageLayoutWidget['configuration']>;
}) => {
const prev = store.get(pageLayoutDraftAtom);
store.set(pageLayoutDraftAtom, {
const prev = store.get(pageLayoutDraft);
store.set(pageLayoutDraft, {
...prev,
tabs: prev.tabs.map((tab) => ({
...tab,
@@ -47,7 +47,7 @@ export const useUpdateCurrentWidgetConfig = (pageLayoutIdFromProps: string) => {
})),
});
},
[pageLayoutDraftAtom, currentlyEditingWidgetId, store],
[pageLayoutDraft, currentlyEditingWidgetId, store],
);
return { updateCurrentWidgetConfig };
@@ -2,13 +2,12 @@ import { useCallback } from 'react';
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useStore } from 'jotai';
export const useBuildSearchParamsFromUrlSyncedStates = () => {
const store = useStore();
const buildSearchParamsFromUrlSyncedStates = useCallback(async () => {
const billingCheckoutSession = jotaiStore.get(
billingCheckoutSessionState.atom,
);
const billingCheckoutSession = store.get(billingCheckoutSessionState.atom);
const output = {
...(billingCheckoutSession !== BILLING_CHECKOUT_SESSION_DEFAULT_VALUE
@@ -19,7 +18,7 @@ export const useBuildSearchParamsFromUrlSyncedStates = () => {
};
return output;
}, []);
}, [store]);
return {
buildSearchParamsFromUrlSyncedStates,
@@ -8,11 +8,11 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
import { usePrefetchedFavoritesFoldersData } from '@/favorites/hooks/usePrefetchedFavoritesFoldersData';
import { type FavoriteFolder } from '@/favorites/types/FavoriteFolder';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentFamilyStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentFamilyStateCallbackStateV2';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
type FavoriteFolderPickerEffectProps = {
record?: ObjectRecord;
@@ -21,10 +21,11 @@ type FavoriteFolderPickerEffectProps = {
export const FavoriteFolderPickerEffect = ({
record,
}: FavoriteFolderPickerEffectProps) => {
const store = useStore();
const [favoriteFolderIdsPicker, setFavoriteFolderIdsPicker] =
useRecoilComponentStateV2(favoriteFolderIdsPickerComponentState);
const favoriteFolderPickerFamilyAtom =
const favoriteFolderPickerFamily =
useRecoilComponentFamilyStateCallbackStateV2(
favoriteFolderPickerComponentFamilyState,
);
@@ -39,15 +40,15 @@ export const FavoriteFolderPickerEffect = ({
const updateFolders = useCallback(
(folders: FavoriteFolder[]) => {
folders.forEach((folder) => {
const atom = favoriteFolderPickerFamilyAtom(folder.id);
const currentFolder = jotaiStore.get(atom);
const atom = favoriteFolderPickerFamily(folder.id);
const currentFolder = store.get(atom);
if (!isDeeplyEqual(folder, currentFolder)) {
jotaiStore.set(atom, folder);
store.set(atom, folder);
}
});
},
[favoriteFolderPickerFamilyAtom],
[favoriteFolderPickerFamily, store],
);
useEffect(() => {
@@ -26,7 +26,7 @@ export const useFavoriteFolderPicker = ({
record,
objectNameSingular,
}: useFavoriteFolderPickerProps): useFavoriteFolderPickerReturnType => {
const favoriteFoldersMultiSelectCheckedAtom =
const favoriteFoldersMultiSelectChecked =
useRecoilComponentStateCallbackStateV2(
favoriteFolderPickerCheckedComponentState,
);
@@ -70,7 +70,7 @@ export const useFavoriteFolderPicker = ({
await deleteFavorite(favoriteToDelete.id);
};
const checkedIds = store.get(favoriteFoldersMultiSelectCheckedAtom);
const checkedIds = store.get(favoriteFoldersMultiSelectChecked);
const isAlreadyChecked = checkedIds.includes(folderId);
@@ -78,7 +78,7 @@ export const useFavoriteFolderPicker = ({
await deleteFavoriteForRecord(folderId === 'no-folder');
const newCheckedIds = checkedIds.filter((id) => id !== folderId);
store.set(favoriteFoldersMultiSelectCheckedAtom, newCheckedIds);
store.set(favoriteFoldersMultiSelectChecked, newCheckedIds);
return;
}
@@ -89,10 +89,10 @@ export const useFavoriteFolderPicker = ({
}
const newCheckedIds = [...checkedIds, folderId];
store.set(favoriteFoldersMultiSelectCheckedAtom, newCheckedIds);
store.set(favoriteFoldersMultiSelectChecked, newCheckedIds);
},
[
favoriteFoldersMultiSelectCheckedAtom,
favoriteFoldersMultiSelectChecked,
createFavorite,
deleteFavorite,
favorites,
@@ -1,17 +1,17 @@
import { useCallback } from 'react';
import { mountedHeadlessFrontComponentIdsState } from '@/front-components/states/mountedHeadlessFrontComponentIdsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useStore } from 'jotai';
export const useMountHeadlessFrontComponent = () => {
const store = useStore();
const mountHeadlessFrontComponent = useCallback(
(frontComponentId: string) => {
jotaiStore.set(
mountedHeadlessFrontComponentIdsState.atom,
(previousIds) => new Set(previousIds).add(frontComponentId),
store.set(mountedHeadlessFrontComponentIdsState.atom, (previousIds) =>
new Set(previousIds).add(frontComponentId),
);
},
[],
[store],
);
return mountHeadlessFrontComponent;
@@ -1,21 +1,19 @@
import { useCallback } from 'react';
import { mountedHeadlessFrontComponentIdsState } from '@/front-components/states/mountedHeadlessFrontComponentIdsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useStore } from 'jotai';
export const useUnmountHeadlessFrontComponent = () => {
const store = useStore();
const unmountHeadlessFrontComponent = useCallback(
(frontComponentId: string) => {
jotaiStore.set(
mountedHeadlessFrontComponentIdsState.atom,
(previousIds) => {
const next = new Set(previousIds);
next.delete(frontComponentId);
return next;
},
);
store.set(mountedHeadlessFrontComponentIdsState.atom, (previousIds) => {
const next = new Set(previousIds);
next.delete(frontComponentId);
return next;
});
},
[],
[store],
);
return unmountHeadlessFrontComponent;
@@ -20,8 +20,12 @@ export const ObjectMetadataProviderInitialEffect = () => {
const { updateDraft, applyChanges } = useMetadataStore();
useEffect(() => {
if (isInitialized) return;
if (!isCurrentUserLoaded) return;
if (isInitialized) {
return;
}
if (!isCurrentUserLoaded) {
return;
}
const shouldLoadReal = isWorkspaceActiveOrSuspended(currentWorkspace);
@@ -15,20 +15,17 @@ import { type ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { enUS } from 'date-fns/locale';
import { useStore } from 'jotai';
import { useCallback, useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { AppPath, type ObjectPermissions } from 'twenty-shared/types';
import { type ObjectPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
useGetCurrentUserQuery,
type WorkspaceMember,
} from '~/generated-metadata/graphql';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { isMatchingLocation } from '~/utils/isMatchingLocation';
export const UserMetadataProviderInitialEffect = () => {
const location = useLocation();
const isLoggedIn = useIsLogged();
const currentUser = useRecoilValueV2(currentUserState);
const store = useStore();
@@ -53,28 +50,23 @@ export const UserMetadataProviderInitialEffect = () => {
const { initializeFormatPreferences } = useInitializeFormatPreferences();
const isOnAuthPath =
isMatchingLocation(location, AppPath.Verify) ||
isMatchingLocation(location, AppPath.VerifyEmail);
const updateLocaleCatalog = useCallback(
async (newLocale: keyof typeof APP_LOCALES) => {
const localeValue = store.get(dateLocaleStateV2.atom);
const localeValue = store.get(dateLocaleState.atom);
if (localeValue.locale !== newLocale) {
getDateFnsLocale(newLocale).then((localeCatalog) => {
const newValue = {
locale: newLocale,
localeCatalog: localeCatalog || enUS,
};
store.set(dateLocaleStateV2.atom, newValue);
store.set(dateLocaleState.atom, newValue);
});
}
},
[store],
);
const shouldSkipUserQuery =
!isLoggedIn || isDefined(currentUser) || isOnAuthPath;
const shouldSkipUserQuery = !isLoggedIn || isDefined(currentUser);
const { data: userQueryData, loading: userQueryLoading } =
useGetCurrentUserQuery({
@@ -82,7 +74,9 @@ export const UserMetadataProviderInitialEffect = () => {
});
useEffect(() => {
if (isInitialized) return;
if (isInitialized) {
return;
}
if (!isLoggedIn) {
setIsCurrentUserLoaded(true);
@@ -48,8 +48,12 @@ export const ViewMetadataProviderInitialEffect = () => {
);
useEffect(() => {
if (isInitialized) return;
if (!isCurrentUserLoaded) return;
if (isInitialized) {
return;
}
if (!isCurrentUserLoaded) {
return;
}
if (!isLoggedIn || !isWorkspaceActiveOrSuspended(currentWorkspace)) {
setIsInitialized(true);
@@ -1,7 +1,6 @@
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { useNavigate } from 'react-router-dom';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { IconLink, IconPlus, IconTool, useIcons } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
@@ -43,7 +42,7 @@ export const WorkspaceNavigationMenuItems = () => {
const items = useWorkspaceSectionItems();
const store = useStore();
const enterEditMode = () => {
const prefetchNavigationMenuItems = jotaiStore.get(
const prefetchNavigationMenuItems = store.get(
prefetchNavigationMenuItemsState.atom,
);
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
@@ -20,14 +20,15 @@ import { openNavigationMenuItemFolderIdsStateV2 } from '@/navigation-menu-item/s
import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { isWorkspaceDroppableId } from '@/navigation-menu-item/utils/isWorkspaceDroppableId';
import { validateAndExtractWorkspaceFolderId } from '@/navigation-menu-item/utils/validateAndExtractWorkspaceFolderId';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { coreViewsState } from '@/views/states/coreViewState';
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
import { useStore } from 'jotai';
export const useHandleAddToNavigationDrop = () => {
const store = useStore();
const { addObjectToDraft } = useAddObjectToNavigationMenuDraft();
const { addViewToDraft } = useAddViewToNavigationMenuDraft();
const { addRecordToDraft } = useAddRecordToNavigationMenuDraft();
@@ -64,8 +65,7 @@ export const useHandleAddToNavigationDrop = () => {
}
const payload =
jotaiStore.get(addToNavPayloadRegistryStateV2.atom).get(draggableId) ??
null;
store.get(addToNavPayloadRegistryStateV2.atom).get(draggableId) ?? null;
if (!payload) {
return;
}
@@ -203,6 +203,7 @@ export const useHandleAddToNavigationDrop = () => {
setIsNavigationMenuInEditMode,
setSelectedNavigationMenuItemInEditMode,
workspaceNavigationMenuItems,
store,
],
);
@@ -3,11 +3,12 @@ import { useCallback } from 'react';
import { FIND_MANY_NAVIGATION_MENU_ITEMS } from '@/navigation-menu-item/graphql/queries/findManyNavigationMenuItems';
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useRemoveNavigationMenuItemByTargetRecordId = () => {
const store = useStore();
const apolloCoreClient = useApolloCoreClient();
const cache = apolloCoreClient.cache;
@@ -18,7 +19,7 @@ export const useRemoveNavigationMenuItemByTargetRecordId = () => {
const removeNavigationMenuItemsByTargetRecordIds = useCallback(
(targetRecordIds: string[]) => {
const targetRecordIdsSet = new Set(targetRecordIds);
const currentNavigationMenuItems = jotaiStore.get(
const currentNavigationMenuItems = store.get(
prefetchNavigationMenuItemsState.atom,
);
@@ -41,7 +42,7 @@ export const useRemoveNavigationMenuItemByTargetRecordId = () => {
};
});
},
[cache, setNavigationMenuItemsState],
[cache, setNavigationMenuItemsState, store],
);
return {
@@ -1,6 +1,5 @@
import { currentUserState } from '@/auth/states/currentUserState';
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type ObjectPathInfo } from '@/navigation/types/ObjectPathInfo';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
@@ -12,8 +11,10 @@ import isEmpty from 'lodash.isempty';
import { useCallback, useMemo } from 'react';
import { AppPath, SettingsPath } from 'twenty-shared/types';
import { getAppPath, getSettingsPath, isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useDefaultHomePagePath = () => {
const store = useStore();
const currentUser = useRecoilValueV2(currentUserState);
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
@@ -69,7 +70,7 @@ export const useDefaultHomePagePath = () => {
}, [getFirstView, readableAlphaSortedActiveNonSystemObjectMetadataItems]);
const getDefaultObjectPathInfo = useCallback(() => {
const lastVisitedObjectMetadataItemId = jotaiStore.get(
const lastVisitedObjectMetadataItemId = store.get(
lastVisitedObjectMetadataItemIdState.atom,
);
@@ -91,6 +92,7 @@ export const useDefaultHomePagePath = () => {
firstObjectPathInfo,
getActiveObjectMetadataItemMatchingId,
getFirstView,
store,
]);
const defaultHomePagePath = useMemo(() => {
@@ -1,19 +1,20 @@
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useSetLastVisitedObjectMetadataId = () => {
const store = useStore();
const setLastVisitedObjectMetadataId = useCallback(
({ objectMetadataItemId }: { objectMetadataItemId: string }) => {
const objectMetadataItems = jotaiStore.get(objectMetadataItemsState.atom);
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === objectMetadataItemId,
);
const lastVisitedObjectMetadataItemId = jotaiStore.get(
const lastVisitedObjectMetadataItemId = store.get(
lastVisitedObjectMetadataItemIdState.atom,
);
@@ -21,13 +22,13 @@ export const useSetLastVisitedObjectMetadataId = () => {
isDefined(objectMetadataItem) &&
lastVisitedObjectMetadataItemId !== objectMetadataItemId
) {
jotaiStore.set(
store.set(
lastVisitedObjectMetadataItemIdState.atom,
objectMetadataItemId,
);
}
},
[],
[store],
);
return {
@@ -1,12 +1,13 @@
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
const store = useStore();
const setLastVisitedViewForObjectMetadataNamePlural = useCallback(
async ({
objectNamePlural,
@@ -15,13 +16,13 @@ export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
objectNamePlural: string;
viewId: string;
}) => {
const views = jotaiStore.get(coreViewsState.atom);
const views = store.get(coreViewsState.atom);
const view = views.find(
(view: CoreViewWithRelations) => view.id === viewId,
);
const objectMetadataItems = jotaiStore.get(objectMetadataItemsState.atom);
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
const objectMetadataItem = objectMetadataItems.find(
(item) => item.namePlural === objectNamePlural,
@@ -35,7 +36,7 @@ export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
return;
}
const lastVisitedViewPerObjectMetadataItem = jotaiStore.get(
const lastVisitedViewPerObjectMetadataItem = store.get(
lastVisitedViewPerObjectMetadataItemState.atom,
);
@@ -43,13 +44,13 @@ export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem?.id];
if (isDefined(objectMetadataItem) && lastVisitedViewId !== viewId) {
jotaiStore.set(lastVisitedViewPerObjectMetadataItemState.atom, {
store.set(lastVisitedViewPerObjectMetadataItemState.atom, {
...lastVisitedViewPerObjectMetadataItem,
[objectMetadataItem.id]: viewId,
});
}
},
[],
[store],
);
return {
@@ -1,10 +1,10 @@
import { useCallback } from 'react';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getFieldMetadataItemByIdOrThrow } from '@/object-metadata/utils/getFieldMetadataItemByIdOrThrow';
import { useStore } from 'jotai';
type GetFieldMetadataItemByIdOrThrowResult = {
fieldMetadataItem: FieldMetadataItem;
@@ -12,16 +12,17 @@ type GetFieldMetadataItemByIdOrThrowResult = {
};
export const useGetFieldMetadataItemByIdOrThrow = () => {
const store = useStore();
const getFieldMetadataItemByIdOrThrowCallback = useCallback(
(fieldMetadataId: string): GetFieldMetadataItemByIdOrThrowResult => {
const objectMetadataItems = jotaiStore.get(objectMetadataItemsState.atom);
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
return getFieldMetadataItemByIdOrThrow({
fieldMetadataId,
objectMetadataItems,
});
},
[],
[store],
);
return {
@@ -1,13 +1,15 @@
import { useCallback } from 'react';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useStore } from 'jotai';
export const useGetRelationMetadata = () =>
useCallback(
export const useGetRelationMetadata = () => {
const store = useStore();
return useCallback(
({
fieldMetadataItem,
}: {
@@ -19,7 +21,7 @@ export const useGetRelationMetadata = () =>
if (!relation) return null;
const relationObjectMetadataItem = jotaiStore.get(
const relationObjectMetadataItem = store.get(
objectMetadataItemFamilySelector.selectorFamily({
objectName: relation.targetObjectMetadata.nameSingular,
objectNameType: 'singular',
@@ -40,5 +42,6 @@ export const useGetRelationMetadata = () =>
relationType: relation.type,
};
},
[],
[store],
);
};
@@ -1,28 +1,29 @@
import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { useStore } from 'jotai';
export const useLoadMockedObjectMetadataItems = () => {
const store = useStore();
const loadMockedObjectMetadataItems = useCallback(async () => {
if (
!isDeeplyEqual(
jotaiStore.get(objectMetadataItemsState.atom),
store.get(objectMetadataItemsState.atom),
generatedMockObjectMetadataItems,
)
) {
jotaiStore.set(
store.set(
objectMetadataItemsState.atom,
generatedMockObjectMetadataItems,
);
}
if (jotaiStore.get(isAppEffectRedirectEnabledState.atom) === false) {
jotaiStore.set(isAppEffectRedirectEnabledState.atom, true);
if (store.get(isAppEffectRedirectEnabledState.atom) === false) {
store.set(isAppEffectRedirectEnabledState.atom, true);
}
}, []);
}, [store]);
return {
loadMockedObjectMetadataItems,
@@ -5,17 +5,18 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { enrichObjectMetadataItemsWithPermissions } from '@/object-metadata/utils/enrichObjectMetadataItemsWithPermissions';
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { type FetchPolicy, useApolloClient } from '@apollo/client';
import { useCallback } from 'react';
import { type ObjectPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { useStore } from 'jotai';
export const useRefreshObjectMetadataItems = (
fetchPolicy: FetchPolicy = 'network-only',
) => {
const store = useStore();
const client = useApolloClient();
const replaceObjectMetadataItemIfDifferent = useCallback(
@@ -25,9 +26,7 @@ export const useRefreshObjectMetadataItems = (
'readableFields' | 'updatableFields'
>[],
) => {
const currentUserWorkspace = jotaiStore.get(
currentUserWorkspaceState.atom,
);
const currentUserWorkspace = store.get(currentUserWorkspaceState.atom);
if (!isDefined(currentUserWorkspace)) {
return;
@@ -52,21 +51,21 @@ export const useRefreshObjectMetadataItems = (
if (
!isDeeplyEqual(
jotaiStore.get(objectMetadataItemsState.atom),
store.get(objectMetadataItemsState.atom),
newObjectMetadataItems,
) &&
newObjectMetadataItems.length > 0
) {
jotaiStore.set(objectMetadataItemsState.atom, newObjectMetadataItems);
store.set(objectMetadataItemsState.atom, newObjectMetadataItems);
}
if (jotaiStore.get(isAppEffectRedirectEnabledState.atom) === false) {
jotaiStore.set(isAppEffectRedirectEnabledState.atom, true);
if (store.get(isAppEffectRedirectEnabledState.atom) === false) {
store.set(isAppEffectRedirectEnabledState.atom, true);
}
return newObjectMetadataItems;
},
[],
[store],
);
const refreshObjectMetadataItems = async () => {
@@ -1,13 +1,14 @@
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useGetDefaultFieldMetadataItemForFilter = () => {
const store = useStore();
const getDefaultFieldMetadataItemForFilter = useCallback(
(objectMetadataItem: ObjectMetadataItem) => {
const availableFieldMetadataItemsForFilter = jotaiStore.get(
const availableFieldMetadataItemsForFilter = store.get(
availableFieldMetadataItemsForFilterFamilySelector.selectorFamily({
objectMetadataItemId: objectMetadataItem.id,
}),
@@ -30,7 +31,7 @@ export const useGetDefaultFieldMetadataItemForFilter = () => {
return { defaultFieldMetadataItemForFilter };
},
[],
[store],
);
return {
@@ -6,12 +6,13 @@ import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentSelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useSetAdvancedFilterDropdownStates = () => {
const store = useStore();
const rootLevelRecordFilterGroup = useRecoilComponentSelectorValueV2(
rootLevelRecordFilterGroupComponentSelector,
);
@@ -38,21 +39,21 @@ export const useSetAdvancedFilterDropdownStates = () => {
recordFilter.id,
);
jotaiStore.set(
store.set(
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId,
}),
recordFilter,
);
jotaiStore.set(
store.set(
fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({
instanceId,
}),
recordFilter.fieldMetadataId,
);
jotaiStore.set(
store.set(
subFieldNameUsedInDropdownComponentState.atomFamily({
instanceId,
}),
@@ -84,6 +85,7 @@ export const useSetAdvancedFilterDropdownStates = () => {
currentRecordFilterGroups,
currentRecordFilters,
rootLevelRecordFilterGroup,
store,
]);
return {
@@ -3,10 +3,11 @@ import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { useStore } from 'jotai';
export const useSetRecordFilterUsedInAdvancedFilterDropdownRow = () => {
const store = useStore();
const setRecordFilterUsedInAdvancedFilterDropdownRow = useCallback(
(recordFilter: RecordFilter) => {
const advancedFilterRowObjectFilterDropdownComponentInstanceId =
@@ -14,28 +15,28 @@ export const useSetRecordFilterUsedInAdvancedFilterDropdownRow = () => {
recordFilter.id,
);
jotaiStore.set(
store.set(
fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({
instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId,
}),
recordFilter.fieldMetadataId,
);
jotaiStore.set(
store.set(
selectedOperandInDropdownComponentState.atomFamily({
instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId,
}),
recordFilter.operand,
);
jotaiStore.set(
store.set(
objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({
instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId,
}),
recordFilter,
);
},
[],
[store],
);
return {
@@ -31,8 +31,8 @@ import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamily
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
import { useSetFamilyRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetFamilyRecoilStateV2';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export type UseFindManyRecordsParams<T> = ObjectMetadataItemIdentifier &
RecordGqlOperationVariables & {
@@ -81,6 +81,7 @@ export const useFetchMoreRecordsWithPagination = <
objectMetadataItem,
onCompleted,
}: UseFindManyRecordsStateParams<T>) => {
const store = useStore();
const queryIdentifier = getQueryIdentifier({
objectNameSingular,
filter,
@@ -105,11 +106,11 @@ export const useFetchMoreRecordsWithPagination = <
// TODO: put this into a util inspired from https://github.com/apollographql/apollo-client/blob/master/src/utilities/policies/pagination.ts
// This function is equivalent to merge function + read function in field policy
const fetchMoreRecords = useCallback(async () => {
const hasNextPageLocal = jotaiStore.get(
const hasNextPageLocal = store.get(
hasNextPageFamilyState.atomFamily(queryIdentifier),
);
const lastCursorLocal = jotaiStore.get(
const lastCursorLocal = store.get(
cursorFamilyState.atomFamily(queryIdentifier),
);
@@ -147,11 +148,11 @@ export const useFetchMoreRecordsWithPagination = <
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
if (isDefined(pageInfo)) {
jotaiStore.set(
store.set(
cursorFamilyState.atomFamily(queryIdentifier),
pageInfo.endCursor ?? '',
);
jotaiStore.set(
store.set(
hasNextPageFamilyState.atomFamily(queryIdentifier),
pageInfo.hasNextPage ?? false,
);
@@ -205,6 +206,7 @@ export const useFetchMoreRecordsWithPagination = <
onCompleted,
handleFindManyRecordsError,
queryIdentifier,
store,
]);
const totalCount = data?.[objectMetadataItem.namePlural]?.totalCount;
@@ -4,9 +4,9 @@ import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/t
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
import { type OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useHandleFindManyRecordsCompleted = <T>({
queryIdentifier,
@@ -17,6 +17,7 @@ export const useHandleFindManyRecordsCompleted = <T>({
objectMetadataItem: ObjectMetadataItem;
onCompleted?: OnFindManyRecordsCompleted<T>;
}) => {
const store = useStore();
const handleFindManyRecordsCompleted = useCallback(
(data: RecordGqlOperationFindManyResult) => {
const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo;
@@ -31,17 +32,17 @@ export const useHandleFindManyRecordsCompleted = <T>({
});
if (isDefined(data?.[objectMetadataItem.namePlural])) {
jotaiStore.set(
store.set(
cursorFamilyState.atomFamily(queryIdentifier),
pageInfo.endCursor ?? '',
);
jotaiStore.set(
store.set(
hasNextPageFamilyState.atomFamily(queryIdentifier),
pageInfo.hasNextPage ?? false,
);
}
},
[objectMetadataItem.namePlural, onCompleted, queryIdentifier],
[objectMetadataItem.namePlural, onCompleted, queryIdentifier, store],
);
return {
@@ -29,8 +29,8 @@ import {
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export type UseFindManyRecordsParams<T> = ObjectMetadataItemIdentifier &
RecordGqlOperationVariables & {
@@ -77,6 +77,7 @@ export const useLazyFetchMoreRecordsWithPagination = <
fetchMore,
objectMetadataItem,
}: UseFindManyRecordsStateParams<T>) => {
const store = useStore();
const queryIdentifier = getQueryIdentifier({
objectNameSingular,
filter,
@@ -92,11 +93,11 @@ export const useLazyFetchMoreRecordsWithPagination = <
// This function is equivalent to merge function + read function in field policy
const fetchMoreRecordsLazy = useCallback(
async (limit = DEFAULT_SEARCH_REQUEST_LIMIT) => {
const hasNextPageLocal = jotaiStore.get(
const hasNextPageLocal = store.get(
hasNextPageFamilyState.atomFamily(queryIdentifier),
);
const lastCursorLocal = jotaiStore.get(
const lastCursorLocal = store.get(
cursorFamilyState.atomFamily(queryIdentifier),
);
@@ -135,11 +136,11 @@ export const useLazyFetchMoreRecordsWithPagination = <
fetchMoreResult?.[objectMetadataItem.namePlural]?.pageInfo;
if (isDefined(pageInfo)) {
jotaiStore.set(
store.set(
cursorFamilyState.atomFamily(queryIdentifier),
pageInfo.endCursor ?? '',
);
jotaiStore.set(
store.set(
hasNextPageFamilyState.atomFamily(queryIdentifier),
pageInfo.hasNextPage ?? false,
);
@@ -188,6 +189,7 @@ export const useLazyFetchMoreRecordsWithPagination = <
filter,
orderBy,
handleFindManyRecordsError,
store,
],
);
@@ -13,10 +13,10 @@ import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPe
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
import { QUERY_DEFAULT_LIMIT_RECORDS } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
type UseLazyFindManyRecordsParams<T> = Omit<
UseFindManyRecordsParams<T>,
@@ -31,6 +31,7 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
recordGqlFields,
fetchPolicy = 'cache-first',
}: UseLazyFindManyRecordsParams<T>) => {
const store = useStore();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
@@ -83,8 +84,8 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const findManyRecordsLazy = useCallback(async () => {
if (!hasReadPermission) {
jotaiStore.set(hasNextPageFamilyState.atomFamily(queryIdentifier), false);
jotaiStore.set(cursorFamilyState.atomFamily(queryIdentifier), '');
store.set(hasNextPageFamilyState.atomFamily(queryIdentifier), false);
store.set(cursorFamilyState.atomFamily(queryIdentifier), '');
return {
data: null,
@@ -107,11 +108,8 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const lastCursor =
result?.data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor ?? '';
jotaiStore.set(
hasNextPageFamilyState.atomFamily(queryIdentifier),
hasNextPage,
);
jotaiStore.set(cursorFamilyState.atomFamily(queryIdentifier), lastCursor);
store.set(hasNextPageFamilyState.atomFamily(queryIdentifier), hasNextPage);
store.set(cursorFamilyState.atomFamily(queryIdentifier), lastCursor);
const records = getRecordsFromRecordConnection({
recordConnection: {
@@ -141,6 +139,7 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
objectMetadataItem.namePlural,
queryIdentifier,
handleFindManyRecordsError,
store,
]);
return {
@@ -18,12 +18,12 @@ import {
type RelativeDateFilter,
resolveDateFilter,
} from 'twenty-shared/utils';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { formatDateString } from '~/utils/string/formatDateString';
export const ObjectFilterDropdownDateInput = () => {
const { dateFormat, timeZone } = useContext(UserContext);
const dateLocale = useRecoilValueV2(dateLocaleStateV2);
const dateLocale = useRecoilValueV2(dateLocaleState);
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
const selectedOperandInDropdown = useRecoilComponentValueV2(
@@ -21,12 +21,12 @@ import {
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { isNonEmptyString } from '@sniptt/guards';
import { Temporal } from 'temporal-polyfill';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { formatDateTimeString } from '~/utils/string/formatDateTimeString';
export const ObjectFilterDropdownDateTimeInput = () => {
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
const dateLocale = useRecoilValueV2(dateLocaleStateV2);
const dateLocale = useRecoilValueV2(dateLocaleState);
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
const { userTimezone } = useUserTimezone();
@@ -5,18 +5,19 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { useCreateRecordFilterFromObjectFilterDropdownCurrentStates } from '@/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentSelectorCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentSelectorCallbackStateV2';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
export const useApplyObjectFilterDropdownFilterValue = () => {
const objectFilterDropdownCurrentRecordFilterAtom =
const store = useStore();
const objectFilterDropdownCurrentRecordFilter =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
const fieldMetadataItemUsedInDropdownAtom =
const fieldMetadataItemUsedInDropdown =
useRecoilComponentSelectorCallbackStateV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
@@ -29,20 +30,20 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
const applyObjectFilterDropdownFilterValue = useCallback(
(newFilterValue: string, newDisplayValue?: string) => {
const objectFilterDropdownCurrentRecordFilter = jotaiStore.get(
objectFilterDropdownCurrentRecordFilterAtom,
const existingObjectFilterDropdownCurrentRecordFilter = store.get(
objectFilterDropdownCurrentRecordFilter,
) as RecordFilter | undefined | null;
const fieldMetadataItemUsedInDropdown = jotaiStore.get(
fieldMetadataItemUsedInDropdownAtom,
const currentFieldMetadataItemUsedInDropdown = store.get(
fieldMetadataItemUsedInDropdown,
);
const objectFilterDropdownFilterNotYetCreated = !isDefined(
objectFilterDropdownCurrentRecordFilter,
existingObjectFilterDropdownCurrentRecordFilter,
);
if (objectFilterDropdownFilterNotYetCreated) {
if (!isDefined(fieldMetadataItemUsedInDropdown)) {
if (!isDefined(currentFieldMetadataItemUsedInDropdown)) {
throw new Error(
`Field metadata item is not defined in object filter dropdown when setting a filter value to create it, this should not happen.`,
);
@@ -60,7 +61,7 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter);
} else {
const newCurrentRecordFilter = {
...objectFilterDropdownCurrentRecordFilter,
...existingObjectFilterDropdownCurrentRecordFilter,
value: newFilterValue,
displayValue: newDisplayValue ?? newFilterValue,
} satisfies RecordFilter;
@@ -69,10 +70,11 @@ export const useApplyObjectFilterDropdownFilterValue = () => {
}
},
[
objectFilterDropdownCurrentRecordFilterAtom,
fieldMetadataItemUsedInDropdownAtom,
objectFilterDropdownCurrentRecordFilter,
fieldMetadataItemUsedInDropdown,
createRecordFilterFromObjectFilterDropdownCurrentStates,
upsertObjectFilterDropdownCurrentFilter,
store,
],
);
@@ -2,28 +2,30 @@ import { useCallback } from 'react';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useStore } from 'jotai';
export const useEmptyRecordFilter = (componentInstanceId?: string) => {
const objectFilterDropdownSearchInputAtom =
const store = useStore();
const objectFilterDropdownSearchInput =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownSearchInputComponentState,
componentInstanceId,
);
const objectFilterDropdownCurrentRecordFilterAtom =
const objectFilterDropdownCurrentRecordFilter =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
componentInstanceId,
);
const emptyRecordFilter = useCallback(() => {
jotaiStore.set(objectFilterDropdownSearchInputAtom, '');
jotaiStore.set(objectFilterDropdownCurrentRecordFilterAtom, undefined);
store.set(objectFilterDropdownSearchInput, '');
store.set(objectFilterDropdownCurrentRecordFilter, undefined);
}, [
objectFilterDropdownSearchInputAtom,
objectFilterDropdownCurrentRecordFilterAtom,
objectFilterDropdownSearchInput,
objectFilterDropdownCurrentRecordFilter,
store,
]);
return {
@@ -7,67 +7,69 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useStore } from 'jotai';
export const useResetFilterDropdown = (componentInstanceId?: string) => {
const objectFilterDropdownSearchInputAtom =
const store = useStore();
const objectFilterDropdownSearchInput =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownSearchInputComponentState,
componentInstanceId,
);
const fieldMetadataItemIdUsedInDropdownAtom =
const fieldMetadataItemIdUsedInDropdown =
useRecoilComponentStateCallbackStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
componentInstanceId,
);
const selectedOperandInDropdownAtom = useRecoilComponentStateCallbackStateV2(
const selectedOperandInDropdown = useRecoilComponentStateCallbackStateV2(
selectedOperandInDropdownComponentState,
componentInstanceId,
);
const objectFilterDropdownFilterIsSelectedAtom =
const objectFilterDropdownFilterIsSelected =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownFilterIsSelectedComponentState,
componentInstanceId,
);
const objectFilterDropdownAnyFieldSearchIsSelectedAtom =
const objectFilterDropdownAnyFieldSearchIsSelected =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownAnyFieldSearchIsSelectedComponentState,
componentInstanceId,
);
const objectFilterDropdownIsSelectingCompositeFieldAtom =
const objectFilterDropdownIsSelectingCompositeField =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownIsSelectingCompositeFieldComponentState,
componentInstanceId,
);
const objectFilterDropdownCurrentRecordFilterAtom =
const objectFilterDropdownCurrentRecordFilter =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
componentInstanceId,
);
const resetFilterDropdown = useCallback(() => {
jotaiStore.set(objectFilterDropdownSearchInputAtom, '');
jotaiStore.set(selectedOperandInDropdownAtom, null);
jotaiStore.set(objectFilterDropdownFilterIsSelectedAtom, false);
jotaiStore.set(objectFilterDropdownIsSelectingCompositeFieldAtom, false);
jotaiStore.set(fieldMetadataItemIdUsedInDropdownAtom, null);
jotaiStore.set(objectFilterDropdownCurrentRecordFilterAtom, null);
jotaiStore.set(objectFilterDropdownAnyFieldSearchIsSelectedAtom, false);
store.set(objectFilterDropdownSearchInput, '');
store.set(selectedOperandInDropdown, null);
store.set(objectFilterDropdownFilterIsSelected, false);
store.set(objectFilterDropdownIsSelectingCompositeField, false);
store.set(fieldMetadataItemIdUsedInDropdown, null);
store.set(objectFilterDropdownCurrentRecordFilter, null);
store.set(objectFilterDropdownAnyFieldSearchIsSelected, false);
}, [
objectFilterDropdownSearchInputAtom,
selectedOperandInDropdownAtom,
objectFilterDropdownFilterIsSelectedAtom,
objectFilterDropdownIsSelectingCompositeFieldAtom,
fieldMetadataItemIdUsedInDropdownAtom,
objectFilterDropdownCurrentRecordFilterAtom,
objectFilterDropdownAnyFieldSearchIsSelectedAtom,
objectFilterDropdownSearchInput,
selectedOperandInDropdown,
objectFilterDropdownFilterIsSelected,
objectFilterDropdownIsSelectingCompositeField,
fieldMetadataItemIdUsedInDropdown,
objectFilterDropdownCurrentRecordFilter,
objectFilterDropdownAnyFieldSearchIsSelected,
store,
]);
return {
@@ -3,11 +3,12 @@ import { useCallback } from 'react';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
import { useStore } from 'jotai';
export const useUpsertObjectFilterDropdownCurrentFilter = () => {
const objectFilterDropdownCurrentRecordFilterAtom =
const store = useStore();
const objectFilterDropdownCurrentRecordFilter =
useRecoilComponentStateCallbackStateV2(
objectFilterDropdownCurrentRecordFilterComponentState,
);
@@ -18,12 +19,9 @@ export const useUpsertObjectFilterDropdownCurrentFilter = () => {
(recordFilterToUpsert: RecordFilter) => {
upsertRecordFilter(recordFilterToUpsert);
jotaiStore.set(
objectFilterDropdownCurrentRecordFilterAtom,
recordFilterToUpsert,
);
store.set(objectFilterDropdownCurrentRecordFilter, recordFilterToUpsert);
},
[objectFilterDropdownCurrentRecordFilterAtom, upsertRecordFilter],
[objectFilterDropdownCurrentRecordFilter, upsertRecordFilter, store],
);
return {
@@ -1,24 +1,40 @@
import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField';
import { objectOptionsDropdownSearchInputComponentState } from '@/object-record/object-options-dropdown/states/objectOptionsDropdownSearchInputComponentState';
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { renderHook } from '@testing-library/react';
import { Provider as JotaiProvider } from 'jotai';
import { act } from 'react';
import { RecoilRoot } from 'recoil';
import { FieldMetadataType } from '~/generated-metadata/graphql';
const INSTANCE_ID = 'myViewInstanceId';
describe('useSearchRecordGroupField', () => {
beforeEach(() => {
jotaiStore.set(
objectOptionsDropdownSearchInputComponentState.atomFamily({
instanceId: INSTANCE_ID,
}),
'',
);
});
const renderWithContext = (contextValue: any) =>
renderHook(() => useSearchRecordGroupField(), {
wrapper: ({ children }) => (
<RecoilRoot>
<RecordIndexContextProvider value={contextValue}>
<ViewComponentInstanceContext.Provider
value={{ instanceId: 'myViewInstanceId' }}
>
{children}
</ViewComponentInstanceContext.Provider>
</RecordIndexContextProvider>
</RecoilRoot>
<JotaiProvider store={jotaiStore}>
<RecoilRoot>
<RecordIndexContextProvider value={contextValue}>
<ViewComponentInstanceContext.Provider
value={{ instanceId: INSTANCE_ID }}
>
{children}
</ViewComponentInstanceContext.Provider>
</RecordIndexContextProvider>
</RecoilRoot>
</JotaiProvider>
),
});
@@ -4,7 +4,6 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates';
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView';
import { coreViewsState } from '@/views/states/coreViewState';
import { type GraphQLView } from '@/views/types/GraphQLView';
@@ -40,7 +39,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
}),
);
const existingCoreViews = jotaiStore.get(coreViewsState.atom);
const existingCoreViews = store.get(coreViewsState.atom);
if (!isDefined(currentViewId)) {
throw new Error('No view id found');
@@ -75,7 +74,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
}
setRecordIndexViewType(viewType);
jotaiStore.set(coreViewsState.atom, [
store.set(coreViewsState.atom, [
...existingCoreViews.filter(
(coreView) => coreView.id !== currentView.id,
),
@@ -96,7 +95,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
updateCurrentViewParams.mainGroupByFieldMetadataId = null;
await updateCurrentView(updateCurrentViewParams);
setRecordIndexViewType(viewType);
jotaiStore.set(coreViewsState.atom, [
store.set(coreViewsState.atom, [
...existingCoreViews.filter(
(coreView) => coreView.id !== currentView.id,
),
@@ -116,7 +115,7 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
const calendarFieldMetadataId = availableFieldsForCalendar[0].id;
setRecordIndexViewType(viewType);
jotaiStore.set(coreViewsState.atom, [
store.set(coreViewsState.atom, [
...existingCoreViews.filter(
(coreView) => coreView.id !== currentView.id,
),
@@ -28,7 +28,7 @@ export const RecordBoardDataChangedEffect = () => {
useRecoilComponentFamilySelectorCallbackStateV2(
recordGroupFromGroupValueComponentFamilySelector,
);
const recordIndexGroupFieldMetadataItemAtom =
const recordIndexGroupFieldMetadataItem =
useRecoilComponentStateCallbackStateV2(
recordIndexGroupFieldMetadataItemComponentState,
);
@@ -73,17 +73,17 @@ export const RecordBoardDataChangedEffect = () => {
return;
}
const recordIndexGroupFieldMetadataItem = store.get(
recordIndexGroupFieldMetadataItemAtom,
const currentRecordIndexGroupFieldMetadataItem = store.get(
recordIndexGroupFieldMetadataItem,
);
if (!isDefined(recordIndexGroupFieldMetadataItem)) {
if (!isDefined(currentRecordIndexGroupFieldMetadataItem)) {
return;
}
const recordGroupValue =
objectRecordOperation.createdRecord[
recordIndexGroupFieldMetadataItem.name
currentRecordIndexGroupFieldMetadataItem.name
];
const recordGroupDefinitionFromGroupValue = store.get(
@@ -157,7 +157,7 @@ export const RecordBoardDataChangedEffect = () => {
store,
triggerRecordBoardInitialQuery,
getShouldInitializeRecordBoardForUpdateInputs,
recordIndexGroupFieldMetadataItemAtom,
recordIndexGroupFieldMetadataItem,
recordGroupFromGroupValueCallbackState,
recordIndexRecordIdsByGroupCallbackState,
removeRecordsFromBoard,
@@ -23,12 +23,12 @@ export const RecordBoardDragDropContext = ({
}: React.PropsWithChildren) => {
const { recordBoardId } = useContext(RecordBoardContext);
const currentRecordSortsAtom = useRecoilComponentStateCallbackStateV2(
const currentRecordSorts = useRecoilComponentStateCallbackStateV2(
currentRecordSortsComponentState,
recordBoardId,
);
const recordBoardSelectedRecordIdsAtom =
const recordBoardSelectedRecordIds =
useRecoilComponentSelectorCallbackStateV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardId,
@@ -36,7 +36,7 @@ export const RecordBoardDragDropContext = ({
const store = useStore();
const originalDragSelectionAtom = useRecoilComponentStateCallbackStateV2(
const originalDragSelection = useRecoilComponentStateCallbackStateV2(
originalDragSelectionComponentState,
recordBoardId,
);
@@ -50,13 +50,11 @@ export const RecordBoardDragDropContext = ({
const handleDragStart = useCallback(
(start: DragStart) => {
const currentSelectedRecordIds = store.get(
recordBoardSelectedRecordIdsAtom,
);
const currentSelectedRecordIds = store.get(recordBoardSelectedRecordIds);
startRecordDrag(start, currentSelectedRecordIds);
},
[recordBoardSelectedRecordIdsAtom, startRecordDrag, store],
[recordBoardSelectedRecordIds, startRecordDrag, store],
);
const handleDragEnd: OnDragEndResponder = useCallback(
@@ -65,24 +63,22 @@ export const RecordBoardDragDropContext = ({
if (!result.destination) return;
const currentRecordSorts = store.get(currentRecordSortsAtom);
const existingRecordSorts = store.get(currentRecordSorts);
if (currentRecordSorts.length > 0) {
if (existingRecordSorts.length > 0) {
openModal(RECORD_INDEX_REMOVE_SORTING_MODAL_ID);
return;
}
const originalSelection = store.get(
originalDragSelectionAtom,
) as string[];
const originalSelection = store.get(originalDragSelection) as string[];
processBoardCardDrop(result, originalSelection);
},
[
processBoardCardDrop,
originalDragSelectionAtom,
originalDragSelection,
endRecordDrag,
currentRecordSortsAtom,
currentRecordSorts,
openModal,
store,
],
@@ -22,10 +22,7 @@ export const useActiveRecordBoardCard = (recordBoardId?: string) => {
const store = useStore();
const deactivateBoardCard = useCallback(() => {
const activeBoardCardIndexes = store.get(activeBoardCardIndexesState) as
| BoardCardIndexes
| null
| undefined;
const activeBoardCardIndexes = store.get(activeBoardCardIndexesState);
if (!isDefined(activeBoardCardIndexes)) {
return;
@@ -37,10 +34,7 @@ export const useActiveRecordBoardCard = (recordBoardId?: string) => {
const activateBoardCard = useCallback(
(boardCardIndexes: BoardCardIndexes) => {
const activeBoardCardIndexes = store.get(activeBoardCardIndexesState) as
| BoardCardIndexes
| null
| undefined;
const activeBoardCardIndexes = store.get(activeBoardCardIndexesState);
if (
activeBoardCardIndexes?.rowIndex === boardCardIndexes.rowIndex &&
@@ -14,17 +14,17 @@ import { isDefined } from 'twenty-shared/utils';
export const useFocusedRecordBoardCard = (recordBoardId?: string) => {
const store = useStore();
const isCardFocusedAtom = useRecoilComponentFamilyStateCallbackStateV2(
const isCardFocused = useRecoilComponentFamilyStateCallbackStateV2(
isRecordBoardCardFocusedComponentFamilyState,
recordBoardId,
);
const focusedBoardCardIndexesAtom = useRecoilComponentStateCallbackStateV2(
const focusedBoardCardIndexes = useRecoilComponentStateCallbackStateV2(
focusedRecordBoardCardIndexesComponentState,
recordBoardId,
);
const isCardFocusActiveAtom = useRecoilComponentStateCallbackStateV2(
const isCardFocusActive = useRecoilComponentStateCallbackStateV2(
isRecordBoardCardFocusActiveComponentState,
recordBoardId,
);
@@ -34,53 +34,49 @@ export const useFocusedRecordBoardCard = (recordBoardId?: string) => {
useRemoveFocusItemFromFocusStackById();
const unfocusBoardCard = useCallback(() => {
const focusedBoardCardIndexes = store.get(focusedBoardCardIndexesAtom) as
| BoardCardIndexes
| null
| undefined;
const currentFocusedBoardCardIndexes = store.get(focusedBoardCardIndexes);
if (!isDefined(focusedBoardCardIndexes)) {
if (!isDefined(currentFocusedBoardCardIndexes)) {
return;
}
const focusId = getRecordBoardCardFocusId({
recordBoardId: recordBoardId || '',
cardIndexes: focusedBoardCardIndexes,
cardIndexes: currentFocusedBoardCardIndexes,
});
removeFocusItemFromFocusStackById({
focusId,
});
store.set(focusedBoardCardIndexesAtom, null);
store.set(isCardFocusedAtom(focusedBoardCardIndexes), false);
store.set(isCardFocusActiveAtom, false);
store.set(focusedBoardCardIndexes, null);
store.set(isCardFocused(currentFocusedBoardCardIndexes), false);
store.set(isCardFocusActive, false);
}, [
store,
focusedBoardCardIndexesAtom,
isCardFocusedAtom,
isCardFocusActiveAtom,
focusedBoardCardIndexes,
isCardFocused,
isCardFocusActive,
recordBoardId,
removeFocusItemFromFocusStackById,
]);
const focusBoardCard = useCallback(
(boardCardIndexes: BoardCardIndexes) => {
const focusedBoardCardIndexes = store.get(focusedBoardCardIndexesAtom) as
| BoardCardIndexes
| null
| undefined;
const currentFocusedBoardCardIndexes = store.get(focusedBoardCardIndexes);
if (
isDefined(focusedBoardCardIndexes) &&
(focusedBoardCardIndexes.rowIndex !== boardCardIndexes.rowIndex ||
focusedBoardCardIndexes.columnIndex !== boardCardIndexes.columnIndex)
isDefined(currentFocusedBoardCardIndexes) &&
(currentFocusedBoardCardIndexes.rowIndex !==
boardCardIndexes.rowIndex ||
currentFocusedBoardCardIndexes.columnIndex !==
boardCardIndexes.columnIndex)
) {
store.set(isCardFocusedAtom(focusedBoardCardIndexes), false);
store.set(isCardFocused(currentFocusedBoardCardIndexes), false);
const currentFocusId = getRecordBoardCardFocusId({
recordBoardId: recordBoardId || '',
cardIndexes: focusedBoardCardIndexes,
cardIndexes: currentFocusedBoardCardIndexes,
});
removeFocusItemFromFocusStackById({
@@ -101,15 +97,15 @@ export const useFocusedRecordBoardCard = (recordBoardId?: string) => {
},
});
store.set(focusedBoardCardIndexesAtom, boardCardIndexes);
store.set(isCardFocusedAtom(boardCardIndexes), true);
store.set(isCardFocusActiveAtom, true);
store.set(focusedBoardCardIndexes, boardCardIndexes);
store.set(isCardFocused(boardCardIndexes), true);
store.set(isCardFocusActive, true);
},
[
store,
focusedBoardCardIndexesAtom,
isCardFocusedAtom,
isCardFocusActiveAtom,
focusedBoardCardIndexes,
isCardFocused,
isCardFocusActive,
recordBoardId,
pushFocusItemToFocusStack,
removeFocusItemFromFocusStackById,
@@ -17,7 +17,7 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
const store = useStore();
const { focusBoardCard } = useFocusedRecordBoardCard(recordBoardId);
const focusedBoardCardIndexesAtom = useRecoilComponentStateCallbackStateV2(
const focusedBoardCardIndexes = useRecoilComponentStateCallbackStateV2(
focusedRecordBoardCardIndexesComponentState,
recordBoardId,
);
@@ -38,9 +38,7 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
const firstColumnWithRecords = visibleRecordGroupIds.findIndex(
(groupId) => {
const recordIdsInGroup = store.get(
recordIdsByGroupState(groupId),
) as string[];
const recordIdsInGroup = store.get(recordIdsByGroupState(groupId));
return Array.isArray(recordIdsInGroup) && recordIdsInGroup.length > 0;
},
);
@@ -55,12 +53,9 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
const moveHorizontally = useCallback(
(direction: 'left' | 'right') => {
const focusedBoardCardIndexes = store.get(focusedBoardCardIndexesAtom) as
| { rowIndex: number; columnIndex: number }
| null
| undefined;
const currentFocusedBoardCardIndexes = store.get(focusedBoardCardIndexes);
if (!isDefined(focusedBoardCardIndexes)) {
if (!isDefined(currentFocusedBoardCardIndexes)) {
focusFirstAvailableRecord();
return;
}
@@ -71,8 +66,8 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
let newColumnIndex =
direction === 'right'
? focusedBoardCardIndexes.columnIndex + 1
: focusedBoardCardIndexes.columnIndex - 1;
? currentFocusedBoardCardIndexes.columnIndex + 1
: currentFocusedBoardCardIndexes.columnIndex - 1;
if (newColumnIndex < 0) {
newColumnIndex = 0;
@@ -80,7 +75,7 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
newColumnIndex = visibleRecordGroupIds.length - 1;
}
if (newColumnIndex === focusedBoardCardIndexes.columnIndex) {
if (newColumnIndex === currentFocusedBoardCardIndexes.columnIndex) {
return;
}
@@ -91,7 +86,7 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
const currentGroupId = visibleRecordGroupIds[newColumnIndex];
const recordIdsInGroup = store.get(
recordIdsByGroupState(currentGroupId),
) as string[];
);
if (Array.isArray(recordIdsInGroup) && recordIdsInGroup.length > 0) {
foundColumnWithRecords = true;
@@ -117,11 +112,9 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
}
const currentGroupId = visibleRecordGroupIds[newColumnIndex];
const recordIdsInGroup = store.get(
recordIdsByGroupState(currentGroupId),
) as string[];
const recordIdsInGroup = store.get(recordIdsByGroupState(currentGroupId));
let newRowIndex = focusedBoardCardIndexes.rowIndex;
let newRowIndex = currentFocusedBoardCardIndexes.rowIndex;
if (newRowIndex >= recordIdsInGroup.length) {
newRowIndex = recordIdsInGroup.length - 1;
}
@@ -133,7 +126,7 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
},
[
store,
focusedBoardCardIndexesAtom,
focusedBoardCardIndexes,
visibleRecordGroupIds,
recordIdsByGroupState,
focusBoardCard,
@@ -143,12 +136,9 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
const moveVertically = useCallback(
(direction: 'up' | 'down') => {
const focusedBoardCardIndexes = store.get(focusedBoardCardIndexesAtom) as
| { rowIndex: number; columnIndex: number }
| null
| undefined;
const currentFocusedBoardCardIndexes = store.get(focusedBoardCardIndexes);
if (!isDefined(focusedBoardCardIndexes)) {
if (!isDefined(currentFocusedBoardCardIndexes)) {
focusFirstAvailableRecord();
return;
}
@@ -156,10 +146,8 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
if (visibleRecordGroupIds.length === 0) return;
const currentGroupId =
visibleRecordGroupIds[focusedBoardCardIndexes.columnIndex];
const recordIdsInGroup = store.get(
recordIdsByGroupState(currentGroupId),
) as string[];
visibleRecordGroupIds[currentFocusedBoardCardIndexes.columnIndex];
const recordIdsInGroup = store.get(recordIdsByGroupState(currentGroupId));
if (!Array.isArray(recordIdsInGroup) || recordIdsInGroup.length === 0) {
focusFirstAvailableRecord();
@@ -168,8 +156,8 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
let newRowIndex =
direction === 'down'
? focusedBoardCardIndexes.rowIndex + 1
: focusedBoardCardIndexes.rowIndex - 1;
? currentFocusedBoardCardIndexes.rowIndex + 1
: currentFocusedBoardCardIndexes.rowIndex - 1;
if (newRowIndex < 0) {
newRowIndex = 0;
@@ -177,18 +165,18 @@ export const useRecordBoardCardNavigation = (recordBoardId?: string) => {
newRowIndex = recordIdsInGroup.length - 1;
}
if (newRowIndex === focusedBoardCardIndexes.rowIndex) {
if (newRowIndex === currentFocusedBoardCardIndexes.rowIndex) {
return;
}
focusBoardCard({
columnIndex: focusedBoardCardIndexes.columnIndex,
columnIndex: currentFocusedBoardCardIndexes.columnIndex,
rowIndex: newRowIndex,
});
},
[
store,
focusedBoardCardIndexesAtom,
focusedBoardCardIndexes,
visibleRecordGroupIds,
recordIdsByGroupState,
focusBoardCard,
@@ -22,7 +22,7 @@ export const useRecordBoardSelection = (recordBoardId?: string) => {
recordBoardId,
);
const recordBoardSelectedRecordIdsAtom =
const recordBoardSelectedRecordIds =
useRecoilComponentSelectorCallbackStateV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardId,
@@ -50,12 +50,12 @@ export const useRecordBoardSelection = (recordBoardId?: string) => {
);
const checkIfLastUnselectAndCloseDropdown = useCallback(() => {
const recordIds = store.get(recordBoardSelectedRecordIdsAtom);
const recordIds = store.get(recordBoardSelectedRecordIds);
if (recordIds.length === 0) {
closeDropdown(dropdownId);
}
}, [recordBoardSelectedRecordIdsAtom, store, closeDropdown, dropdownId]);
}, [recordBoardSelectedRecordIds, store, closeDropdown, dropdownId]);
return {
setRecordAsSelected,
@@ -1,4 +1,3 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { recordGroupFromGroupValueComponentFamilySelector } from '@/object-record/record-group/states/selectors/recordGroupFromGroupValueComponentFamilySelector';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
@@ -13,7 +12,7 @@ import { isDefined } from 'twenty-shared/utils';
export const useRemoveRecordsFromBoard = () => {
const store = useStore();
const recordIndexGroupFieldMetadataItemAtom =
const recordIndexGroupFieldMetadataItem =
useRecoilComponentStateCallbackStateV2(
recordIndexGroupFieldMetadataItemComponentState,
);
@@ -35,22 +34,22 @@ export const useRemoveRecordsFromBoard = () => {
for (const recordIdToRemove of recordIdsToRemove) {
const recordToRemove = store.get(
recordStoreFamilyState.atomFamily(recordIdToRemove),
) as Record<string, unknown> | null | undefined;
);
if (!isDefined(recordToRemove)) {
continue;
}
const recordIndexGroupFieldMetadataItem = store.get(
recordIndexGroupFieldMetadataItemAtom,
) as FieldMetadataItem | undefined;
const currentRecordIndexGroupFieldMetadataItem = store.get(
recordIndexGroupFieldMetadataItem,
);
if (!isDefined(recordIndexGroupFieldMetadataItem)) {
if (!isDefined(currentRecordIndexGroupFieldMetadataItem)) {
continue;
}
const recordGroupValue = recordToRemove[
recordIndexGroupFieldMetadataItem.name
currentRecordIndexGroupFieldMetadataItem.name
] as string | undefined;
const recordGroupDefinitionFromGroupValue = store.get(
@@ -76,7 +75,7 @@ export const useRemoveRecordsFromBoard = () => {
] of recordIdsToRemoveByGroup) {
const currentRecordIdsForGroup = store.get(
recordIndexRecordIdsByGroupFamilyCallbackState(groupId),
) as string[];
);
const recordIdsWithoutRemovedRecords = currentRecordIdsForGroup.filter(
(recordId) => !recordIdsToRemoveInGroup.includes(recordId),
@@ -90,7 +89,7 @@ export const useRemoveRecordsFromBoard = () => {
},
[
store,
recordIndexGroupFieldMetadataItemAtom,
recordIndexGroupFieldMetadataItem,
recordGroupFromGroupValueFamilyCallbackState,
recordIndexRecordIdsByGroupFamilyCallbackState,
],
@@ -29,9 +29,10 @@ export const useRemoveSelectedRecordsFromRecordBoard = (
const recordIndexRecordIdsByGroupCallbackState =
useRecoilComponentFamilyStateCallbackStateV2(
recordIndexRecordIdsByGroupComponentFamilyState,
recordBoardIndexId,
);
const recordBoardSelectedRecordIdsAtom =
const recordBoardSelectedRecordIds =
useRecoilComponentSelectorCallbackStateV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardIndexId,
@@ -42,7 +43,7 @@ export const useRemoveSelectedRecordsFromRecordBoard = (
const removeSelectedRecordsFromRecordBoard = useCallback(() => {
const deletedRecordIds = store.get(
recordBoardSelectedRecordIdsAtom,
recordBoardSelectedRecordIds,
) as string[];
if (
@@ -89,7 +90,7 @@ export const useRemoveSelectedRecordsFromRecordBoard = (
groupByFieldMetadataItem,
recordIndexRecordIdsByGroupCallbackState,
recordGroupDefinitions,
recordBoardSelectedRecordIdsAtom,
recordBoardSelectedRecordIds,
resetRecordBoardSelection,
]);
@@ -22,7 +22,7 @@ export const useResetRecordBoardSelection = (recordBoardId?: string) => {
recordBoardId,
);
const recordBoardSelectedRecordIdsAtom =
const recordBoardSelectedRecordIds =
useRecoilComponentSelectorCallbackStateV2(
recordBoardSelectedRecordIdsComponentSelector,
recordBoardId,
@@ -38,7 +38,7 @@ export const useResetRecordBoardSelection = (recordBoardId?: string) => {
const resetRecordBoardSelection = useCallback(() => {
closeDropdown(dropdownId);
const recordIds = store.get(recordBoardSelectedRecordIdsAtom);
const recordIds = store.get(recordBoardSelectedRecordIds);
for (const recordId of recordIds) {
store.set(isRecordBoardCardSelectedFamilyState(recordId), false);
@@ -46,7 +46,7 @@ export const useResetRecordBoardSelection = (recordBoardId?: string) => {
}, [
closeDropdown,
dropdownId,
recordBoardSelectedRecordIdsAtom,
recordBoardSelectedRecordIds,
isRecordBoardCardSelectedFamilyState,
store,
]);
@@ -8,7 +8,7 @@ import { useStore } from 'jotai';
import { useCallback } from 'react';
export const useSelectAllCards = (recordBoardId?: string) => {
const allCardsSelectedStatusAtom = useRecoilComponentSelectorCallbackStateV2(
const allCardsSelectedStatus = useRecoilComponentSelectorCallbackStateV2(
allCardsSelectedStatusComponentSelector,
recordBoardId,
);
@@ -17,7 +17,7 @@ export const useSelectAllCards = (recordBoardId?: string) => {
isRecordBoardCardSelectedComponentFamilyState,
recordBoardId,
);
const allRecordIdsOfAllRecordGroupsAtom =
const allRecordIdsOfAllRecordGroups =
useRecoilComponentSelectorCallbackStateV2(
allRecordIdsOfAllRecordGroupsComponentSelector,
recordBoardId,
@@ -29,22 +29,23 @@ export const useSelectAllCards = (recordBoardId?: string) => {
const store = useStore();
const selectAllCards = useCallback(() => {
const allCardsSelectedStatus = store.get(allCardsSelectedStatusAtom);
const allRecordIds = store.get(allRecordIdsOfAllRecordGroupsAtom);
const currentAllCardsSelectedStatus = store.get(allCardsSelectedStatus);
const allRecordIds = store.get(allRecordIdsOfAllRecordGroups);
if (allCardsSelectedStatus === 'all') {
if (currentAllCardsSelectedStatus === 'all') {
resetRecordBoardSelection();
}
for (const recordId of allRecordIds) {
const isSelected =
allCardsSelectedStatus === 'none' || allCardsSelectedStatus === 'some';
currentAllCardsSelectedStatus === 'none' ||
currentAllCardsSelectedStatus === 'some';
store.set(isRecordBoardCardSelectedFamilyState(recordId), isSelected);
}
}, [
allCardsSelectedStatusAtom,
allRecordIdsOfAllRecordGroupsAtom,
allCardsSelectedStatus,
allRecordIdsOfAllRecordGroups,
resetRecordBoardSelection,
isRecordBoardCardSelectedFamilyState,
store,
@@ -14,7 +14,7 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
const store = useStore();
const recordGroupFieldMetadataAtom = useRecoilComponentStateCallbackStateV2(
const recordGroupFieldMetadata = useRecoilComponentStateCallbackStateV2(
recordIndexGroupFieldMetadataItemComponentState,
recordBoardId,
);
@@ -40,16 +40,18 @@ export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
recordIndexRecordIdsByGroupFamilyState(currentRecordGroupId),
);
const recordGroupFieldMetadata = store.get(recordGroupFieldMetadataAtom);
const currentRecordGroupFieldMetadata = store.get(
recordGroupFieldMetadata,
);
if (!isDefined(recordGroupFieldMetadata)) {
if (!isDefined(currentRecordGroupFieldMetadata)) {
return;
}
const recordGroupRowIds = records
.filter(
(record) =>
record[recordGroupFieldMetadata.name] === recordGroup?.value,
record[currentRecordGroupFieldMetadata.name] === recordGroup?.value,
)
.map((record) => record.id);
@@ -75,7 +77,7 @@ export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
},
[
recordIndexRecordIdsByGroupFamilyState,
recordGroupFieldMetadataAtom,
recordGroupFieldMetadata,
emptyRecordGroupByIdCallbackState,
store,
],
@@ -62,22 +62,22 @@ export const useTriggerRecordBoardFetchMore = () => {
recordIndexRecordIdsByGroupComponentFamilyState,
);
const recordBoardIsFetchingMoreAtom = useRecoilComponentStateCallbackStateV2(
const recordBoardIsFetchingMore = useRecoilComponentStateCallbackStateV2(
recordBoardIsFetchingMoreComponentState,
);
const triggerRecordBoardFetchMore = useCallback(async () => {
const isAlreadyFetchingMore = store.get(recordBoardIsFetchingMoreAtom);
const isAlreadyFetchingMore = store.get(recordBoardIsFetchingMore);
const cleanStateBeforeExit = () => {
store.set(recordBoardIsFetchingMoreAtom, false);
store.set(recordBoardIsFetchingMore, false);
};
if (isAlreadyFetchingMore) {
return;
}
store.set(recordBoardIsFetchingMoreAtom, true);
store.set(recordBoardIsFetchingMore, true);
const currentOffset = store.get(
recordBoardCurrentGroupByQueryOffsetCallbackState,
@@ -214,7 +214,7 @@ export const useTriggerRecordBoardFetchMore = () => {
upsertRecordsInStore,
executeRecordIndexGroupsRecordsLazyGroupBy,
recordIndexRecordIdsByGroupCallbackState,
recordBoardIsFetchingMoreAtom,
recordBoardIsFetchingMore,
recordBoardCurrentGroupByQueryOffsetCallbackState,
recordBoardShouldFetchMoreInColumnFamilyCallbackState,
combinedFilters,
@@ -44,7 +44,7 @@ export const useTriggerRecordBoardInitialQuery = () => {
recordBoardShouldFetchMoreInColumnComponentFamilyState,
);
const recordIndexRecordGroupsAreInInitialLoadingAtom =
const recordIndexRecordGroupsAreInInitialLoading =
useRecoilComponentStateCallbackStateV2(
recordIndexRecordGroupsAreInInitialLoadingComponentState,
);
@@ -76,10 +76,10 @@ export const useTriggerRecordBoardInitialQuery = () => {
});
const triggerRecordBoardInitialQuery = useCallback(async () => {
store.set(recordIndexRecordGroupsAreInInitialLoadingAtom, true);
store.set(recordIndexRecordGroupsAreInInitialLoading, true);
const cleanStateBeforeExit = () => {
store.set(recordIndexRecordGroupsAreInInitialLoadingAtom, false);
store.set(recordIndexRecordGroupsAreInInitialLoading, false);
setLastRecordBoardQueryIdentifier(queryIdentifier);
@@ -165,7 +165,7 @@ export const useTriggerRecordBoardInitialQuery = () => {
cleanStateBeforeExit();
}, [
recordIndexRecordGroupsAreInInitialLoadingAtom,
recordIndexRecordGroupsAreInInitialLoading,
store,
executeRecordIndexGroupsRecordsLazyGroupBy,
objectMetadataItem,
@@ -16,7 +16,7 @@ import {
turnJSDateToPlainDate,
turnPlainDateToShiftedDateInSystemTimeZone,
} from 'twenty-shared/utils';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
// TODO: we could refactor this to use Temporal.PlainDate directly
// But it would require recoding the utils here, not really worth it for now
@@ -24,7 +24,7 @@ export const useRecordCalendarMonthDaysRange = (
selectedDate: Temporal.PlainDate,
) => {
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
const dateLocale = useRecoilValueV2(dateLocaleStateV2);
const dateLocale = useRecoilValueV2(dateLocaleState);
if (!currentWorkspaceMember) {
throw new Error('Current workspace member not found');
@@ -23,7 +23,7 @@ export const useRecordCalendarSelection = (recordCalendarId?: string) => {
recordCalendarId,
);
const recordCalendarSelectedRecordIdsAtom =
const recordCalendarSelectedRecordIds =
useRecoilComponentSelectorCallbackStateV2(
recordCalendarSelectedRecordIdsComponentSelector,
recordCalendarId,
@@ -39,7 +39,7 @@ export const useRecordCalendarSelection = (recordCalendarId?: string) => {
const resetRecordSelection = useCallback(() => {
closeDropdown(dropdownId);
const recordIds = store.get(recordCalendarSelectedRecordIdsAtom);
const recordIds = store.get(recordCalendarSelectedRecordIds);
for (const recordId of recordIds) {
store.set(isRecordCalendarCardSelectedFamilyState(recordId), false);
@@ -47,7 +47,7 @@ export const useRecordCalendarSelection = (recordCalendarId?: string) => {
}, [
closeDropdown,
dropdownId,
recordCalendarSelectedRecordIdsAtom,
recordCalendarSelectedRecordIds,
isRecordCalendarCardSelectedFamilyState,
store,
]);
@@ -32,7 +32,7 @@ export const useEndRecordDrag = (contextStoreInstanceId?: string) => {
contextStoreInstanceId,
);
const originalSelectionAtom = useRecoilComponentStateCallbackStateV2(
const originalSelection = useRecoilComponentStateCallbackStateV2(
originalDragSelectionComponentState,
contextStoreInstanceId,
);
@@ -49,13 +49,13 @@ export const useEndRecordDrag = (contextStoreInstanceId?: string) => {
contextStoreInstanceId,
);
const isDraggingRecordAtom = useRecoilComponentStateCallbackStateV2(
const isDraggingRecord = useRecoilComponentStateCallbackStateV2(
isDraggingRecordComponentState,
contextStoreInstanceId,
);
const endRecordDrag = useCallback(() => {
store.set(isDraggingRecordAtom, false);
store.set(isDraggingRecord, false);
const currentlyDraggedRecordIds = store.get(draggedRecordIdsCallbackState);
@@ -90,16 +90,16 @@ export const useEndRecordDrag = (contextStoreInstanceId?: string) => {
store.set(isMultiDragActiveCallbackState, false);
store.set(draggedRecordIdsCallbackState, []);
store.set(primaryDraggedRecordIdCallbackState, null);
store.set(originalSelectionAtom, []);
store.set(originalSelection, []);
}, [
store,
isMultiDragActiveCallbackState,
draggedRecordIdsCallbackState,
primaryDraggedRecordIdCallbackState,
originalSelectionAtom,
originalSelection,
isRecordIdPrimaryDragMultipleCallbackState,
isRecordIdSecondaryDragMultipleCallbackState,
isDraggingRecordAtom,
isDraggingRecord,
]);
return { endRecordDrag };
@@ -43,10 +43,7 @@ export const useProcessCalendarCardDrop = () => {
const destinationPlainDate = Temporal.PlainDate.from(destinationDate);
const record = store.get(recordStoreFamilyState.atomFamily(recordId)) as
| Record<string, unknown>
| null
| undefined;
const record = store.get(recordStoreFamilyState.atomFamily(recordId));
if (!record) return;
@@ -61,7 +58,7 @@ export const useProcessCalendarCardDrop = () => {
day: destinationPlainDate,
timeZone: userTimezone,
}),
) as string[];
);
const targetDayIsEmpty = destinationRecordIds.length === 0;
@@ -36,22 +36,22 @@ export const useProcessTableWithGroupRecordDrop = () => {
recordIndexRecordIdsByGroupComponentFamilyState,
);
const currentRecordSortsAtom = useRecoilComponentStateCallbackStateV2(
const currentRecordSorts = useRecoilComponentStateCallbackStateV2(
currentRecordSortsComponentState,
recordTableId,
);
const selectedRowIdsAtom = useRecoilComponentSelectorCallbackStateV2(
const selectedRowIds = useRecoilComponentSelectorCallbackStateV2(
selectedRowIdsComponentSelector,
recordTableId,
);
const isDraggingRecordAtom = useRecoilComponentStateCallbackStateV2(
const isDraggingRecord = useRecoilComponentStateCallbackStateV2(
isDraggingRecordComponentState,
recordIndexId,
);
const originalDragSelectionAtom = useRecoilComponentStateCallbackStateV2(
const originalDragSelection = useRecoilComponentStateCallbackStateV2(
originalDragSelectionComponentState,
recordIndexId,
);
@@ -81,19 +81,17 @@ export const useProcessTableWithGroupRecordDrop = () => {
throw new Error('Field metadata is not defined');
}
const originalDragSelection = store.get(
originalDragSelectionAtom,
) as string[];
const existingOriginalDragSelection = store.get(originalDragSelection);
const isDraggingRecord = store.get(isDraggingRecordAtom);
const isCurrentlyDraggingRecord = store.get(isDraggingRecord);
const selectedRecordIds = isDraggingRecord
? originalDragSelection
: (store.get(selectedRowIdsAtom) as string[]);
const selectedRecordIds = isCurrentlyDraggingRecord
? existingOriginalDragSelection
: store.get(selectedRowIds);
const currentRecordSorts = store.get(currentRecordSortsAtom);
const existingRecordSorts = store.get(currentRecordSorts);
if (currentRecordSorts.length > 0) {
if (existingRecordSorts.length > 0) {
openModal(RECORD_INDEX_REMOVE_SORTING_MODAL_ID);
return;
}
@@ -116,13 +114,13 @@ export const useProcessTableWithGroupRecordDrop = () => {
});
},
[
currentRecordSortsAtom,
currentRecordSorts,
store,
objectNameSingular,
objectMetadataItem.fields,
originalDragSelectionAtom,
isDraggingRecordAtom,
selectedRowIdsAtom,
originalDragSelection,
isDraggingRecord,
selectedRowIds,
recordIdsByGroupFamilyState,
groupFieldMetadata?.id,
openModal,
@@ -31,16 +31,15 @@ export const useProcessTableWithoutGroupRecordDrop = () => {
const { updateOneRecord } = useUpdateOneRecord();
const allRecordIdsWithoutGroupAtom =
useRecoilComponentSelectorCallbackStateV2(
allRecordIdsWithoutGroupsComponentSelector,
);
const allRecordIdsWithoutGroup = useRecoilComponentSelectorCallbackStateV2(
allRecordIdsWithoutGroupsComponentSelector,
);
const selectedRowIdsAtom = useRecoilComponentSelectorCallbackStateV2(
const selectedRowIds = useRecoilComponentSelectorCallbackStateV2(
selectedRowIdsComponentSelector,
);
const originalDragSelectionAtom = useRecoilComponentStateCallbackStateV2(
const originalDragSelection = useRecoilComponentStateCallbackStateV2(
originalDragSelectionComponentState,
recordIndexId,
);
@@ -63,12 +62,10 @@ export const useProcessTableWithoutGroupRecordDrop = () => {
return;
}
const allSparseRecordIds = store.get(
allRecordIdsWithoutGroupAtom,
) as string[];
const allSparseRecordIds = store.get(allRecordIdsWithoutGroup);
const draggedRecordId = tableRecordDropResult.draggableId;
const selectedRecordIds = store.get(selectedRowIdsAtom) as string[];
const selectedRecordIds = store.get(selectedRowIds);
const isDroppedAfterList =
tableRecordDropResult.destination.index + 1 >=
@@ -77,9 +74,7 @@ export const useProcessTableWithoutGroupRecordDrop = () => {
const recordsWithPosition: RecordWithPosition[] = allSparseRecordIds
.filter((recordId): recordId is string => isDefined(recordId))
.map((recordId: string) => {
const record = store.get(
recordStoreFamilyState.atomFamily(recordId),
) as { position?: number } | null | undefined;
const record = store.get(recordStoreFamilyState.atomFamily(recordId));
return {
id: recordId,
position: record?.position ?? 0,
@@ -136,14 +131,12 @@ export const useProcessTableWithoutGroupRecordDrop = () => {
);
}
const originalDragSelection = store.get(
originalDragSelectionAtom,
) as string[];
const existingOriginalDragSelection = store.get(originalDragSelection);
const multiDragResult = processMultiDrag({
draggedRecordId,
targetRecordId: targetRecordId ?? '',
selectedRecordIds: originalDragSelection,
selectedRecordIds: existingOriginalDragSelection,
recordsWithPosition: contiguousRecordsWithPosition,
isDroppedAfterList,
});
@@ -165,13 +158,13 @@ export const useProcessTableWithoutGroupRecordDrop = () => {
},
[
objectNameSingular,
selectedRowIdsAtom,
selectedRowIds,
store,
updateOneRecord,
openModal,
currentRecordSorts,
originalDragSelectionAtom,
allRecordIdsWithoutGroupAtom,
originalDragSelection,
allRecordIdsWithoutGroup,
triggerTableWithoutGroupDragAndDropOptimisticUpdate,
],
);
@@ -44,19 +44,19 @@ export const useStartRecordDrag = (contextStoreInstanceId?: string) => {
contextStoreInstanceId,
);
const originalSelectionAtom = useRecoilComponentStateCallbackStateV2(
const originalSelection = useRecoilComponentStateCallbackStateV2(
originalDragSelectionComponentState,
contextStoreInstanceId,
);
const isDraggingRecordAtom = useRecoilComponentStateCallbackStateV2(
const isDraggingRecord = useRecoilComponentStateCallbackStateV2(
isDraggingRecordComponentState,
contextStoreInstanceId,
);
const startRecordDrag = useCallback(
(start: DragStart, selectedRecordIds: string[]) => {
store.set(isDraggingRecordAtom, true);
store.set(isDraggingRecord, true);
const draggedRecordId = start.draggableId;
@@ -69,7 +69,7 @@ export const useStartRecordDrag = (contextStoreInstanceId?: string) => {
store.set(isMultiDragActiveCallbackState, true);
store.set(draggedRecordIdsCallbackState, selectedRecordIds);
store.set(primaryDraggedRecordIdCallbackState, draggedRecordId);
store.set(originalSelectionAtom, selectedRecordIds);
store.set(originalSelection, selectedRecordIds);
store.set(
isRecordIdPrimaryDragMultipleCallbackState({
@@ -94,7 +94,7 @@ export const useStartRecordDrag = (contextStoreInstanceId?: string) => {
store.set(isMultiDragActiveCallbackState, true);
store.set(draggedRecordIdsCallbackState, [draggedRecordId]);
store.set(primaryDraggedRecordIdCallbackState, draggedRecordId);
store.set(originalSelectionAtom, [draggedRecordId]);
store.set(originalSelection, [draggedRecordId]);
}
},
[
@@ -102,8 +102,8 @@ export const useStartRecordDrag = (contextStoreInstanceId?: string) => {
isMultiDragActiveCallbackState,
draggedRecordIdsCallbackState,
primaryDraggedRecordIdCallbackState,
originalSelectionAtom,
isDraggingRecordAtom,
originalSelection,
isDraggingRecord,
isRecordIdSecondaryDragMultipleCallbackState,
isRecordIdPrimaryDragMultipleCallbackState,
],
@@ -66,7 +66,7 @@ export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => {
const correspondingRecordInStore = store.get(
recordStoreFamilyState.atomFamily(recordIdAtRealIndex),
) as { id: string; position?: number } | null | undefined;
);
if (
isDefined(correspondingRecordInStore) &&
@@ -8,10 +8,7 @@ export const extractRecordPositions = (
store: Store,
): RecordWithPosition[] => {
return recordIds.map((recordId) => {
const record = store.get(recordStoreFamilyState.atomFamily(recordId)) as
| { position?: number }
| null
| undefined;
const record = store.get(recordStoreFamilyState.atomFamily(recordId));
return {
id: recordId,

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