mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-03 17:53:58 +03:00
Fix race condition while loading metadata on sign in (#9027)
This commit is contained in:
parent
183fd877c4
commit
90c26643a8
@ -8,6 +8,7 @@ import { ClientConfigProvider } from '@/client-config/components/ClientConfigPro
|
|||||||
import { ClientConfigProviderEffect } from '@/client-config/components/ClientConfigProviderEffect';
|
import { ClientConfigProviderEffect } from '@/client-config/components/ClientConfigProviderEffect';
|
||||||
import { PromiseRejectionEffect } from '@/error-handler/components/PromiseRejectionEffect';
|
import { PromiseRejectionEffect } from '@/error-handler/components/PromiseRejectionEffect';
|
||||||
import { ApolloMetadataClientProvider } from '@/object-metadata/components/ApolloMetadataClientProvider';
|
import { ApolloMetadataClientProvider } from '@/object-metadata/components/ApolloMetadataClientProvider';
|
||||||
|
import { ObjectMetadataItemsGater } from '@/object-metadata/components/ObjectMetadataItemsGater';
|
||||||
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
|
||||||
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
import { PrefetchDataProvider } from '@/prefetch/components/PrefetchDataProvider';
|
||||||
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
|
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
|
||||||
@ -15,6 +16,7 @@ import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogMa
|
|||||||
import { SnackBarProvider } from '@/ui/feedback/snack-bar-manager/components/SnackBarProvider';
|
import { SnackBarProvider } from '@/ui/feedback/snack-bar-manager/components/SnackBarProvider';
|
||||||
import { UserThemeProviderEffect } from '@/ui/theme/components/AppThemeProvider';
|
import { UserThemeProviderEffect } from '@/ui/theme/components/AppThemeProvider';
|
||||||
import { BaseThemeProvider } from '@/ui/theme/components/BaseThemeProvider';
|
import { BaseThemeProvider } from '@/ui/theme/components/BaseThemeProvider';
|
||||||
|
import { PageFavicon } from '@/ui/utilities/page-favicon/components/PageFavicon';
|
||||||
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
|
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
|
||||||
import { UserProvider } from '@/users/components/UserProvider';
|
import { UserProvider } from '@/users/components/UserProvider';
|
||||||
import { UserProviderEffect } from '@/users/components/UserProviderEffect';
|
import { UserProviderEffect } from '@/users/components/UserProviderEffect';
|
||||||
@ -22,7 +24,6 @@ import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProvide
|
|||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import { Outlet, useLocation } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
import { getPageTitleFromPath } from '~/utils/title-utils';
|
import { getPageTitleFromPath } from '~/utils/title-utils';
|
||||||
import { PageFavicon } from '@/ui/utilities/page-favicon/components/PageFavicon';
|
|
||||||
|
|
||||||
export const AppRouterProviders = () => {
|
export const AppRouterProviders = () => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@ -41,22 +42,24 @@ export const AppRouterProviders = () => {
|
|||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<ApolloMetadataClientProvider>
|
<ApolloMetadataClientProvider>
|
||||||
<ObjectMetadataItemsProvider>
|
<ObjectMetadataItemsProvider>
|
||||||
<PrefetchDataProvider>
|
<ObjectMetadataItemsGater>
|
||||||
<UserThemeProviderEffect />
|
<PrefetchDataProvider>
|
||||||
<SnackBarProvider>
|
<UserThemeProviderEffect />
|
||||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
<SnackBarProvider>
|
||||||
<DialogManager>
|
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||||
<StrictMode>
|
<DialogManager>
|
||||||
<PromiseRejectionEffect />
|
<StrictMode>
|
||||||
<GotoHotkeysEffectsProvider />
|
<PromiseRejectionEffect />
|
||||||
<PageTitle title={pageTitle} />
|
<GotoHotkeysEffectsProvider />
|
||||||
<PageFavicon />
|
<PageTitle title={pageTitle} />
|
||||||
<Outlet />
|
<PageFavicon />
|
||||||
</StrictMode>
|
<Outlet />
|
||||||
</DialogManager>
|
</StrictMode>
|
||||||
</DialogManagerScope>
|
</DialogManager>
|
||||||
</SnackBarProvider>
|
</DialogManagerScope>
|
||||||
</PrefetchDataProvider>
|
</SnackBarProvider>
|
||||||
|
</PrefetchDataProvider>
|
||||||
|
</ObjectMetadataItemsGater>
|
||||||
<PageChangeEffect />
|
<PageChangeEffect />
|
||||||
</ObjectMetadataItemsProvider>
|
</ObjectMetadataItemsProvider>
|
||||||
</ApolloMetadataClientProvider>
|
</ApolloMetadataClientProvider>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
setSessionId,
|
setSessionId,
|
||||||
@ -8,12 +8,14 @@ import {
|
|||||||
} from '@/analytics/hooks/useEventTracker';
|
} from '@/analytics/hooks/useEventTracker';
|
||||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||||
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||||
import { AppBasePath } from '@/types/AppBasePath';
|
import { AppBasePath } from '@/types/AppBasePath';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
import { useCleanRecoilState } from '~/hooks/useCleanRecoilState';
|
import { useCleanRecoilState } from '~/hooks/useCleanRecoilState';
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
|
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
|
||||||
@ -50,11 +52,27 @@ export const PageChangeEffect = () => {
|
|||||||
}
|
}
|
||||||
}, [location, previousLocation]);
|
}, [location, previousLocation]);
|
||||||
|
|
||||||
|
const setIsAppWaitingForFreshObjectMetadata = useSetRecoilState(
|
||||||
|
isAppWaitingForFreshObjectMetadataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setIsAppWaitingForFreshObjectMetadataDebounced = useDebouncedCallback(
|
||||||
|
() => {
|
||||||
|
setIsAppWaitingForFreshObjectMetadata(false);
|
||||||
|
},
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDefined(pageChangeEffectNavigateLocation)) {
|
if (isDefined(pageChangeEffectNavigateLocation)) {
|
||||||
navigate(pageChangeEffectNavigateLocation);
|
navigate(pageChangeEffectNavigateLocation);
|
||||||
|
setIsAppWaitingForFreshObjectMetadataDebounced();
|
||||||
}
|
}
|
||||||
}, [navigate, pageChangeEffectNavigateLocation]);
|
}, [
|
||||||
|
navigate,
|
||||||
|
pageChangeEffectNavigateLocation,
|
||||||
|
setIsAppWaitingForFreshObjectMetadataDebounced,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
@ -3,7 +3,9 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||||
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
export const VerifyEffect = () => {
|
export const VerifyEffect = () => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@ -14,11 +16,16 @@ export const VerifyEffect = () => {
|
|||||||
|
|
||||||
const { verify } = useAuth();
|
const { verify } = useAuth();
|
||||||
|
|
||||||
|
const setIsAppWaitingForFreshObjectMetadata = useSetRecoilState(
|
||||||
|
isAppWaitingForFreshObjectMetadataState,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getTokens = async () => {
|
const getTokens = async () => {
|
||||||
if (!loginToken) {
|
if (!loginToken) {
|
||||||
navigate(AppPath.SignInUp);
|
navigate(AppPath.SignInUp);
|
||||||
} else {
|
} else {
|
||||||
|
setIsAppWaitingForFreshObjectMetadata(true);
|
||||||
await verify(loginToken);
|
await verify(loginToken);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -47,6 +47,7 @@ import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hook
|
|||||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||||
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
|
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
|
||||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||||
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
|
|
||||||
export const useAuth = () => {
|
export const useAuth = () => {
|
||||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||||
@ -54,6 +55,9 @@ export const useAuth = () => {
|
|||||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||||
currentWorkspaceMemberState,
|
currentWorkspaceMemberState,
|
||||||
);
|
);
|
||||||
|
const setIsAppWaitingForFreshObjectMetadataState = useSetRecoilState(
|
||||||
|
isAppWaitingForFreshObjectMetadataState,
|
||||||
|
);
|
||||||
const setCurrentWorkspaceMembers = useSetRecoilState(
|
const setCurrentWorkspaceMembers = useSetRecoilState(
|
||||||
currentWorkspaceMembersState,
|
currentWorkspaceMembersState,
|
||||||
);
|
);
|
||||||
@ -240,6 +244,7 @@ export const useAuth = () => {
|
|||||||
|
|
||||||
setWorkspaces(validWorkspaces);
|
setWorkspaces(validWorkspaces);
|
||||||
}
|
}
|
||||||
|
setIsAppWaitingForFreshObjectMetadataState(true);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
@ -254,6 +259,7 @@ export const useAuth = () => {
|
|||||||
setCurrentUser,
|
setCurrentUser,
|
||||||
setCurrentWorkspace,
|
setCurrentWorkspace,
|
||||||
isOnAWorkspaceSubdomain,
|
isOnAWorkspaceSubdomain,
|
||||||
|
setIsAppWaitingForFreshObjectMetadataState,
|
||||||
setCurrentWorkspaceMembers,
|
setCurrentWorkspaceMembers,
|
||||||
setCurrentWorkspaceMember,
|
setCurrentWorkspaceMember,
|
||||||
setDateTimeFormat,
|
setDateTimeFormat,
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||||
|
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
|
||||||
|
|
||||||
|
export const ObjectMetadataItemsGater = ({
|
||||||
|
children,
|
||||||
|
}: React.PropsWithChildren) => {
|
||||||
|
const isAppWaitingForFreshObjectMetadata = useRecoilValue(
|
||||||
|
isAppWaitingForFreshObjectMetadataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldDisplayChildren = !isAppWaitingForFreshObjectMetadata;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>{shouldDisplayChildren ? <>{children}</> : <UserOrMetadataLoader />}</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
import { createState } from 'twenty-ui';
|
||||||
|
|
||||||
|
export const isAppWaitingForFreshObjectMetadataState = createState<boolean>({
|
||||||
|
key: 'isAppWaitingForFreshObjectMetadataState',
|
||||||
|
defaultValue: false,
|
||||||
|
});
|
@ -21,17 +21,14 @@ export const useShowAuthModal = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (isMatchingLocation(AppPath.SignInUp)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMatchingLocation(AppPath.Verify)) {
|
if (isMatchingLocation(AppPath.Verify)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isMatchingLocation(AppPath.Invite) ||
|
isMatchingLocation(AppPath.Invite) ||
|
||||||
isMatchingLocation(AppPath.ResetPassword)
|
isMatchingLocation(AppPath.ResetPassword) ||
|
||||||
|
isMatchingLocation(AppPath.SignInUp)
|
||||||
) {
|
) {
|
||||||
return isDefaultLayoutAuthModalVisible;
|
return isDefaultLayoutAuthModalVisible;
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ import { createState } from 'twenty-ui';
|
|||||||
|
|
||||||
export const isDefaultLayoutAuthModalVisibleState = createState<boolean>({
|
export const isDefaultLayoutAuthModalVisibleState = createState<boolean>({
|
||||||
key: 'isDefaultLayoutAuthModalVisibleState',
|
key: 'isDefaultLayoutAuthModalVisibleState',
|
||||||
defaultValue: false,
|
defaultValue: true,
|
||||||
});
|
});
|
||||||
|
@ -5,21 +5,21 @@ import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
|||||||
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
import { SignInUpStep } from '@/auth/states/signInUpStepState';
|
||||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||||
|
|
||||||
import { SignInUpGlobalScopeForm } from '@/auth/sign-in-up/components/SignInUpGlobalScopeForm';
|
|
||||||
import { FooterNote } from '@/auth/sign-in-up/components/FooterNote';
|
|
||||||
import { AnimatedEaseIn } from 'twenty-ui';
|
|
||||||
import { Logo } from '@/auth/components/Logo';
|
import { Logo } from '@/auth/components/Logo';
|
||||||
import { Title } from '@/auth/components/Title';
|
import { Title } from '@/auth/components/Title';
|
||||||
import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm';
|
import { FooterNote } from '@/auth/sign-in-up/components/FooterNote';
|
||||||
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
|
import { SignInUpGlobalScopeForm } from '@/auth/sign-in-up/components/SignInUpGlobalScopeForm';
|
||||||
import { SignInUpSSOIdentityProviderSelection } from '@/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection';
|
import { SignInUpSSOIdentityProviderSelection } from '@/auth/sign-in-up/components/SignInUpSSOIdentityProviderSelection';
|
||||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
import { SignInUpWorkspaceScopeForm } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeForm';
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect';
|
import { SignInUpWorkspaceScopeFormEffect } from '@/auth/sign-in-up/components/SignInUpWorkspaceScopeFormEffect';
|
||||||
|
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||||
import { useGetPublicWorkspaceDataBySubdomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain';
|
import { useGetPublicWorkspaceDataBySubdomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain';
|
||||||
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
|
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
|
||||||
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
||||||
|
import { DEFAULT_WORKSPACE_NAME } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceName';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { AnimatedEaseIn } from 'twenty-ui';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const SignInUp = () => {
|
export const SignInUp = () => {
|
||||||
const { form } = useSignInUpForm();
|
const { form } = useSignInUpForm();
|
||||||
|
Loading…
Reference in New Issue
Block a user