Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Charles Bochet
2026-02-23 12:01:37 +01:00
committed by GitHub
parent 22bc3004b9
commit b51eb6471f
538 changed files with 4664 additions and 4127 deletions
@@ -0,0 +1,121 @@
import { useMetadataStore } from '@/app/hooks/useMetadataStore';
import { metadataStoreState } from '@/app/states/metadataStoreState';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItems';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { useStore } from 'jotai';
import { useCallback, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { AppPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace';
import {
ViewType as CoreViewType,
useFindAllCoreViewsQuery,
} from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isMatchingLocation } from '~/utils/isMatchingLocation';
const INDEX_VIEW_TYPES = [
CoreViewType.TABLE,
CoreViewType.KANBAN,
CoreViewType.CALENDAR,
];
export const EagerMetadataLoadEffect = () => {
const location = useLocation();
const isLoggedIn = useIsLogged();
const currentUser = useRecoilValueV2(currentUserState);
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
const store = useStore();
const objectsEntry = useFamilyRecoilValueV2(metadataStoreState, 'objects');
const viewsEntry = useFamilyRecoilValueV2(metadataStoreState, 'views');
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
const { loadMockedObjectMetadataItems } = useLoadMockedObjectMetadataItems();
const { updateDraft, applyChanges } = useMetadataStore();
const isOnAuthPath =
isMatchingLocation(location, AppPath.Verify) ||
isMatchingLocation(location, AppPath.VerifyEmail);
const { data: queryDataCoreViews } = useFindAllCoreViewsQuery({
skip: !isLoggedIn || viewsEntry.status !== 'empty' || isOnAuthPath,
variables: { viewTypes: INDEX_VIEW_TYPES },
});
const setIndexCoreViews = useCallback(
(indexViews: CoreViewWithRelations[]) => {
const existingCoreViews = store.get(coreViewsState.atom);
const existingFieldsWidgetViews = existingCoreViews.filter(
(view) => view.type === CoreViewType.FIELDS_WIDGET,
);
const mergedViews = [...indexViews, ...existingFieldsWidgetViews];
if (!isDeeplyEqual(existingCoreViews, mergedViews)) {
store.set(coreViewsState.atom, mergedViews);
}
},
[store],
);
useEffect(() => {
if (objectsEntry.status !== 'empty') {
return;
}
if (isLoggedIn && !isDefined(currentUser)) {
return;
}
const loadObjectMetadata = async () => {
if (!isLoggedIn || !isWorkspaceActiveOrSuspended(currentWorkspace)) {
await loadMockedObjectMetadataItems();
} else {
await refreshObjectMetadataItems();
}
const loadedItems = store.get(objectMetadataItemsState.atom);
updateDraft('objects', loadedItems);
};
loadObjectMetadata();
}, [
currentUser,
currentWorkspace,
isLoggedIn,
loadMockedObjectMetadataItems,
objectsEntry.status,
refreshObjectMetadataItems,
store,
updateDraft,
]);
useEffect(() => {
if (!isDefined(queryDataCoreViews?.getCoreViews)) {
return;
}
setIndexCoreViews(queryDataCoreViews.getCoreViews);
updateDraft('views', queryDataCoreViews.getCoreViews);
}, [queryDataCoreViews?.getCoreViews, setIndexCoreViews, updateDraft]);
useEffect(() => {
if (
objectsEntry.status === 'draft_pending' &&
viewsEntry.status === 'draft_pending'
) {
applyChanges();
}
}, [objectsEntry.status, viewsEntry.status, applyChanges]);
return null;
};
@@ -0,0 +1,122 @@
import { metadataStoreState } from '@/app/states/metadataStoreState';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { recordPageLayoutsState } from '@/page-layout/states/recordPageLayoutsState';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState';
import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { useStore } from 'jotai';
import { useCallback, useEffect } from 'react';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useLocation } from 'react-router-dom';
import { AppPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
ViewType as CoreViewType,
useFindAllRecordPageLayoutsQuery,
useFindFieldsWidgetCoreViewsQuery,
useFindManyLogicFunctionsQuery,
} from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isMatchingLocation } from '~/utils/isMatchingLocation';
const FIELDS_WIDGET_VIEW_TYPES = [CoreViewType.FIELDS_WIDGET];
export const LazyMetadataLoadEffect = () => {
const location = useLocation();
const isLoggedIn = useIsLogged();
const store = useStore();
const setLogicFunctions = useSetRecoilState(logicFunctionsState);
const isOnAuthPath =
isMatchingLocation(location, AppPath.Verify) ||
isMatchingLocation(location, AppPath.VerifyEmail);
const shouldSkip = !isLoggedIn || isOnAuthPath;
const { data: queryDataFieldsWidgetCoreViews } =
useFindFieldsWidgetCoreViewsQuery({
skip: shouldSkip,
variables: { viewTypes: FIELDS_WIDGET_VIEW_TYPES },
});
const { data: queryDataRecordPageLayouts } = useFindAllRecordPageLayoutsQuery(
{ skip: shouldSkip },
);
const { data: logicFunctionsData } = useFindManyLogicFunctionsQuery({
skip: !isLoggedIn,
});
const setFieldsWidgetCoreViews = useCallback(
(fieldsWidgetViews: CoreViewWithRelations[]) => {
const existingCoreViews = store.get(coreViewsState.atom);
const existingIndexViews = existingCoreViews.filter(
(view) => view.type !== CoreViewType.FIELDS_WIDGET,
);
const mergedViews = [...existingIndexViews, ...fieldsWidgetViews];
if (!isDeeplyEqual(existingCoreViews, mergedViews)) {
store.set(coreViewsState.atom, mergedViews);
}
},
[store],
);
const setRecordPageLayouts = useRecoilCallback(
({ set, snapshot }) =>
(recordPageLayouts: PageLayout[]) => {
const existingRecordPageLayouts = snapshot
.getLoadable(recordPageLayoutsState)
.getValue();
if (!isDeeplyEqual(existingRecordPageLayouts, recordPageLayouts)) {
set(recordPageLayoutsState, recordPageLayouts);
}
store.set(metadataStoreState.atomFamily('pageLayouts'), {
current: recordPageLayouts,
draft: [],
status: 'loaded',
});
},
[store],
);
useEffect(() => {
if (!isDefined(queryDataFieldsWidgetCoreViews?.getCoreViews)) {
return;
}
setFieldsWidgetCoreViews(queryDataFieldsWidgetCoreViews.getCoreViews);
}, [queryDataFieldsWidgetCoreViews?.getCoreViews, setFieldsWidgetCoreViews]);
useEffect(() => {
if (!isDefined(queryDataRecordPageLayouts?.getPageLayouts)) {
return;
}
const transformedPageLayouts =
queryDataRecordPageLayouts.getPageLayouts.map(transformPageLayout);
setRecordPageLayouts(transformedPageLayouts);
}, [queryDataRecordPageLayouts?.getPageLayouts, setRecordPageLayouts]);
useEffect(() => {
if (!isDefined(logicFunctionsData?.findManyLogicFunctions)) {
return;
}
setLogicFunctions(logicFunctionsData.findManyLogicFunctions);
store.set(metadataStoreState.atomFamily('logicFunctions'), {
current: logicFunctionsData.findManyLogicFunctions,
draft: [],
status: 'loaded',
});
}, [logicFunctionsData?.findManyLogicFunctions, setLogicFunctions, store]);
return null;
};
@@ -1,4 +1,4 @@
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
import { useRecoilCallback } from 'recoil';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
@@ -8,62 +8,38 @@ import { currentWorkspaceDeletedMembersState } from '@/auth/states/currentWorksp
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
import { useInitializeFormatPreferences } from '@/localization/hooks/useInitializeFormatPreferences';
import { recordPageLayoutsState } from '@/page-layout/states/recordPageLayoutsState';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState';
import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util';
import { useStore } from 'jotai';
import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
import { type ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { enUS } from 'date-fns/locale';
import { useEffect, useState } from 'react';
import { useStore } from 'jotai';
import { useEffect } 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 { isDefined } from 'twenty-shared/utils';
import {
ViewType as CoreViewType,
useFindAllCoreViewsQuery,
useFindAllRecordPageLayoutsQuery,
useFindFieldsWidgetCoreViewsQuery,
useFindManyLogicFunctionsQuery,
useGetCurrentUserQuery,
type WorkspaceMember,
} from '~/generated-metadata/graphql';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { dateLocaleStateV2 } from '~/localization/states/dateLocaleStateV2';
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isMatchingLocation } from '~/utils/isMatchingLocation';
const INDEX_VIEW_TYPES = [
CoreViewType.TABLE,
CoreViewType.KANBAN,
CoreViewType.CALENDAR,
];
const FIELDS_WIDGET_VIEW_TYPES = [CoreViewType.FIELDS_WIDGET];
export const MetadataProviderEffect = () => {
const location = useLocation();
const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState(
isCurrentUserLoadedState,
const currentUser = useRecoilValueV2(currentUserState);
const setCurrentUser = useSetRecoilStateV2(currentUserState);
const setCurrentWorkspace = useSetRecoilStateV2(currentWorkspaceState);
const setCurrentUserWorkspace = useSetRecoilStateV2(
currentUserWorkspaceState,
);
const [localIsCurrentUserLoaded, setLocalIsCurrentUserLoaded] =
useState(false);
const [localAreViewsLoaded, setLocalAreViewsLoaded] = useState(false);
const setCurrentUser = useSetRecoilState(currentUserState);
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
const setAvailableWorkspaces = useSetRecoilState(availableWorkspacesState);
const setLogicFunctions = useSetRecoilState(logicFunctionsState);
const setAvailableWorkspaces = useSetRecoilStateV2(availableWorkspacesState);
const { initializeFormatPreferences } = useInitializeFormatPreferences();
const isLoggedIn = useIsLogged();
@@ -87,73 +63,19 @@ export const MetadataProviderEffect = () => {
[store],
);
const setIndexCoreViews = useRecoilCallback(
({ set, snapshot }) =>
(indexViews: CoreViewWithRelations[]) => {
const existingCoreViews = snapshot
.getLoadable(coreViewsState)
.getValue();
const existingFieldsWidgetViews = existingCoreViews.filter(
(view) => view.type === CoreViewType.FIELDS_WIDGET,
);
const mergedViews = [...indexViews, ...existingFieldsWidgetViews];
if (!isDeeplyEqual(existingCoreViews, mergedViews)) {
set(coreViewsState, mergedViews);
}
},
[],
);
const setFieldsWidgetCoreViews = useRecoilCallback(
({ set, snapshot }) =>
(fieldsWidgetViews: CoreViewWithRelations[]) => {
const existingCoreViews = snapshot
.getLoadable(coreViewsState)
.getValue();
const existingIndexViews = existingCoreViews.filter(
(view) => view.type !== CoreViewType.FIELDS_WIDGET,
);
const mergedViews = [...existingIndexViews, ...fieldsWidgetViews];
if (!isDeeplyEqual(existingCoreViews, mergedViews)) {
set(coreViewsState, mergedViews);
}
},
[],
);
const setRecordPageLayouts = useRecoilCallback(
({ set, snapshot }) =>
(recordPageLayouts: PageLayout[]) => {
const existingRecordPageLayouts = snapshot
.getLoadable(recordPageLayoutsState)
.getValue();
if (!isDeeplyEqual(existingRecordPageLayouts, recordPageLayouts)) {
set(recordPageLayoutsState, recordPageLayouts);
}
},
[],
);
const setCurrentWorkspaceMember = useSetRecoilState(
const setCurrentWorkspaceMember = useSetRecoilStateV2(
currentWorkspaceMemberState,
);
const setCurrentWorkspaceMembers = useSetRecoilState(
const setCurrentWorkspaceMembers = useSetRecoilStateV2(
currentWorkspaceMembersState,
);
const setCurrentWorkspaceMembersWithDeleted = useSetRecoilState(
const setCurrentWorkspaceMembersWithDeleted = useSetRecoilStateV2(
currentWorkspaceDeletedMembersState,
);
const shouldSkip =
!isLoggedIn ||
isCurrentUserLoaded ||
isDefined(currentUser) ||
isMatchingLocation(location, AppPath.Verify) ||
isMatchingLocation(location, AppPath.VerifyEmail);
@@ -162,40 +84,10 @@ export const MetadataProviderEffect = () => {
skip: shouldSkip,
});
const { data: queryDataCoreViews, loading: queryLoadingCoreViews } =
useFindAllCoreViewsQuery({
skip: shouldSkip,
variables: { viewTypes: INDEX_VIEW_TYPES },
});
const { data: queryDataFieldsWidgetCoreViews } =
useFindFieldsWidgetCoreViewsQuery({
skip: shouldSkip,
variables: { viewTypes: FIELDS_WIDGET_VIEW_TYPES },
});
const { data: queryDataRecordPageLayouts } = useFindAllRecordPageLayoutsQuery(
{
skip: shouldSkip,
},
);
const { data: logicFunctionsData } = useFindManyLogicFunctionsQuery({
skip: !isLoggedIn,
});
useEffect(() => {
if (isDefined(logicFunctionsData?.findManyLogicFunctions)) {
setLogicFunctions(logicFunctionsData.findManyLogicFunctions);
if (userQueryLoading || !isDefined(userQueryData?.currentUser)) {
return;
}
}, [logicFunctionsData?.findManyLogicFunctions, setLogicFunctions]);
useEffect(() => {
if (!userQueryLoading) {
setLocalIsCurrentUserLoaded(true);
}
if (!isDefined(userQueryData?.currentUser)) return;
setCurrentUser(userQueryData.currentUser);
@@ -250,7 +142,6 @@ export const MetadataProviderEffect = () => {
updateLocaleCatalog(updatedWorkspaceMember.locale);
// Initialize format preferences from workspace member
initializeFormatPreferences(updatedWorkspaceMember);
dynamicActivate(
@@ -278,47 +169,10 @@ export const MetadataProviderEffect = () => {
setAvailableWorkspaces,
setCurrentWorkspace,
setCurrentWorkspaceMember,
setIsCurrentUserLoaded,
initializeFormatPreferences,
setCurrentWorkspaceMembersWithDeleted,
updateLocaleCatalog,
setIndexCoreViews,
]);
useEffect(() => {
if (!queryLoadingCoreViews) {
setLocalAreViewsLoaded(true);
}
if (!isDefined(queryDataCoreViews?.getCoreViews)) return;
setIndexCoreViews(queryDataCoreViews.getCoreViews);
}, [
queryDataCoreViews?.getCoreViews,
setIndexCoreViews,
queryLoadingCoreViews,
]);
useEffect(() => {
if (!isDefined(queryDataFieldsWidgetCoreViews?.getCoreViews)) return;
setFieldsWidgetCoreViews(queryDataFieldsWidgetCoreViews.getCoreViews);
}, [queryDataFieldsWidgetCoreViews?.getCoreViews, setFieldsWidgetCoreViews]);
useEffect(() => {
if (!isDefined(queryDataRecordPageLayouts?.getPageLayouts)) return;
const transformedPageLayouts =
queryDataRecordPageLayouts.getPageLayouts.map(transformPageLayout);
setRecordPageLayouts(transformedPageLayouts);
}, [queryDataRecordPageLayouts?.getPageLayouts, setRecordPageLayouts]);
useEffect(() => {
if (localIsCurrentUserLoaded && localAreViewsLoaded) {
setIsCurrentUserLoaded(true);
}
}, [localIsCurrentUserLoaded, localAreViewsLoaded, setIsCurrentUserLoaded]);
return null;
};
@@ -1,8 +1,9 @@
import React from 'react';
import { useRecoilValue } from 'recoil';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
import { isAppLoadingState } from '@/app/states/isAppLoadingState';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat';
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
import { UserContext } from '@/users/contexts/UserContext';
import { useLocation } from 'react-router-dom';
import { AppPath } from 'twenty-shared/types';
@@ -10,15 +11,20 @@ import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader'
import { isMatchingLocation } from '~/utils/isMatchingLocation';
export const UserProvider = ({ children }: React.PropsWithChildren) => {
const isCurrentUserLoaded = useRecoilValue(isCurrentUserLoadedState);
const isAppLoading = useRecoilValueV2(isAppLoadingState);
const isLoggedIn = useIsLogged();
const location = useLocation();
const { dateFormat, timeFormat, timeZone } = useDateTimeFormat();
return !isCurrentUserLoaded &&
const shouldShowLoader =
isAppLoading &&
isLoggedIn &&
!isMatchingLocation(location, AppPath.Verify) &&
!isMatchingLocation(location, AppPath.VerifyEmail) &&
!isMatchingLocation(location, AppPath.CreateWorkspace) ? (
!isMatchingLocation(location, AppPath.CreateWorkspace);
return shouldShowLoader ? (
<UserOrMetadataLoader />
) : (
<UserContext.Provider