Remove repetitive query of ClientConfig and CurrentWorkspace member (#4859)
## Removing repetitive queries (impacting performance on page load) We have recently introduced the capability to detect schema version mismatch. To do that, we add a new header in all our queries. On page load, this header is added once we know the currentUser and especially its currentWorkspace and related schema version. However, applying this header to apollo client will re-trigger all queries that have been already performed (GetClientConfig and GetCurrentUser). To avoid re-triggering them, I'm introducing two new "isLoaded" states and skip the query if the query has already been performed ## Fixing Relation Detail not displaying data on show page Small bug introduced in a previous PR
This commit is contained in:
@ -6,6 +6,7 @@ import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { ApolloProvider } from '@/apollo/components/ApolloProvider';
|
||||
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
|
||||
import { ClientConfigProviderEffect } from '@/client-config/components/ClientConfigProviderEffect';
|
||||
import { ApolloDevLogEffect } from '@/debug/components/ApolloDevLogEffect';
|
||||
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
|
||||
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
|
||||
@ -22,6 +23,7 @@ import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/Sn
|
||||
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
|
||||
import { ThemeType } from '@/ui/theme/constants/ThemeLight';
|
||||
import { UserProvider } from '@/users/components/UserProvider';
|
||||
import { UserProviderEffect } from '@/users/components/UserProviderEffect';
|
||||
import { PageChangeEffect } from '~/effect-components/PageChangeEffect';
|
||||
|
||||
import '@emotion/react';
|
||||
@ -46,7 +48,9 @@ root.render(
|
||||
<ExceptionHandlerProvider>
|
||||
<ApolloProvider>
|
||||
<HelmetProvider>
|
||||
<ClientConfigProviderEffect />
|
||||
<ClientConfigProvider>
|
||||
<UserProviderEffect />
|
||||
<UserProvider>
|
||||
<ApolloMetadataClientProvider>
|
||||
<ObjectMetadataItemsProvider>
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const isCurrentUserLoadedState = createState<boolean>({
|
||||
key: 'isCurrentUserLoadedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,64 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
|
||||
import { isSignUpDisabledState } from '@/client-config/states/isSignUpDisabledState';
|
||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { telemetryState } from '@/client-config/states/telemetryState';
|
||||
import { useGetClientConfigQuery } from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isClientConfigLoadedState } from '@/client-config/states/isClientConfigLoadedState';
|
||||
|
||||
export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const setAuthProviders = useSetRecoilState(authProvidersState);
|
||||
const setIsDebugMode = useSetRecoilState(isDebugModeState);
|
||||
const isClientConfigLoaded = useRecoilValue(isClientConfigLoadedState);
|
||||
|
||||
const setIsSignInPrefilled = useSetRecoilState(isSignInPrefilledState);
|
||||
const setIsSignUpDisabled = useSetRecoilState(isSignUpDisabledState);
|
||||
|
||||
const setBilling = useSetRecoilState(billingState);
|
||||
const setTelemetry = useSetRecoilState(telemetryState);
|
||||
const setSupportChat = useSetRecoilState(supportChatState);
|
||||
|
||||
const setSentryConfig = useSetRecoilState(sentryConfigState);
|
||||
|
||||
const { data, loading } = useGetClientConfigQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(data?.clientConfig)) {
|
||||
setAuthProviders({
|
||||
google: data?.clientConfig.authProviders.google,
|
||||
password: data?.clientConfig.authProviders.password,
|
||||
magicLink: false,
|
||||
});
|
||||
setIsDebugMode(data?.clientConfig.debugMode);
|
||||
setIsSignInPrefilled(data?.clientConfig.signInPrefilled);
|
||||
setIsSignUpDisabled(data?.clientConfig.signUpDisabled);
|
||||
|
||||
setBilling(data?.clientConfig.billing);
|
||||
setTelemetry(data?.clientConfig.telemetry);
|
||||
setSupportChat(data?.clientConfig.support);
|
||||
|
||||
setSentryConfig({
|
||||
dsn: data?.clientConfig?.sentry?.dsn,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
setAuthProviders,
|
||||
setIsDebugMode,
|
||||
setIsSignInPrefilled,
|
||||
setIsSignUpDisabled,
|
||||
setTelemetry,
|
||||
setSupportChat,
|
||||
setBilling,
|
||||
setSentryConfig,
|
||||
]);
|
||||
|
||||
return loading ? <></> : <>{children}</>;
|
||||
return isClientConfigLoaded ? <>{children}</> : <></>;
|
||||
};
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
import { isClientConfigLoadedState } from '@/client-config/states/isClientConfigLoadedState';
|
||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
|
||||
import { isSignUpDisabledState } from '@/client-config/states/isSignUpDisabledState';
|
||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { telemetryState } from '@/client-config/states/telemetryState';
|
||||
import { useGetClientConfigQuery } from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const ClientConfigProviderEffect = () => {
|
||||
const setAuthProviders = useSetRecoilState(authProvidersState);
|
||||
const setIsDebugMode = useSetRecoilState(isDebugModeState);
|
||||
|
||||
const setIsSignInPrefilled = useSetRecoilState(isSignInPrefilledState);
|
||||
const setIsSignUpDisabled = useSetRecoilState(isSignUpDisabledState);
|
||||
|
||||
const setBilling = useSetRecoilState(billingState);
|
||||
const setTelemetry = useSetRecoilState(telemetryState);
|
||||
const setSupportChat = useSetRecoilState(supportChatState);
|
||||
|
||||
const setSentryConfig = useSetRecoilState(sentryConfigState);
|
||||
const [isClientConfigLoaded, setIsClientConfigLoaded] = useRecoilState(
|
||||
isClientConfigLoadedState,
|
||||
);
|
||||
|
||||
const { data, loading } = useGetClientConfigQuery({
|
||||
skip: isClientConfigLoaded,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && isDefined(data?.clientConfig)) {
|
||||
setIsClientConfigLoaded(true);
|
||||
setAuthProviders({
|
||||
google: data?.clientConfig.authProviders.google,
|
||||
password: data?.clientConfig.authProviders.password,
|
||||
magicLink: false,
|
||||
});
|
||||
setIsDebugMode(data?.clientConfig.debugMode);
|
||||
setIsSignInPrefilled(data?.clientConfig.signInPrefilled);
|
||||
setIsSignUpDisabled(data?.clientConfig.signUpDisabled);
|
||||
|
||||
setBilling(data?.clientConfig.billing);
|
||||
setTelemetry(data?.clientConfig.telemetry);
|
||||
setSupportChat(data?.clientConfig.support);
|
||||
|
||||
setSentryConfig({
|
||||
dsn: data?.clientConfig?.sentry?.dsn,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
setAuthProviders,
|
||||
setIsDebugMode,
|
||||
setIsSignInPrefilled,
|
||||
setIsSignUpDisabled,
|
||||
setTelemetry,
|
||||
setSupportChat,
|
||||
setBilling,
|
||||
setSentryConfig,
|
||||
loading,
|
||||
setIsClientConfigLoaded,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -0,0 +1,6 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const isClientConfigLoadedState = createState<boolean>({
|
||||
key: 'isClientConfigLoadedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -32,8 +32,12 @@ export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
||||
findOneRecord: ({ objectRecordId, onCompleted }: FindOneRecordParams<T>) =>
|
||||
findOneRecord({
|
||||
variables: { objectRecordId },
|
||||
onCompleted: (data) =>
|
||||
onCompleted?.(getRecordFromRecordNode(data[objectNameSingular])),
|
||||
onCompleted: (data) => {
|
||||
const record = getRecordFromRecordNode<T>({
|
||||
recordNode: data[objectNameSingular],
|
||||
});
|
||||
onCompleted?.(record);
|
||||
},
|
||||
}),
|
||||
called,
|
||||
error,
|
||||
|
||||
@ -1,67 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { workspacesState } from '@/auth/states/workspaces';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { User } from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
|
||||
|
||||
export const UserProvider = ({ children }: React.PropsWithChildren) => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const isCurrentUserLoaded = useRecoilValue(isCurrentUserLoadedState);
|
||||
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setWorkspaces = useSetRecoilState(workspacesState);
|
||||
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { loading: queryLoading, data: queryData } = useQuery<{
|
||||
currentUser: User;
|
||||
}>(GET_CURRENT_USER);
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryLoading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
if (!isDefined(queryData?.currentUser)) return;
|
||||
|
||||
setCurrentUser(queryData.currentUser);
|
||||
setCurrentWorkspace(queryData.currentUser.defaultWorkspace);
|
||||
|
||||
const { workspaceMember, workspaces: userWorkspaces } =
|
||||
queryData.currentUser;
|
||||
|
||||
if (isDefined(workspaceMember)) {
|
||||
setCurrentWorkspaceMember({
|
||||
...workspaceMember,
|
||||
colorScheme: (workspaceMember.colorScheme as ColorScheme) ?? 'Light',
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(userWorkspaces)) {
|
||||
const workspaces = userWorkspaces
|
||||
.map(({ workspace }) => workspace)
|
||||
.filter(isDefined);
|
||||
|
||||
setWorkspaces(workspaces);
|
||||
}
|
||||
}, [
|
||||
setCurrentUser,
|
||||
isLoading,
|
||||
queryLoading,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
setWorkspaces,
|
||||
queryData?.currentUser,
|
||||
]);
|
||||
|
||||
return isLoading ? <></> : <>{children}</>;
|
||||
return !isCurrentUserLoaded ? <></> : <>{children}</>;
|
||||
};
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
|
||||
import { workspacesState } from '@/auth/states/workspaces';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { User } from '~/generated/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const UserProviderEffect = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState(
|
||||
isCurrentUserLoadedState,
|
||||
);
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setWorkspaces = useSetRecoilState(workspacesState);
|
||||
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { loading: queryLoading, data: queryData } = useQuery<{
|
||||
currentUser: User;
|
||||
}>(GET_CURRENT_USER, { skip: isCurrentUserLoaded });
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryLoading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
if (!isDefined(queryData?.currentUser)) return;
|
||||
|
||||
setIsCurrentUserLoaded(true);
|
||||
setCurrentUser(queryData.currentUser);
|
||||
setCurrentWorkspace(queryData.currentUser.defaultWorkspace);
|
||||
|
||||
const { workspaceMember, workspaces: userWorkspaces } =
|
||||
queryData.currentUser;
|
||||
|
||||
if (isDefined(workspaceMember)) {
|
||||
setCurrentWorkspaceMember({
|
||||
...workspaceMember,
|
||||
colorScheme: (workspaceMember.colorScheme as ColorScheme) ?? 'Light',
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(userWorkspaces)) {
|
||||
const workspaces = userWorkspaces
|
||||
.map(({ workspace }) => workspace)
|
||||
.filter(isDefined);
|
||||
|
||||
setWorkspaces(workspaces);
|
||||
}
|
||||
}, [
|
||||
setCurrentUser,
|
||||
isLoading,
|
||||
queryLoading,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
setWorkspaces,
|
||||
queryData?.currentUser,
|
||||
setIsCurrentUserLoaded,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
Reference in New Issue
Block a user