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} );