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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user