diff --git a/packages/twenty-front/src/modules/metadata-store/effect-components/FieldMetadataSSEEffect.tsx b/packages/twenty-front/src/modules/metadata-store/effect-components/FieldMetadataSSEEffect.tsx
new file mode 100644
index 0000000000..35fef1073c
--- /dev/null
+++ b/packages/twenty-front/src/modules/metadata-store/effect-components/FieldMetadataSSEEffect.tsx
@@ -0,0 +1,37 @@
+import { useListenToMetadataOperationBrowserEvent } from '@/browser-event/hooks/useListenToMetadataOperationBrowserEvent';
+import { useMetadataStore } from '@/metadata-store/hooks/useMetadataStore';
+import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItems';
+import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
+import { useListenToEventsForQuery } from '@/sse-db-event/hooks/useListenToEventsForQuery';
+import { useStore } from 'jotai';
+import { AllMetadataName } from '~/generated-metadata/graphql';
+
+export const FieldMetadataSSEEffect = () => {
+ const queryId = 'field-metadata-sse-effect';
+
+ const store = useStore();
+
+ const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
+ const { updateDraft, applyChanges } = useMetadataStore();
+
+ useListenToEventsForQuery({
+ queryId,
+ operationSignature: {
+ metadataName: AllMetadataName.fieldMetadata,
+ variables: {},
+ },
+ });
+
+ useListenToMetadataOperationBrowserEvent({
+ metadataName: AllMetadataName.fieldMetadata,
+ onMetadataOperationBrowserEvent: async () => {
+ await refreshObjectMetadataItems();
+
+ const loadedObjects = store.get(objectMetadataItemsState.atom);
+ updateDraft('objectMetadataItems', loadedObjects);
+ applyChanges();
+ },
+ });
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/metadata-store/effect-components/NavigationMenuItemSSEEffect.tsx b/packages/twenty-front/src/modules/metadata-store/effect-components/NavigationMenuItemSSEEffect.tsx
new file mode 100644
index 0000000000..7fc7910b05
--- /dev/null
+++ b/packages/twenty-front/src/modules/metadata-store/effect-components/NavigationMenuItemSSEEffect.tsx
@@ -0,0 +1,63 @@
+import { useListenToMetadataOperationBrowserEvent } from '@/browser-event/hooks/useListenToMetadataOperationBrowserEvent';
+import { useMetadataStore } from '@/metadata-store/hooks/useMetadataStore';
+import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState';
+import { useListenToEventsForQuery } from '@/sse-db-event/hooks/useListenToEventsForQuery';
+import { useStore } from 'jotai';
+import { isDefined } from 'twenty-shared/utils';
+import {
+ AllMetadataName,
+ useFindManyNavigationMenuItemsLazyQuery,
+} from '~/generated-metadata/graphql';
+import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
+
+export const NavigationMenuItemSSEEffect = () => {
+ const queryId = 'navigation-menu-item-sse-effect';
+
+ const store = useStore();
+ const { updateDraft, applyChanges } = useMetadataStore();
+
+ const [findManyNavigationMenuItemsLazy] =
+ useFindManyNavigationMenuItemsLazyQuery();
+
+ useListenToEventsForQuery({
+ queryId,
+ operationSignature: {
+ metadataName: AllMetadataName.navigationMenuItem,
+ variables: {},
+ },
+ });
+
+ useListenToMetadataOperationBrowserEvent({
+ metadataName: AllMetadataName.navigationMenuItem,
+ onMetadataOperationBrowserEvent: async () => {
+ const result = await findManyNavigationMenuItemsLazy({
+ fetchPolicy: 'network-only',
+ });
+
+ if (!isDefined(result.data?.navigationMenuItems)) {
+ return;
+ }
+
+ const existingNavigationMenuItems = store.get(
+ prefetchNavigationMenuItemsState.atom,
+ );
+
+ if (
+ !isDeeplyEqual(
+ existingNavigationMenuItems,
+ result.data.navigationMenuItems,
+ )
+ ) {
+ store.set(
+ prefetchNavigationMenuItemsState.atom,
+ result.data.navigationMenuItems,
+ );
+
+ updateDraft('navigationMenuItems', result.data.navigationMenuItems);
+ applyChanges();
+ }
+ },
+ });
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/metadata-store/effect-components/ObjectMetadataItemSSEEffect.tsx b/packages/twenty-front/src/modules/metadata-store/effect-components/ObjectMetadataItemSSEEffect.tsx
new file mode 100644
index 0000000000..efd42ecb39
--- /dev/null
+++ b/packages/twenty-front/src/modules/metadata-store/effect-components/ObjectMetadataItemSSEEffect.tsx
@@ -0,0 +1,65 @@
+import { useListenToMetadataOperationBrowserEvent } from '@/browser-event/hooks/useListenToMetadataOperationBrowserEvent';
+import { useMetadataStore } from '@/metadata-store/hooks/useMetadataStore';
+import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItems';
+import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
+import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState';
+import { useListenToEventsForQuery } from '@/sse-db-event/hooks/useListenToEventsForQuery';
+import { useStore } from 'jotai';
+import {
+ AllMetadataName,
+ useFindManyNavigationMenuItemsLazyQuery,
+} from '~/generated-metadata/graphql';
+import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
+
+export const ObjectMetadataItemSSEEffect = () => {
+ const queryId = 'object-metadata-sse-effect';
+
+ const store = useStore();
+
+ const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
+ const { updateDraft, applyChanges } = useMetadataStore();
+
+ const [findManyNavigationMenuItemsLazy] =
+ useFindManyNavigationMenuItemsLazyQuery();
+
+ useListenToEventsForQuery({
+ queryId,
+ operationSignature: {
+ metadataName: AllMetadataName.objectMetadata,
+ variables: {},
+ },
+ });
+
+ useListenToMetadataOperationBrowserEvent({
+ metadataName: AllMetadataName.objectMetadata,
+ onMetadataOperationBrowserEvent: async () => {
+ await refreshObjectMetadataItems();
+
+ const loadedObjects = store.get(objectMetadataItemsState.atom);
+ updateDraft('objectMetadataItems', loadedObjects);
+ applyChanges();
+
+ const navigationMenuItemsResult = await findManyNavigationMenuItemsLazy({
+ fetchPolicy: 'network-only',
+ });
+
+ const existingNavigationMenuItems = store.get(
+ prefetchNavigationMenuItemsState.atom,
+ );
+
+ if (
+ !isDeeplyEqual(
+ existingNavigationMenuItems,
+ navigationMenuItemsResult.data?.navigationMenuItems,
+ )
+ ) {
+ store.set(
+ prefetchNavigationMenuItemsState.atom,
+ navigationMenuItemsResult.data?.navigationMenuItems ?? [],
+ );
+ }
+ },
+ });
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldMetadataSSEEffect.tsx b/packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldSSEEffect.tsx
similarity index 97%
rename from packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldMetadataSSEEffect.tsx
rename to packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldSSEEffect.tsx
index 8e685ad0e1..d9014683b0 100644
--- a/packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldMetadataSSEEffect.tsx
+++ b/packages/twenty-front/src/modules/metadata-store/effect-components/ViewFieldSSEEffect.tsx
@@ -9,7 +9,7 @@ import {
type CoreViewField,
} from '~/generated-metadata/graphql';
-export const ViewFieldMetadataSSEEffect = () => {
+export const ViewFieldSSEEffect = () => {
const store = useStore();
const queryId = 'view-field-metadata-sse-effect';
diff --git a/packages/twenty-front/src/modules/metadata-store/effect-components/ViewMetadataSSEEffect.tsx b/packages/twenty-front/src/modules/metadata-store/effect-components/ViewSSEEffect.tsx
similarity index 98%
rename from packages/twenty-front/src/modules/metadata-store/effect-components/ViewMetadataSSEEffect.tsx
rename to packages/twenty-front/src/modules/metadata-store/effect-components/ViewSSEEffect.tsx
index 7f736f0842..e9387cb767 100644
--- a/packages/twenty-front/src/modules/metadata-store/effect-components/ViewMetadataSSEEffect.tsx
+++ b/packages/twenty-front/src/modules/metadata-store/effect-components/ViewSSEEffect.tsx
@@ -5,7 +5,7 @@ import { type CoreViewWithoutRelations } from '@/views/types/CoreViewWithoutRela
import { useStore } from 'jotai';
import { AllMetadataName } from '~/generated-metadata/graphql';
-export const ViewMetadataSSEEffect = () => {
+export const ViewSSEEffect = () => {
const queryId = 'view-metadata-sse-effect';
const store = useStore();
diff --git a/packages/twenty-front/src/modules/metadata-store/states/metadataStoreState.ts b/packages/twenty-front/src/modules/metadata-store/states/metadataStoreState.ts
index d6decf8bcf..85dbafd168 100644
--- a/packages/twenty-front/src/modules/metadata-store/states/metadataStoreState.ts
+++ b/packages/twenty-front/src/modules/metadata-store/states/metadataStoreState.ts
@@ -14,6 +14,7 @@ export const ALL_METADATA_ENTITY_KEYS = [
'viewSorts',
'pageLayouts',
'logicFunctions',
+ 'navigationMenuItems',
] as const;
export type MetadataEntityKey = (typeof ALL_METADATA_ENTITY_KEYS)[number];
diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/sortNavigationMenuItems.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/sortNavigationMenuItems.ts
index 91a472c58b..e87e82d40b 100644
--- a/packages/twenty-front/src/modules/navigation-menu-item/utils/sortNavigationMenuItems.ts
+++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/sortNavigationMenuItems.ts
@@ -6,7 +6,6 @@ import { AppPath } from 'twenty-shared/types';
import { getAppPath, isDefined } from 'twenty-shared/utils';
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
-import { getObjectMetadataNamePluralFromViewId } from '@/favorites/utils/getObjectMetadataNamePluralFromViewId';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item';
import {
@@ -33,13 +32,16 @@ export const sortNavigationMenuItems = (
);
if (isDefined(view)) {
- const { namePlural } = getObjectMetadataNamePluralFromViewId(
- view,
- objectMetadataItems,
- );
const objectMetadataItem = objectMetadataItems.find(
(meta) => meta.id === view.objectMetadataId,
);
+
+ if (!isDefined(objectMetadataItem)) {
+ return null;
+ }
+
+ const namePlural = objectMetadataItem.namePlural;
+
const isIndexView = view.key === ViewKey.Index;
const labelIdentifier =
isIndexView && isDefined(objectMetadataItem)
diff --git a/packages/twenty-front/src/modules/sse-db-event/components/SSEProvider.tsx b/packages/twenty-front/src/modules/sse-db-event/components/SSEProvider.tsx
index d268c43dff..a44ba8b6e5 100644
--- a/packages/twenty-front/src/modules/sse-db-event/components/SSEProvider.tsx
+++ b/packages/twenty-front/src/modules/sse-db-event/components/SSEProvider.tsx
@@ -1,5 +1,8 @@
-import { ViewFieldMetadataSSEEffect } from '@/metadata-store/effect-components/ViewFieldMetadataSSEEffect';
-import { ViewMetadataSSEEffect } from '@/metadata-store/effect-components/ViewMetadataSSEEffect';
+import { FieldMetadataSSEEffect } from '@/metadata-store/effect-components/FieldMetadataSSEEffect';
+import { NavigationMenuItemSSEEffect } from '@/metadata-store/effect-components/NavigationMenuItemSSEEffect';
+import { ObjectMetadataItemSSEEffect } from '@/metadata-store/effect-components/ObjectMetadataItemSSEEffect';
+import { ViewFieldSSEEffect } from '@/metadata-store/effect-components/ViewFieldSSEEffect';
+import { ViewSSEEffect } from '@/metadata-store/effect-components/ViewSSEEffect';
import { SSEClientEffect } from '@/sse-db-event/components/SSEClientEffect';
import { SSEEventStreamEffect } from '@/sse-db-event/components/SSEEventStreamEffect';
import { SSEQuerySubscribeEffect } from '@/sse-db-event/components/SSEQuerySubscribeEffect';
@@ -15,8 +18,11 @@ export const SSEProvider = ({ children }: SSEProviderProps) => {
-
-
+
+
+
+
+
{children}
>
);