Replace sign-in mocked metadata with hardcoded BackgroundMock (#20308)
## Summary When the user is logged out, we render the auth modal on top of a sample table to make the empty page feel alive. So far this was achieved by **loading a full set of mocked object / field / view / navigation-menu metadata into the runtime metadata store** and then mounting the real `RecordTable` and `AppNavigationDrawer` behind the modal. This had a few downsides: - Significant bundle weight pulled in for unauthenticated users (mocked GraphQL fixtures + the real `RecordTable` virtualization stack). - Plenty of code paths that had to know about the "showAuthModal" case (`useRecordIndexTableQuery`, `useTriggerInitialRecordTableDataLoad`, `MainContextStoreProvider`, `IsMinimalMetadataReadyEffect`...). - Any change to metadata-store internals or to the record-table runtime risked breaking the logged-out background. This PR replaces the entire flow with a small, self-contained `BackgroundMock` component tree that **does not consume any metadata** and **does not load any mocked metadata at runtime**. ### What changed - New module under `sign-in-background-mock`: - `BackgroundMockPage` + `BackgroundMockViewBar` + `BackgroundMockTable` + `BackgroundMockTableRow` render a hardcoded "Companies" table that visually mirrors the real one. - `BackgroundMockNavigationDrawer` renders a hardcoded sidebar with People / Companies / Opportunities / Tasks / Notes (with their standard colors). - Hardcoded constants in `BackgroundMockCompanies.ts`, `BackgroundMockColumns.ts`, `BackgroundMockNavigationItems.ts`. - `MinimalMetadataLoadEffect` no longer calls `loadMockedMetadataAtomic` for unauthenticated users — it just doesn't load anything. - `IsMinimalMetadataReadyEffect` now reports ready immediately when there is no access token pair, so the skeleton loader doesn't hang waiting for metadata that will never come. - `MainContextStoreProvider`, `useRecordIndexTableQuery`, and `useTriggerInitialRecordTableDataLoad` drop their `showAuthModal` branches — the real `RecordTable` is no longer mounted behind the modal. - `DefaultLayout` and `NotFound` now lazily load `BackgroundMockPage` / `BackgroundMockNavigationDrawer` instead of the deleted `SignInBackgroundMockPage` / `SignInAppNavigationDrawerMock`. - Removed: `SignInBackgroundMockPage`, `SignInBackgroundMockContainer`, `SignInBackgroundMockContainerEffect`, `SignInAppNavigationDrawerMock`, `SignInBackgroundMockColumnDefinitions`, `SignInBackgroundMockCompanies`, `SignInBackgroundMockViewFields`. `useLoadMockedMetadata` and `preloadMockedMetadata` are kept on purpose: Storybook decorators (`ObjectMetadataItemsDecorator`, `WorkflowStepDecorator`) still rely on the mocked metadata fixtures, but **production** unauthenticated runtime no longer touches them. ### Visual parity Side-by-side at 1440×900 on `/sign-in`: **Before** (loads mocked metadata + real RecordTable):  **After** (purely hardcoded BackgroundMock):  ## Test plan - [ ] `npx nx typecheck twenty-front` ✅ (passes locally) - [ ] `npx nx lint:diff-with-main twenty-front` ✅ (oxlint + prettier clean) - [ ] `npx jest useRecordIndexTableQuery` ✅ - [ ] Manually verify `/sign-in` renders the table + nav drawer behind the modal - [ ] Manually verify `/not-found` still renders the background - [ ] Verify CI: storybook, unit tests, e2e tests
This commit is contained in:
+8
-5
@@ -28,21 +28,24 @@ export const IsMinimalMetadataReadyEffect = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasAccessTokenPair) {
|
||||
setIsMinimalMetadataReady(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const hasActiveWorkspace = isWorkspaceActiveOrSuspended(currentWorkspace);
|
||||
|
||||
const areObjectsLoaded = metadataStore.status === 'up-to-date';
|
||||
const areViewsLoaded = metadataStoreViews.status === 'up-to-date';
|
||||
|
||||
const isReady = !areObjectsLoaded
|
||||
? false
|
||||
: !hasAccessTokenPair ||
|
||||
(isDefined(currentUser) && (!hasActiveWorkspace || areViewsLoaded));
|
||||
|
||||
if (!areObjectsLoaded) {
|
||||
setIsMinimalMetadataReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const isReady =
|
||||
isDefined(currentUser) && (!hasActiveWorkspace || areViewsLoaded);
|
||||
|
||||
if (isReady) {
|
||||
setIsMinimalMetadataReady(true);
|
||||
}
|
||||
|
||||
+10
-42
@@ -2,70 +2,41 @@ import { useHasAccessTokenPair } from '@/auth/hooks/useHasAccessTokenPair';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
|
||||
import { useLoadMinimalMetadata } from '@/metadata-store/hooks/useLoadMinimalMetadata';
|
||||
import { useLoadMockedMetadata } from '@/metadata-store/hooks/useLoadMockedMetadata';
|
||||
import { useLoadStaleMetadataEntities } from '@/metadata-store/hooks/useLoadStaleMetadataEntities';
|
||||
import { metadataLoadedVersionState } from '@/metadata-store/states/metadataLoadedVersionState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace';
|
||||
|
||||
type LoadedState = 'none' | 'mocked' | 'real';
|
||||
|
||||
const computeDesiredLoadState = (
|
||||
hasAccessTokenPair: boolean,
|
||||
isActiveWorkspace: boolean,
|
||||
): LoadedState => {
|
||||
if (hasAccessTokenPair && isActiveWorkspace) {
|
||||
return 'real';
|
||||
}
|
||||
|
||||
return 'mocked';
|
||||
};
|
||||
|
||||
export const MinimalMetadataLoadEffect = () => {
|
||||
const hasAccessTokenPair = useHasAccessTokenPair();
|
||||
const isCurrentUserLoaded = useAtomStateValue(isCurrentUserLoadedState);
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
const metadataLoadedVersion = useAtomStateValue(metadataLoadedVersionState);
|
||||
const [lastMetadataLoadData, setLastMetadataLoadData] = useState<{
|
||||
state: LoadedState;
|
||||
version: number;
|
||||
}>({ state: 'none', version: -1 });
|
||||
const [lastLoadedVersion, setLastLoadedVersion] = useState<number>(-1);
|
||||
|
||||
const { loadMinimalMetadata } = useLoadMinimalMetadata();
|
||||
const { loadMockedMetadataAtomic } = useLoadMockedMetadata();
|
||||
const { loadStaleMetadataEntities } = useLoadStaleMetadataEntities();
|
||||
|
||||
const isActiveWorkspace = isWorkspaceActiveOrSuspended(currentWorkspace);
|
||||
|
||||
const desiredLoadState = computeDesiredLoadState(
|
||||
hasAccessTokenPair,
|
||||
isActiveWorkspace,
|
||||
);
|
||||
const shouldLoadRealMetadata = hasAccessTokenPair && isActiveWorkspace;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCurrentUserLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const versionChanged =
|
||||
metadataLoadedVersion !== lastMetadataLoadData.version;
|
||||
|
||||
if (!versionChanged && lastMetadataLoadData.state === desiredLoadState) {
|
||||
if (!shouldLoadRealMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLastMetadataLoadData({
|
||||
state: desiredLoadState,
|
||||
version: metadataLoadedVersion,
|
||||
});
|
||||
if (metadataLoadedVersion === lastLoadedVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLastLoadedVersion(metadataLoadedVersion);
|
||||
|
||||
const performLoad = async () => {
|
||||
if (desiredLoadState === 'mocked') {
|
||||
await loadMockedMetadataAtomic();
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await loadMinimalMetadata();
|
||||
|
||||
if (result?.staleEntityKeys && result.staleEntityKeys.length > 0) {
|
||||
@@ -76,13 +47,10 @@ export const MinimalMetadataLoadEffect = () => {
|
||||
performLoad();
|
||||
}, [
|
||||
isCurrentUserLoaded,
|
||||
hasAccessTokenPair,
|
||||
isActiveWorkspace,
|
||||
desiredLoadState,
|
||||
lastMetadataLoadData,
|
||||
shouldLoadRealMetadata,
|
||||
lastLoadedVersion,
|
||||
metadataLoadedVersion,
|
||||
loadMinimalMetadata,
|
||||
loadMockedMetadataAtomic,
|
||||
loadStaleMetadataEntities,
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user