refactor: metadata store cleanup, SSE unification, mock metadata loading & login redirect fix (#18651)

## Summary
- **SSE unification**: Replaced 11 individual SSE effect components with
a single generic `MetadataStoreSSEEffect`
- **Metadata store cleanup**: Merged `metadataCollectionHashesState`
into `metadataStoreState` (currentCollectionHash / draftCollectionHash
per entity), moved `objectMetadataItemsSelector` to `object-metadata`
domain, converted `navigationMenuItemsState` to a derived selector
- **Naming clarity**: Renamed `isAppMetadataReadyState` →
`isMinimalMetadataReadyState`, `MetadataGater` → `MinimalMetadataGater`,
`useIsLogged` → `useHasAccessTokenPair`,
`patchMetadataStoreFromSSEEvent` now takes named object params
- **Mock metadata loading**: Added `generate-navigation-menu-items.ts`
script, rewrote `useLoadMockedMinimalMetadata` to load full
objects/fields/indexes/views/navItems from generated mock data, enabling
proper sign-in background rendering (table columns, view picker,
navigation)
- **Login/logout transitions**: `MinimalMetadataLoadEffect` manages
mocked↔real metadata transitions based on auth state,
`MainContextStoreProvider` computes context on auth pages for view
picker support
- **Login redirect fix**: `handleLoadWorkspaceAfterAuthentication` now
re-enables `isAppEffectRedirectEnabled` after `loadCurrentUser()`
completes, fixing the blocked post-login navigation
- **Dead code removal**: Deleted `useRefreshPageLayouts`,
`useApplyPageLayouts`, `useStaleMetadataEntities`,
`metadataCollectionHashesState`, and all individual SSE effects

## Test plan
- [x] Login from welcome page redirects to companies page
- [x] Logout transitions cleanly to mocked metadata on welcome page
- [x] Sign-in background shows table columns, view picker, and
navigation items
- [x] SSE events still update metadata store entries correctly
- [x] Navigation menu items persist across page refreshes
- [ ] CI: lint, typecheck, tests pass
This commit is contained in:
Charles Bochet
2026-03-16 00:38:11 +01:00
committed by GitHub
parent 06efee1eef
commit ba9aa41bba
161 changed files with 3106 additions and 4350 deletions
@@ -14,10 +14,12 @@ import { ClientConfigProviderEffect } from '@/client-config/components/ClientCon
import { ApolloCoreClientMockedProvider } from '@/object-metadata/hooks/__mocks__/ApolloCoreClientMockedProvider';
import { DefaultLayout } from '@/ui/layout/page/components/DefaultLayout';
import { MetadataGater } from '@/metadata-store/components/MetadataGater';
import { MetadataProviderInitialEffects } from '@/metadata-store/effect-components/MetadataProviderInitialEffects';
import { IsAppMetadataReadyEffect } from '@/metadata-store/effect-components/IsAppMetadataReadyEffect';
import { MinimalMetadataGater } from '@/metadata-store/components/MinimalMetadataGater';
import { UserMetadataProviderInitialEffect } from '@/metadata-store/effect-components/UserMetadataProviderInitialEffect';
import { IsMinimalMetadataReadyEffect } from '@/metadata-store/effect-components/IsMinimalMetadataReadyEffect';
import { MinimalMetadataLoadEffect } from '@/metadata-store/effect-components/MinimalMetadataLoadEffect';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { useState } from 'react';
import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider';
import { mockedApolloClient } from '~/testing/mockedApolloClient';
@@ -72,8 +74,10 @@ const ApolloStorybookDevLogEffect = () => {
await dynamicActivate(SOURCE_LOCALE);
const Providers = () => {
const [store] = useState(() => jotaiStore);
return (
<JotaiProvider store={jotaiStore}>
<JotaiProvider store={store}>
<SnackBarComponentInstanceContext.Provider
value={{ instanceId: 'snack-bar-manager' }}
>
@@ -82,10 +86,11 @@ const Providers = () => {
<ApolloStorybookDevLogEffect />
<ClientConfigProviderEffect />
<ClientConfigProvider>
<MetadataProviderInitialEffects />
<IsAppMetadataReadyEffect />
<UserMetadataProviderInitialEffect />
<MinimalMetadataLoadEffect />
<IsMinimalMetadataReadyEffect />
<WorkspaceProviderEffect />
<MetadataGater>
<MinimalMetadataGater>
<ApolloCoreClientMockedProvider>
<PreComputedChipGeneratorsProvider>
<FullHeightStorybookLayout>
@@ -100,7 +105,7 @@ const Providers = () => {
</PreComputedChipGeneratorsProvider>
<MainContextStoreProvider />
</ApolloCoreClientMockedProvider>
</MetadataGater>
</MinimalMetadataGater>
</ClientConfigProvider>
</I18nProvider>
</ApolloProvider>
@@ -1,14 +1,11 @@
import { type Decorator } from '@storybook/react-vite';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { getCommandMenuIdFromRecordIndexId } from '@/command-menu-item/utils/getCommandMenuIdFromRecordIndexId';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { labelIdentifierFieldMetadataItemSelector } from '@/object-metadata/states/labelIdentifierFieldMetadataItemSelector';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
@@ -28,9 +25,11 @@ import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/u
import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { coreViewsState } from '@/views/states/coreViewState';
import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations';
import { type View } from '@/views/types/View';
import { mapViewFieldToRecordField } from '@/views/utils/mapViewFieldToRecordField';
@@ -38,6 +37,7 @@ import { useEffect, useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { mockedCompanyRecords } from '~/testing/mock-data/generated/data/companies/mock-companies-data';
import { mockedCoreViews } from '~/testing/mock-data/generated/metadata/views/mock-views-data';
import { setTestCoreViewsInMetadataStore } from '~/testing/utils/setTestCoreViewsInMetadataStore';
const companyView = mockedCoreViews.find((v) => v.name === 'All Companies')!;
@@ -57,8 +57,6 @@ const InternalTableStateLoaderEffect = ({
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const setCoreViews = useSetAtomState(coreViewsState);
const setRecordTableData = useSetRecordTableData({
recordTableId,
});
@@ -81,7 +79,9 @@ const InternalTableStateLoaderEffect = ({
setCurrentRecordFields(recordFields);
setCoreViews([view as unknown as CoreViewWithRelations]);
setTestCoreViewsInMetadataStore(jotaiStore, [
view as unknown as CoreViewWithRelations,
]);
setContextStoreCurrentViewId(view.id);
}, [
@@ -91,7 +91,6 @@ const InternalTableStateLoaderEffect = ({
view,
setCurrentRecordFields,
setContextStoreCurrentViewId,
setCoreViews,
]);
return null;