diff --git a/packages/twenty-front/src/index.tsx b/packages/twenty-front/src/index.tsx index ef0192fd418e..756d1cc59d67 100644 --- a/packages/twenty-front/src/index.tsx +++ b/packages/twenty-front/src/index.tsx @@ -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( + + diff --git a/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts b/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts new file mode 100644 index 000000000000..0a62d92ab4cf --- /dev/null +++ b/packages/twenty-front/src/modules/auth/states/isCurrentUserLoadingState.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui'; + +export const isCurrentUserLoadedState = createState({ + key: 'isCurrentUserLoadedState', + defaultValue: false, +}); diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx index db62ad64fa8e..671842687435 100644 --- a/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx +++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProvider.tsx @@ -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 = ({ 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} : <>; }; diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx new file mode 100644 index 000000000000..e8d57ee71a76 --- /dev/null +++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx @@ -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 <>; +}; diff --git a/packages/twenty-front/src/modules/client-config/states/isClientConfigLoadedState.ts b/packages/twenty-front/src/modules/client-config/states/isClientConfigLoadedState.ts new file mode 100644 index 000000000000..7b6cff0b3612 --- /dev/null +++ b/packages/twenty-front/src/modules/client-config/states/isClientConfigLoadedState.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui'; + +export const isClientConfigLoadedState = createState({ + key: 'isClientConfigLoadedState', + defaultValue: false, +}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts index e905b58a2c6b..c8a37252634f 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts @@ -32,8 +32,12 @@ export const useLazyFindOneRecord = ({ findOneRecord: ({ objectRecordId, onCompleted }: FindOneRecordParams) => findOneRecord({ variables: { objectRecordId }, - onCompleted: (data) => - onCompleted?.(getRecordFromRecordNode(data[objectNameSingular])), + onCompleted: (data) => { + const record = getRecordFromRecordNode({ + recordNode: data[objectNameSingular], + }); + onCompleted?.(record); + }, }), called, error, diff --git a/packages/twenty-front/src/modules/users/components/UserProvider.tsx b/packages/twenty-front/src/modules/users/components/UserProvider.tsx index e1cc61fdfced..5287cb4e24f4 100644 --- a/packages/twenty-front/src/modules/users/components/UserProvider.tsx +++ b/packages/twenty-front/src/modules/users/components/UserProvider.tsx @@ -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}; }; diff --git a/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx b/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx new file mode 100644 index 000000000000..f4f10c417ef9 --- /dev/null +++ b/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx @@ -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 <>; +};