fix(client-config): set isLoaded to false on API status update (#12371)

Attempt at #12289 (edit Félix: removed fix keyword since I don't think
it fixes it)

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-05-30 14:44:31 +02:00
committed by GitHub
parent 35a4b07bc2
commit b7473371b3
25 changed files with 224 additions and 170 deletions

View File

@ -12,3 +12,4 @@ VITE_DISABLE_ESLINT_CHECKER=true
# VITE_HOST=localhost.com # VITE_HOST=localhost.com
# SSL_KEY_PATH="./certs/your-cert.key" # SSL_KEY_PATH="./certs/your-cert.key"
# SSL_CERT_PATH="./certs/your-cert.crt" # SSL_CERT_PATH="./certs/your-cert.crt"
# IS_DEBUG_MODE=false

View File

@ -1,7 +1,7 @@
import { InMemoryCache, NormalizedCacheObject } from '@apollo/client'; import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilState, useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
@ -9,7 +9,6 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { previousUrlState } from '@/auth/states/previousUrlState'; import { previousUrlState } from '@/auth/states/previousUrlState';
import { tokenPairState } from '@/auth/states/tokenPairState'; import { tokenPairState } from '@/auth/states/tokenPairState';
import { workspacesState } from '@/auth/states/workspaces'; import { workspacesState } from '@/auth/states/workspaces';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useUpdateEffect } from '~/hooks/useUpdateEffect'; import { useUpdateEffect } from '~/hooks/useUpdateEffect';
import { isMatchingLocation } from '~/utils/isMatchingLocation'; import { isMatchingLocation } from '~/utils/isMatchingLocation';
@ -22,18 +21,16 @@ import { ApolloFactory, Options } from '../services/apollo.factory';
export const useApolloFactory = (options: Partial<Options<any>> = {}) => { export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
// eslint-disable-next-line @nx/workspace-no-state-useref // eslint-disable-next-line @nx/workspace-no-state-useref
const apolloRef = useRef<ApolloFactory<NormalizedCacheObject> | null>(null); const apolloRef = useRef<ApolloFactory<NormalizedCacheObject> | null>(null);
const [isDebugMode] = useRecoilState(isDebugModeState);
const navigate = useNavigate(); const navigate = useNavigate();
const setTokenPair = useSetRecoilState(tokenPairState); const setTokenPair = useSetRecoilState(tokenPairState);
const [currentWorkspace, setCurrentWorkspace] = useRecoilState( const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
currentWorkspaceState, currentWorkspaceState,
); );
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
const setCurrentUser = useSetRecoilState(currentUserState);
const setCurrentWorkspaceMember = useSetRecoilState(
currentWorkspaceMemberState, currentWorkspaceMemberState,
); );
const setCurrentUser = useSetRecoilState(currentUserState);
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState); const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
const setWorkspaces = useSetRecoilState(workspacesState); const setWorkspaces = useSetRecoilState(workspacesState);
@ -50,18 +47,15 @@ export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
}, },
}, },
}), }),
headers: {
...(currentWorkspace?.metadataVersion && {
'X-Schema-Version': `${currentWorkspace.metadataVersion}`,
}),
},
defaultOptions: { defaultOptions: {
watchQuery: { watchQuery: {
fetchPolicy: 'cache-and-network', fetchPolicy: 'cache-and-network',
}, },
}, },
connectToDevTools: isDebugMode, connectToDevTools: process.env.IS_DEBUG_MODE === 'true',
currentWorkspaceMember: currentWorkspaceMember, currentWorkspaceMember: currentWorkspaceMember,
currentWorkspace: currentWorkspace,
onTokenPairChange: (tokenPair) => { onTokenPairChange: (tokenPair) => {
setTokenPair(tokenPair); setTokenPair(tokenPair);
}, },
@ -83,7 +77,7 @@ export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
} }
}, },
extraLinks: [], extraLinks: [],
isDebugMode, isDebugMode: process.env.IS_DEBUG_MODE === 'true',
// Override options // Override options
...options, ...options,
}); });
@ -96,8 +90,6 @@ export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
setCurrentWorkspaceMember, setCurrentWorkspaceMember,
setCurrentWorkspace, setCurrentWorkspace,
setWorkspaces, setWorkspaces,
isDebugMode,
currentWorkspace?.metadataVersion,
setPreviousUrl, setPreviousUrl,
]); ]);
@ -107,5 +99,11 @@ export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
} }
}, [currentWorkspaceMember]); }, [currentWorkspaceMember]);
useUpdateEffect(() => {
if (isDefined(apolloRef.current)) {
apolloRef.current.updateCurrentWorkspace(currentWorkspace);
}
}, [currentWorkspace]);
return apolloClient; return apolloClient;
}; };

View File

@ -1,6 +1,7 @@
import { ApolloError, gql, InMemoryCache } from '@apollo/client'; import { ApolloError, gql, InMemoryCache } from '@apollo/client';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'; import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
import { WorkspaceActivationStatus } from '~/generated/graphql';
import { ApolloFactory, Options } from '../apollo.factory'; import { ApolloFactory, Options } from '../apollo.factory';
enableFetchMocks(); enableFetchMocks();
@ -31,9 +32,32 @@ const mockWorkspaceMember = {
colorScheme: 'Light' as const, colorScheme: 'Light' as const,
}; };
const mockWorkspace = {
id: 'workspace-id',
metadataVersion: 1,
allowImpersonation: false,
activationStatus: WorkspaceActivationStatus.ACTIVE,
billingSubscriptions: [],
currentBillingSubscription: null,
workspaceMembersCount: 0,
isPublicInviteLinkEnabled: false,
isGoogleAuthEnabled: false,
isMicrosoftAuthEnabled: false,
isPasswordAuthEnabled: false,
isCustomDomainEnabled: false,
hasValidEnterpriseKey: false,
subdomain: 'test',
customDomain: 'test.com',
workspaceUrls: {
subdomainUrl: 'test.com',
customUrl: 'test.com',
},
};
const createMockOptions = (): Options<any> => ({ const createMockOptions = (): Options<any> => ({
uri: 'http://localhost:3000', uri: 'http://localhost:3000',
currentWorkspaceMember: mockWorkspaceMember, currentWorkspaceMember: mockWorkspaceMember,
currentWorkspace: mockWorkspace,
cache: new InMemoryCache(), cache: new InMemoryCache(),
isDebugMode: true, isDebugMode: true,
onError: mockOnError, onError: mockOnError,

View File

@ -13,6 +13,7 @@ import { createUploadLink } from 'apollo-upload-client';
import { renewToken } from '@/auth/services/AuthService'; import { renewToken } from '@/auth/services/AuthService';
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState'; import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
import { AuthTokenPair } from '~/generated/graphql'; import { AuthTokenPair } from '~/generated/graphql';
import { logDebug } from '~/utils/logDebug'; import { logDebug } from '~/utils/logDebug';
@ -34,6 +35,7 @@ export interface Options<TCacheShape> extends ApolloClientOptions<TCacheShape> {
onTokenPairChange?: (tokenPair: AuthTokenPair) => void; onTokenPairChange?: (tokenPair: AuthTokenPair) => void;
onUnauthenticatedError?: () => void; onUnauthenticatedError?: () => void;
currentWorkspaceMember: CurrentWorkspaceMember | null; currentWorkspaceMember: CurrentWorkspaceMember | null;
currentWorkspace: CurrentWorkspace | null;
extraLinks?: ApolloLink[]; extraLinks?: ApolloLink[];
isDebugMode?: boolean; isDebugMode?: boolean;
} }
@ -41,6 +43,7 @@ export interface Options<TCacheShape> extends ApolloClientOptions<TCacheShape> {
export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> { export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> {
private client: ApolloClient<TCacheShape>; private client: ApolloClient<TCacheShape>;
private currentWorkspaceMember: CurrentWorkspaceMember | null = null; private currentWorkspaceMember: CurrentWorkspaceMember | null = null;
private currentWorkspace: CurrentWorkspace | null = null;
constructor(opts: Options<TCacheShape>) { constructor(opts: Options<TCacheShape>) {
const { const {
@ -50,12 +53,14 @@ export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> {
onTokenPairChange, onTokenPairChange,
onUnauthenticatedError, onUnauthenticatedError,
currentWorkspaceMember, currentWorkspaceMember,
currentWorkspace,
extraLinks, extraLinks,
isDebugMode, isDebugMode,
...options ...options
} = opts; } = opts;
this.currentWorkspaceMember = currentWorkspaceMember; this.currentWorkspaceMember = currentWorkspaceMember;
this.currentWorkspace = currentWorkspace;
const buildApolloLink = (): ApolloLink => { const buildApolloLink = (): ApolloLink => {
const httpLink = createUploadLink({ const httpLink = createUploadLink({
@ -84,6 +89,9 @@ export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> {
...(this.currentWorkspaceMember?.locale ...(this.currentWorkspaceMember?.locale
? { 'x-locale': this.currentWorkspaceMember.locale } ? { 'x-locale': this.currentWorkspaceMember.locale }
: { 'x-locale': i18n.locale }), : { 'x-locale': i18n.locale }),
...(this.currentWorkspace?.metadataVersion && {
'X-Schema-Version': `${this.currentWorkspace.metadataVersion}`,
}),
}, },
}; };
}); });
@ -188,6 +196,10 @@ export class ApolloFactory<TCacheShape> implements ApolloManager<TCacheShape> {
this.currentWorkspaceMember = workspaceMember; this.currentWorkspaceMember = workspaceMember;
} }
updateCurrentWorkspace(workspace: CurrentWorkspace | null) {
this.currentWorkspace = workspace;
}
getClient() { getClient() {
return this.client; return this.client;
} }

View File

@ -0,0 +1,7 @@
import { getTokenPair } from '@/apollo/utils/getTokenPair';
import { isDefined } from 'twenty-shared/utils';
export const hasTokenPair = () => {
const tokenPair = getTokenPair();
return isDefined(tokenPair) && isDefined(tokenPair.accessToken?.token);
};

View File

@ -33,15 +33,15 @@ export const AppRouterProviders = () => {
const pageTitle = getPageTitleFromPath(pathname); const pageTitle = getPageTitleFromPath(pathname);
return ( return (
<CaptchaProvider> <ApolloProvider>
<ApolloProvider> <BaseThemeProvider>
<BaseThemeProvider> <ClientConfigProviderEffect />
<ClientConfigProviderEffect /> <UserProviderEffect />
<ClientConfigProvider> <WorkspaceProviderEffect />
<ClientConfigProvider>
<CaptchaProvider>
<ChromeExtensionSidecarEffect /> <ChromeExtensionSidecarEffect />
<ChromeExtensionSidecarProvider> <ChromeExtensionSidecarProvider>
<UserProviderEffect />
<WorkspaceProviderEffect />
<UserProvider> <UserProvider>
<AuthProvider> <AuthProvider>
<ApolloMetadataClientProvider> <ApolloMetadataClientProvider>
@ -71,9 +71,9 @@ export const AppRouterProviders = () => {
</AuthProvider> </AuthProvider>
</UserProvider> </UserProvider>
</ChromeExtensionSidecarProvider> </ChromeExtensionSidecarProvider>
</ClientConfigProvider> </CaptchaProvider>
</BaseThemeProvider> </ClientConfigProvider>
</ApolloProvider> </BaseThemeProvider>
</CaptchaProvider> </ApolloProvider>
); );
}; };

View File

@ -17,7 +17,7 @@ export const VerifyLoginTokenEffect = () => {
const navigate = useNavigateApp(); const navigate = useNavigateApp();
const { verifyLoginToken } = useVerifyLogin(); const { verifyLoginToken } = useVerifyLogin();
const { isLoaded: clientConfigLoaded } = useRecoilValue( const { isSaved: clientConfigLoaded } = useRecoilValue(
clientConfigApiStatusState, clientConfigApiStatusState,
); );

View File

@ -1,6 +1,5 @@
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { billingState } from '@/client-config/states/billingState'; import { billingState } from '@/client-config/states/billingState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
import { supportChatState } from '@/client-config/states/supportChatState'; import { supportChatState } from '@/client-config/states/supportChatState';
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
@ -118,7 +117,6 @@ describe('useAuth', () => {
isDeveloperDefaultSignInPrefilledState, isDeveloperDefaultSignInPrefilledState,
); );
const supportChat = useRecoilValue(supportChatState); const supportChat = useRecoilValue(supportChatState);
const isDebugMode = useRecoilValue(isDebugModeState);
const isMultiWorkspaceEnabled = useRecoilValue( const isMultiWorkspaceEnabled = useRecoilValue(
isMultiWorkspaceEnabledState, isMultiWorkspaceEnabledState,
); );
@ -131,7 +129,6 @@ describe('useAuth', () => {
billing, billing,
isDeveloperDefaultSignInPrefilled, isDeveloperDefaultSignInPrefilled,
supportChat, supportChat,
isDebugMode,
isMultiWorkspaceEnabled, isMultiWorkspaceEnabled,
}, },
}; };
@ -160,7 +157,6 @@ describe('useAuth', () => {
supportDriver: 'none', supportDriver: 'none',
supportFrontChatId: null, supportFrontChatId: null,
}); });
expect(state.isDebugMode).toBe(false);
}); });
it('should handle credential sign-up', async () => { it('should handle credential sign-up', async () => {

View File

@ -11,11 +11,9 @@ import {
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
import { workspacesState } from '@/auth/states/workspaces'; import { workspacesState } from '@/auth/states/workspaces';
import { billingState } from '@/client-config/states/billingState'; import { billingState } from '@/client-config/states/billingState';
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { supportChatState } from '@/client-config/states/supportChatState'; import { supportChatState } from '@/client-config/states/supportChatState';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember'; import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { REACT_APP_SERVER_BASE_URL } from '~/config';
@ -42,25 +40,27 @@ import { currentUserState } from '../states/currentUserState';
import { tokenPairState } from '../states/tokenPairState'; import { tokenPairState } from '../states/tokenPairState';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState'; import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
import { import {
SignInUpStep, SignInUpStep,
signInUpStepState, signInUpStepState,
} from '@/auth/states/signInUpStepState'; } from '@/auth/states/signInUpStepState';
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState'; import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type'; import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
import { apiConfigState } from '@/client-config/states/apiConfigState';
import { captchaState } from '@/client-config/states/captchaState'; import { captchaState } from '@/client-config/states/captchaState';
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState'; import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace'; import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain'; import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
import { useOrigin } from '@/domain-manager/hooks/useOrigin'; import { useOrigin } from '@/domain-manager/hooks/useOrigin';
import { useRedirect } from '@/domain-manager/hooks/useRedirect'; import { useRedirect } from '@/domain-manager/hooks/useRedirect';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState'; import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
import { i18n } from '@lingui/core'; import { i18n } from '@lingui/core';
import { useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams } from 'react-router-dom';
import { APP_LOCALES } from 'twenty-shared/translations'; import { APP_LOCALES } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { iconsState } from 'twenty-ui/display'; import { iconsState } from 'twenty-ui/display';
@ -85,8 +85,6 @@ export const useAuth = () => {
isEmailVerificationRequiredState, isEmailVerificationRequiredState,
); );
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
const setSignInUpStep = useSetRecoilState(signInUpStepState); const setSignInUpStep = useSetRecoilState(signInUpStepState);
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
const setWorkspaces = useSetRecoilState(workspacesState); const setWorkspaces = useSetRecoilState(workspacesState);
@ -119,10 +117,13 @@ export const useAuth = () => {
const [, setSearchParams] = useSearchParams(); const [, setSearchParams] = useSearchParams();
const navigate = useNavigate();
const clearSession = useRecoilCallback( const clearSession = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
async () => { async () => {
const emptySnapshot = snapshot_UNSTABLE(); const emptySnapshot = snapshot_UNSTABLE();
const iconsValue = snapshot.getLoadable(iconsState).getValue(); const iconsValue = snapshot.getLoadable(iconsState).getValue();
const authProvidersValue = snapshot const authProvidersValue = snapshot
.getLoadable(workspaceAuthProvidersState) .getLoadable(workspaceAuthProvidersState)
@ -132,7 +133,6 @@ export const useAuth = () => {
.getLoadable(isDeveloperDefaultSignInPrefilledState) .getLoadable(isDeveloperDefaultSignInPrefilledState)
.getValue(); .getValue();
const supportChat = snapshot.getLoadable(supportChatState).getValue(); const supportChat = snapshot.getLoadable(supportChatState).getValue();
const isDebugMode = snapshot.getLoadable(isDebugModeState).getValue();
const captcha = snapshot.getLoadable(captchaState).getValue(); const captcha = snapshot.getLoadable(captchaState).getValue();
const clientConfigApiStatus = snapshot const clientConfigApiStatus = snapshot
.getLoadable(clientConfigApiStatusState) .getLoadable(clientConfigApiStatusState)
@ -146,6 +146,12 @@ export const useAuth = () => {
const domainConfiguration = snapshot const domainConfiguration = snapshot
.getLoadable(domainConfigurationState) .getLoadable(domainConfigurationState)
.getValue(); .getValue();
const apiConfig = snapshot.getLoadable(apiConfigState).getValue();
const sentryConfig = snapshot.getLoadable(sentryConfigState).getValue();
const workspacePublicData = snapshot
.getLoadable(workspacePublicDataState)
.getValue();
const initialSnapshot = emptySnapshot.map(({ set }) => { const initialSnapshot = emptySnapshot.map(({ set }) => {
set(iconsState, iconsValue); set(iconsState, iconsValue);
set(workspaceAuthProvidersState, authProvidersValue); set(workspaceAuthProvidersState, authProvidersValue);
@ -155,22 +161,27 @@ export const useAuth = () => {
isDeveloperDefaultSignInPrefilled, isDeveloperDefaultSignInPrefilled,
); );
set(supportChatState, supportChat); set(supportChatState, supportChat);
set(isDebugModeState, isDebugMode);
set(captchaState, captcha); set(captchaState, captcha);
set(apiConfigState, apiConfig);
set(sentryConfigState, sentryConfig);
set(workspacePublicDataState, workspacePublicData);
set(clientConfigApiStatusState, clientConfigApiStatus); set(clientConfigApiStatusState, clientConfigApiStatus);
set(isCurrentUserLoadedState, isCurrentUserLoaded); set(isCurrentUserLoadedState, isCurrentUserLoaded);
set(isMultiWorkspaceEnabledState, isMultiWorkspaceEnabled); set(isMultiWorkspaceEnabledState, isMultiWorkspaceEnabled);
set(domainConfigurationState, domainConfiguration); set(domainConfigurationState, domainConfiguration);
return undefined; return undefined;
}); });
goToRecoilSnapshot(initialSnapshot); goToRecoilSnapshot(initialSnapshot);
await client.clearStore();
sessionStorage.clear(); sessionStorage.clear();
localStorage.clear(); localStorage.clear();
await client.clearStore();
// We need to explicitly clear the state to trigger the cookie deletion which include the parent domain // We need to explicitly clear the state to trigger the cookie deletion which include the parent domain
setLastAuthenticateWorkspaceDomain(null); setLastAuthenticateWorkspaceDomain(null);
navigate(AppPath.SignInUp);
}, },
[client, goToRecoilSnapshot, setLastAuthenticateWorkspaceDomain], [navigate, client, goToRecoilSnapshot, setLastAuthenticateWorkspaceDomain],
); );
const handleGetLoginTokenFromCredentials = useCallback( const handleGetLoginTokenFromCredentials = useCallback(
@ -368,16 +379,9 @@ export const useAuth = () => {
), ),
); );
await refreshObjectMetadataItems();
await loadCurrentUser(); await loadCurrentUser();
}, },
[ [getAuthTokensFromLoginToken, setTokenPair, loadCurrentUser, origin],
getAuthTokensFromLoginToken,
setTokenPair,
refreshObjectMetadataItems,
loadCurrentUser,
origin,
],
); );
const handleCredentialsSignIn = useCallback( const handleCredentialsSignIn = useCallback(

View File

@ -1,21 +1,30 @@
import React from 'react'; import React, { useMemo } from 'react';
import { CaptchaProviderScriptLoaderEffect } from '@/captcha/components/CaptchaProviderScriptLoaderEffect'; import { CaptchaProviderScriptLoaderEffect } from '@/captcha/components/CaptchaProviderScriptLoaderEffect';
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath'; import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
export const CaptchaProvider = ({ children }: React.PropsWithChildren) => { export const CaptchaProvider = React.memo(
const location = useLocation(); ({ children }: React.PropsWithChildren) => {
const location = useLocation();
if (!isCaptchaRequiredForPath(location.pathname)) { const isCaptchaRequired = useMemo(
return <>{children}</>; () => isCaptchaRequiredForPath(location.pathname),
} [location.pathname],
);
return ( return (
<> <>
<div id="captcha-widget" data-size="invisible"></div> {isCaptchaRequired && (
<CaptchaProviderScriptLoaderEffect /> <>
{children} <div id="captcha-widget" data-size="invisible"></div>
</> <CaptchaProviderScriptLoaderEffect />
); </>
}; )}
{children}
</>
);
},
);
CaptchaProvider.displayName = 'CaptchaProvider';

View File

@ -9,7 +9,6 @@ import { clientConfigApiStatusState } from '@/client-config/states/clientConfigA
import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState'; import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState';
import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState'; import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState';
import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState'; import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState'; import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState'; import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState';
@ -26,7 +25,6 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
export const ClientConfigProviderEffect = () => { export const ClientConfigProviderEffect = () => {
const setIsDebugMode = useSetRecoilState(isDebugModeState);
const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState); const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState);
const setDomainConfiguration = useSetRecoilState(domainConfigurationState); const setDomainConfiguration = useSetRecoilState(domainConfigurationState);
const setAuthProviders = useSetRecoilState(authProvidersState); const setAuthProviders = useSetRecoilState(authProvidersState);
@ -90,10 +88,17 @@ export const ClientConfigProviderEffect = () => {
const { data, loading, error, fetchClientConfig } = useClientConfig(); const { data, loading, error, fetchClientConfig } = useClientConfig();
useEffect(() => { useEffect(() => {
if (!clientConfigApiStatus.isLoaded) { if (
!clientConfigApiStatus.isLoadedOnce &&
!clientConfigApiStatus.isLoading
) {
fetchClientConfig(); fetchClientConfig();
} }
}, [clientConfigApiStatus.isLoaded, fetchClientConfig]); }, [
clientConfigApiStatus.isLoadedOnce,
clientConfigApiStatus.isLoading,
fetchClientConfig,
]);
useEffect(() => { useEffect(() => {
if (loading) return; if (loading) return;
@ -124,7 +129,6 @@ export const ClientConfigProviderEffect = () => {
magicLink: false, magicLink: false,
sso: data?.clientConfig.authProviders.sso, sso: data?.clientConfig.authProviders.sso,
}); });
setIsDebugMode(data?.clientConfig.debugMode);
setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled); setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled);
setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled); setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled);
setIsMultiWorkspaceEnabled(data?.clientConfig.isMultiWorkspaceEnabled); setIsMultiWorkspaceEnabled(data?.clientConfig.isMultiWorkspaceEnabled);
@ -167,24 +171,23 @@ export const ClientConfigProviderEffect = () => {
); );
setClientConfigApiStatus((currentStatus) => ({ setClientConfigApiStatus((currentStatus) => ({
...currentStatus, ...currentStatus,
isLoaded: true, isSaved: true,
})); }));
}, [ }, [
data, data,
setIsDebugMode, loading,
error,
setIsDeveloperDefaultSignInPrefilled, setIsDeveloperDefaultSignInPrefilled,
setIsMultiWorkspaceEnabled, setIsMultiWorkspaceEnabled,
setIsEmailVerificationRequired, setIsEmailVerificationRequired,
setSupportChat, setSupportChat,
setBilling, setBilling,
setSentryConfig, setSentryConfig,
loading,
setClientConfigApiStatus, setClientConfigApiStatus,
setCaptcha, setCaptcha,
setChromeExtensionId, setChromeExtensionId,
setApiConfig, setApiConfig,
setIsAnalyticsEnabled, setIsAnalyticsEnabled,
error,
setDomainConfiguration, setDomainConfiguration,
setAuthProviders, setAuthProviders,
setCanManageFeatureFlags, setCanManageFeatureFlags,

View File

@ -1,4 +1,4 @@
import { useRecoilCallback, useRecoilValue } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { ClientConfig } from '~/generated/graphql'; import { ClientConfig } from '~/generated/graphql';
import { clientConfigApiStatusState } from '../states/clientConfigApiStatusState'; import { clientConfigApiStatusState } from '../states/clientConfigApiStatusState';
import { getClientConfig } from '../utils/getClientConfig'; import { getClientConfig } from '../utils/getClientConfig';
@ -13,41 +13,38 @@ type UseClientConfigResult = {
export const useClientConfig = (): UseClientConfigResult => { export const useClientConfig = (): UseClientConfigResult => {
const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState); const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState);
const setClientConfigApiStatus = useSetRecoilState(
const fetchClientConfig = useRecoilCallback( clientConfigApiStatusState,
({ set }) =>
async () => {
set(clientConfigApiStatusState, (prev) => ({
...prev,
isLoading: true,
isErrored: false,
error: undefined,
}));
try {
const clientConfig = await getClientConfig();
set(clientConfigApiStatusState, (prev) => ({
...prev,
isLoading: false,
isLoaded: true,
data: { clientConfig },
}));
} catch (err) {
const error =
err instanceof Error
? err
: new Error('Failed to fetch client config');
set(clientConfigApiStatusState, (prev) => ({
...prev,
isLoading: false,
isErrored: true,
error,
}));
}
},
[],
); );
const fetchClientConfig = async () => {
setClientConfigApiStatus((prev) => ({
...prev,
isLoading: true,
}));
try {
const clientConfig = await getClientConfig();
setClientConfigApiStatus((prev) => ({
...prev,
isLoading: false,
isLoadedOnce: true,
isErrored: false,
error: undefined,
data: { clientConfig },
}));
} catch (err) {
const error =
err instanceof Error ? err : new Error('Failed to fetch client config');
setClientConfigApiStatus((prev) => ({
...prev,
isLoading: false,
isErrored: true,
error,
}));
}
};
return { return {
data: clientConfigApiStatus.data, data: clientConfigApiStatus.data,
loading: clientConfigApiStatus.isLoading || false, loading: clientConfigApiStatus.isLoading || false,

View File

@ -2,9 +2,10 @@ import { createState } from 'twenty-ui/utilities';
import { ClientConfig } from '~/generated/graphql'; import { ClientConfig } from '~/generated/graphql';
type ClientConfigApiStatus = { type ClientConfigApiStatus = {
isLoaded: boolean; isLoadedOnce: boolean;
isLoading: boolean; isLoading: boolean;
isErrored: boolean; isErrored: boolean;
isSaved: boolean;
error?: Error; error?: Error;
data?: { clientConfig: ClientConfig }; data?: { clientConfig: ClientConfig };
}; };
@ -12,9 +13,10 @@ type ClientConfigApiStatus = {
export const clientConfigApiStatusState = createState<ClientConfigApiStatus>({ export const clientConfigApiStatusState = createState<ClientConfigApiStatus>({
key: 'clientConfigApiStatus', key: 'clientConfigApiStatus',
defaultValue: { defaultValue: {
isLoaded: false, isLoadedOnce: false,
isLoading: false, isLoading: false,
isErrored: false, isErrored: false,
isSaved: false,
error: undefined, error: undefined,
data: undefined, data: undefined,
}, },

View File

@ -1,5 +0,0 @@
import { createState } from 'twenty-ui/utilities';
export const isDebugModeState = createState<boolean>({
key: 'isDebugModeState',
defaultValue: false,
});

View File

@ -2,14 +2,12 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainCo
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { getViewType } from '@/context-store/utils/getViewType';
import { useSetLastVisitedObjectMetadataId } from '@/navigation/hooks/useSetLastVisitedObjectMetadataId'; import { useSetLastVisitedObjectMetadataId } from '@/navigation/hooks/useSetLastVisitedObjectMetadataId';
import { useSetLastVisitedViewForObjectMetadataNamePlural } from '@/navigation/hooks/useSetLastVisitedViewForObjectMetadataNamePlural'; import { useSetLastVisitedViewForObjectMetadataNamePlural } from '@/navigation/hooks/useSetLastVisitedViewForObjectMetadataNamePlural';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { prefetchViewFromViewIdFamilySelector } from '@/prefetch/states/selector/prefetchViewFromViewIdFamilySelector'; import { prefetchViewFromViewIdFamilySelector } from '@/prefetch/states/selector/prefetchViewFromViewIdFamilySelector';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { View } from '@/views/types/View';
import { ViewType } from '@/views/types/ViewType';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
@ -124,31 +122,3 @@ export const MainContextStoreProviderEffect = ({
return null; return null;
}; };
const getViewType = ({
isSettingsPage,
isRecordShowPage,
isRecordIndexPage,
view,
}: {
isSettingsPage: boolean;
isRecordShowPage: boolean;
isRecordIndexPage: boolean;
view?: View;
}) => {
if (isSettingsPage) {
return null;
}
if (isRecordIndexPage) {
return view?.type === ViewType.Kanban
? ContextStoreViewType.Kanban
: ContextStoreViewType.Table;
}
if (isRecordShowPage) {
return ContextStoreViewType.ShowPage;
}
return null;
};

View File

@ -0,0 +1,31 @@
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { View } from '@/views/types/View';
import { ViewType } from '@/views/types/ViewType';
export const getViewType = ({
isSettingsPage,
isRecordShowPage,
isRecordIndexPage,
view,
}: {
isSettingsPage: boolean;
isRecordShowPage: boolean;
isRecordIndexPage: boolean;
view?: View;
}) => {
if (isSettingsPage) {
return null;
}
if (isRecordIndexPage) {
return view?.type === ViewType.Kanban
? ContextStoreViewType.Kanban
: ContextStoreViewType.Table;
}
if (isRecordShowPage) {
return ContextStoreViewType.ShowPage;
}
return null;
};

View File

@ -1,11 +1,8 @@
import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev'; import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
export const ApolloDevLogEffect = () => { export const ApolloDevLogEffect = () => {
const isDebugMode = useRecoilValue(isDebugModeState); const isDebugMode = process.env.IS_DEBUG_MODE === 'true';
useEffect(() => { useEffect(() => {
if (isDebugMode) { if (isDebugMode) {

View File

@ -1,5 +1,4 @@
import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { useRecoilTransactionObserver_UNSTABLE } from 'recoil';
import { useRecoilTransactionObserver_UNSTABLE, useRecoilValue } from 'recoil';
import { logDebug } from '~/utils/logDebug'; import { logDebug } from '~/utils/logDebug';
@ -15,7 +14,7 @@ const formatTitle = (stateName: string) => {
}; };
export const RecoilDebugObserverEffect = () => { export const RecoilDebugObserverEffect = () => {
const isDebugMode = useRecoilValue(isDebugModeState); const isDebugMode = process.env.IS_DEBUG_MODE === 'true';
useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => { useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
if (!isDebugMode) { if (!isDebugMode) {

View File

@ -28,7 +28,7 @@ export const useGetPublicWorkspaceDataByDomain = () => {
origin, origin,
}, },
skip: skip:
!clientConfigApiStatus.isLoaded || !clientConfigApiStatus.isSaved ||
(isMultiWorkspaceEnabled && isDefaultDomain) || (isMultiWorkspaceEnabled && isDefaultDomain) ||
isDefined(workspacePublicData), isDefined(workspacePublicData),
onCompleted: (data) => { onCompleted: (data) => {
@ -38,9 +38,17 @@ export const useGetPublicWorkspaceDataByDomain = () => {
setWorkspacePublicDataState(data.getPublicWorkspaceDataByDomain); setWorkspacePublicDataState(data.getPublicWorkspaceDataByDomain);
}, },
onError: (error) => { onError: (error) => {
// eslint-disable-next-line no-console // Only redirect to default domain if it's a workspace not found error
console.error(error); const isWorkspaceNotFoundError = error.graphQLErrors?.some(
redirectToDefaultDomain(); (graphQLError) => graphQLError.extensions?.code === 'NOT_FOUND',
);
if (isWorkspaceNotFoundError) {
redirectToDefaultDomain();
} else {
// eslint-disable-next-line no-console
console.error(error);
}
}, },
}); });

View File

@ -5,8 +5,8 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems'; import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem'; import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace'; import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const ObjectMetadataItemsLoadEffect = () => { export const ObjectMetadataItemsLoadEffect = () => {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
import { dateTimeFormatState } from '@/localization/states/dateTimeFormatState'; import { dateTimeFormatState } from '@/localization/states/dateTimeFormatState';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { UserContext } from '@/users/contexts/UserContext'; import { UserContext } from '@/users/contexts/UserContext';

View File

@ -1,13 +1,13 @@
import { useEffect, useState } from 'react';
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil'; import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState'; import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { currentWorkspaceDeletedMembersState } from '@/auth/states/currentWorkspaceDeletedMembersStates'; import { currentWorkspaceDeletedMembersState } from '@/auth/states/currentWorkspaceDeletedMembersStates';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersStates'; import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersStates';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadedState';
import { workspacesState } from '@/auth/states/workspaces'; import { workspacesState } from '@/auth/states/workspaces';
import { DateFormat } from '@/localization/constants/DateFormat'; import { DateFormat } from '@/localization/constants/DateFormat';
import { TimeFormat } from '@/localization/constants/TimeFormat'; import { TimeFormat } from '@/localization/constants/TimeFormat';
@ -21,6 +21,7 @@ import { AppPath } from '@/types/AppPath';
import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util'; import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember'; import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { enUS } from 'date-fns/locale'; import { enUS } from 'date-fns/locale';
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations'; import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@ -31,7 +32,6 @@ import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { isMatchingLocation } from '~/utils/isMatchingLocation'; import { isMatchingLocation } from '~/utils/isMatchingLocation';
export const UserProviderEffect = () => { export const UserProviderEffect = () => {
const [isLoading, setIsLoading] = useState(true);
const location = useLocation(); const location = useLocation();
const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState( const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState(
@ -42,6 +42,8 @@ export const UserProviderEffect = () => {
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState); const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
const setWorkspaces = useSetRecoilState(workspacesState); const setWorkspaces = useSetRecoilState(workspacesState);
const setDateTimeFormat = useSetRecoilState(dateTimeFormatState); const setDateTimeFormat = useSetRecoilState(dateTimeFormatState);
const isLoggedIn = useIsLogged();
const updateLocaleCatalog = useRecoilCallback( const updateLocaleCatalog = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
async (newLocale: keyof typeof APP_LOCALES) => { async (newLocale: keyof typeof APP_LOCALES) => {
@ -68,8 +70,9 @@ export const UserProviderEffect = () => {
currentWorkspaceDeletedMembersState, currentWorkspaceDeletedMembersState,
); );
const { loading: queryLoading, data: queryData } = useGetCurrentUserQuery({ const { data: queryData, loading: queryLoading } = useGetCurrentUserQuery({
skip: skip:
!isLoggedIn ||
isCurrentUserLoaded || isCurrentUserLoaded ||
isMatchingLocation(location, AppPath.Verify) || isMatchingLocation(location, AppPath.Verify) ||
isMatchingLocation(location, AppPath.VerifyEmail), isMatchingLocation(location, AppPath.VerifyEmail),
@ -77,7 +80,6 @@ export const UserProviderEffect = () => {
useEffect(() => { useEffect(() => {
if (!queryLoading) { if (!queryLoading) {
setIsLoading(false);
setIsCurrentUserLoaded(true); setIsCurrentUserLoaded(true);
} }
@ -159,15 +161,14 @@ export const UserProviderEffect = () => {
setWorkspaces(workspaces); setWorkspaces(workspaces);
} }
}, [ }, [
queryLoading,
queryData?.currentUser,
setCurrentUser, setCurrentUser,
setCurrentUserWorkspace, setCurrentUserWorkspace,
setCurrentWorkspaceMembers, setCurrentWorkspaceMembers,
isLoading,
queryLoading,
setCurrentWorkspace, setCurrentWorkspace,
setCurrentWorkspaceMember, setCurrentWorkspaceMember,
setWorkspaces, setWorkspaces,
queryData?.currentUser,
setIsCurrentUserLoaded, setIsCurrentUserLoaded,
setDateTimeFormat, setDateTimeFormat,
setCurrentWorkspaceMembersWithDeleted, setCurrentWorkspaceMembersWithDeleted,

View File

@ -1,8 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilState, useSetRecoilState } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util'; import { getDateFnsLocale } from '@/ui/field/display/utils/getDateFnsLocale.util';
@ -29,7 +28,6 @@ export const LocalePicker = () => {
currentWorkspaceMemberState, currentWorkspaceMemberState,
); );
const setDateLocale = useSetRecoilState(dateLocaleState); const setDateLocale = useSetRecoilState(dateLocaleState);
const isDebugMode = useRecoilValue(isDebugModeState);
const { updateOneRecord } = useUpdateOneRecord({ const { updateOneRecord } = useUpdateOneRecord({
objectNameSingular: CoreObjectNameSingular.WorkspaceMember, objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
@ -204,7 +202,7 @@ export const LocalePicker = () => {
}, },
]; ];
if (isDebugMode) { if (process.env.NODE_ENV === 'development') {
unsortedLocaleOptions.push({ unsortedLocaleOptions.push({
label: t`Pseudo-English`, label: t`Pseudo-English`,
value: APP_LOCALES['pseudo-en'], value: APP_LOCALES['pseudo-en'],

View File

@ -24,6 +24,7 @@ export default defineConfig(({ command, mode }) => {
SSL_CERT_PATH, SSL_CERT_PATH,
SSL_KEY_PATH, SSL_KEY_PATH,
REACT_APP_PORT, REACT_APP_PORT,
IS_DEBUG_MODE,
} = env; } = env;
const port = isNonEmptyString(REACT_APP_PORT) const port = isNonEmptyString(REACT_APP_PORT)
@ -191,6 +192,7 @@ export default defineConfig(({ command, mode }) => {
}, },
'process.env': { 'process.env': {
REACT_APP_SERVER_BASE_URL, REACT_APP_SERVER_BASE_URL,
IS_DEBUG_MODE,
}, },
}, },
css: { css: {