mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-07 09:17:31 +03:00
feat: auth race condition & optimize ApolloFactory & too many pageview (#602)
This commit is contained in:
parent
55576cb638
commit
718ad721cf
@ -1,17 +1,26 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import usePrevious from '@/utils/hooks/usePrevious';
|
||||||
|
|
||||||
import { useEventTracker } from './useEventTracker';
|
import { useEventTracker } from './useEventTracker';
|
||||||
|
|
||||||
export function useTrackPageView() {
|
export function useTrackPageView() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const previousLocation = usePrevious(location);
|
||||||
const eventTracker = useEventTracker();
|
const eventTracker = useEventTracker();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventTracker('pageview', {
|
// Avoid lot of pageview events enven if the location is the same
|
||||||
location: {
|
if (
|
||||||
pathname: location.pathname,
|
!previousLocation?.pathname ||
|
||||||
},
|
previousLocation?.pathname !== location.pathname
|
||||||
});
|
) {
|
||||||
}, [location, eventTracker]);
|
eventTracker('pageview', {
|
||||||
|
location: {
|
||||||
|
pathname: location.pathname,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [location, eventTracker, previousLocation?.pathname]);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||||
|
import { useUpdateEffect } from '@/utils/hooks/useUpdateEffect';
|
||||||
import { CommentThreadTarget } from '~/generated/graphql';
|
import { CommentThreadTarget } from '~/generated/graphql';
|
||||||
|
|
||||||
import { ApolloFactory } from '../services/apollo.factory';
|
import { ApolloFactory } from '../services/apollo.factory';
|
||||||
@ -54,7 +55,7 @@ export function useApolloFactory() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [setTokenPair, isDebugMode]);
|
}, [setTokenPair, isDebugMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useUpdateEffect(() => {
|
||||||
if (apolloRef.current) {
|
if (apolloRef.current) {
|
||||||
apolloRef.current.updateTokenPair(tokenPair);
|
apolloRef.current.updateTokenPair(tokenPair);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const isMockModeState = atom({
|
|
||||||
key: 'isMockModeState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
@ -1,14 +1,9 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { isMockModeState } from '@/auth/states/isMockModeState';
|
|
||||||
import { Companies } from '~/pages/companies/Companies';
|
|
||||||
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
|
import { CompaniesMockMode } from '~/pages/companies/CompaniesMockMode';
|
||||||
|
|
||||||
export function AuthLayout({ children }: React.PropsWithChildren) {
|
export function AuthLayout({ children }: React.PropsWithChildren) {
|
||||||
const isMockMode = useRecoilValue(isMockModeState);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isMockMode ? <CompaniesMockMode /> : <Companies />}
|
<CompaniesMockMode />
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
13
front/src/modules/utils/hooks/useFirstMountState.ts
Normal file
13
front/src/modules/utils/hooks/useFirstMountState.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
export function useFirstMountState(): boolean {
|
||||||
|
const isFirst = useRef(true);
|
||||||
|
|
||||||
|
if (isFirst.current) {
|
||||||
|
isFirst.current = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isFirst.current;
|
||||||
|
}
|
11
front/src/modules/utils/hooks/usePrevious.ts
Normal file
11
front/src/modules/utils/hooks/usePrevious.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function usePrevious<T>(state: T): T | undefined {
|
||||||
|
const ref = useRef<T>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = state;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ref.current;
|
||||||
|
}
|
14
front/src/modules/utils/hooks/useUpdateEffect.ts
Normal file
14
front/src/modules/utils/hooks/useUpdateEffect.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { DependencyList, EffectCallback, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useFirstMountState } from './useFirstMountState';
|
||||||
|
|
||||||
|
export function useUpdateEffect(effect: EffectCallback, deps?: DependencyList) {
|
||||||
|
const isFirst = useFirstMountState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFirst) {
|
||||||
|
return effect();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, deps);
|
||||||
|
}
|
@ -9,7 +9,6 @@ import { SubTitle } from '@/auth/components/ui/SubTitle';
|
|||||||
import { Title } from '@/auth/components/ui/Title';
|
import { Title } from '@/auth/components/ui/Title';
|
||||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import { isMockModeState } from '@/auth/states/isMockModeState';
|
|
||||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||||
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
||||||
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
||||||
@ -39,7 +38,6 @@ const StyledButtonContainer = styled.div`
|
|||||||
|
|
||||||
export function CreateProfile() {
|
export function CreateProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [, setMockMode] = useRecoilState(isMockModeState);
|
|
||||||
const onboardingStatus = useOnboardingStatus();
|
const onboardingStatus = useOnboardingStatus();
|
||||||
|
|
||||||
const [currentUser] = useRecoilState(currentUserState);
|
const [currentUser] = useRecoilState(currentUserState);
|
||||||
@ -93,11 +91,10 @@ export function CreateProfile() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMockMode(true);
|
|
||||||
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
|
if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) {
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}
|
}
|
||||||
}, [onboardingStatus, navigate, setMockMode]);
|
}, [onboardingStatus, navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -2,12 +2,10 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { getOperationName } from '@apollo/client/utilities';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { SubTitle } from '@/auth/components/ui/SubTitle';
|
import { SubTitle } from '@/auth/components/ui/SubTitle';
|
||||||
import { Title } from '@/auth/components/ui/Title';
|
import { Title } from '@/auth/components/ui/Title';
|
||||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||||
import { isMockModeState } from '@/auth/states/isMockModeState';
|
|
||||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||||
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
||||||
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
||||||
@ -36,7 +34,6 @@ const StyledButtonContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function CreateWorkspace() {
|
export function CreateWorkspace() {
|
||||||
const [, setMockMode] = useRecoilState(isMockModeState);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const onboardingStatus = useOnboardingStatus();
|
const onboardingStatus = useOnboardingStatus();
|
||||||
|
|
||||||
@ -82,11 +79,10 @@ export function CreateWorkspace() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMockMode(true);
|
|
||||||
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceCreation) {
|
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceCreation) {
|
||||||
navigate('/auth/create/profile');
|
navigate('/auth/create/profile');
|
||||||
}
|
}
|
||||||
}, [onboardingStatus, navigate, setMockMode]);
|
}, [onboardingStatus, navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -10,7 +10,6 @@ import { HorizontalSeparator } from '@/auth/components/ui/HorizontalSeparator';
|
|||||||
import { Logo } from '@/auth/components/ui/Logo';
|
import { Logo } from '@/auth/components/ui/Logo';
|
||||||
import { Title } from '@/auth/components/ui/Title';
|
import { Title } from '@/auth/components/ui/Title';
|
||||||
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
|
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
|
||||||
import { isMockModeState } from '@/auth/states/isMockModeState';
|
|
||||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||||
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
|
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
|
||||||
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
||||||
@ -34,7 +33,6 @@ const StyledFooterNote = styled(FooterNote)`
|
|||||||
export function Index() {
|
export function Index() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [, setMockMode] = useRecoilState(isMockModeState);
|
|
||||||
const [authProviders] = useRecoilState(authProvidersState);
|
const [authProviders] = useRecoilState(authProvidersState);
|
||||||
const [demoMode] = useRecoilState(isDemoModeState);
|
const [demoMode] = useRecoilState(isDemoModeState);
|
||||||
|
|
||||||
@ -67,9 +65,8 @@ export function Index() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMockMode(true);
|
|
||||||
setAuthFlowUserEmail(demoMode ? 'tim@apple.dev' : '');
|
setAuthFlowUserEmail(demoMode ? 'tim@apple.dev' : '');
|
||||||
}, [navigate, setMockMode, setAuthFlowUserEmail, demoMode]);
|
}, [navigate, setAuthFlowUserEmail, demoMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
@ -9,7 +9,6 @@ import { SubTitle } from '@/auth/components/ui/SubTitle';
|
|||||||
import { Title } from '@/auth/components/ui/Title';
|
import { Title } from '@/auth/components/ui/Title';
|
||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
|
import { authFlowUserEmailState } from '@/auth/states/authFlowUserEmailState';
|
||||||
import { isMockModeState } from '@/auth/states/isMockModeState';
|
|
||||||
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
|
import { isDemoModeState } from '@/client-config/states/isDemoModeState';
|
||||||
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/hotkeys/hooks/useScopedHotkeys';
|
||||||
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
import { InternalHotkeysScope } from '@/hotkeys/types/internal/InternalHotkeysScope';
|
||||||
@ -51,12 +50,11 @@ const StyledErrorContainer = styled.div`
|
|||||||
|
|
||||||
export function PasswordLogin() {
|
export function PasswordLogin() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDemoMode] = useRecoilState(isDemoModeState);
|
|
||||||
|
|
||||||
|
const [isDemoMode] = useRecoilState(isDemoModeState);
|
||||||
const [authFlowUserEmail, setAuthFlowUserEmail] = useRecoilState(
|
const [authFlowUserEmail, setAuthFlowUserEmail] = useRecoilState(
|
||||||
authFlowUserEmailState,
|
authFlowUserEmailState,
|
||||||
);
|
);
|
||||||
const [, setMockMode] = useRecoilState(isMockModeState);
|
|
||||||
const [internalPassword, setInternalPassword] = useState(
|
const [internalPassword, setInternalPassword] = useState(
|
||||||
isDemoMode ? 'Applecar2025' : '',
|
isDemoMode ? 'Applecar2025' : '',
|
||||||
);
|
);
|
||||||
@ -69,15 +67,10 @@ export function PasswordLogin() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMockMode(true);
|
|
||||||
}, [setMockMode]);
|
|
||||||
|
|
||||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||||
|
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setMockMode(false);
|
|
||||||
if (data?.checkUserExists.exists) {
|
if (data?.checkUserExists.exists) {
|
||||||
await login(authFlowUserEmail, internalPassword);
|
await login(authFlowUserEmail, internalPassword);
|
||||||
} else {
|
} else {
|
||||||
@ -101,7 +94,6 @@ export function PasswordLogin() {
|
|||||||
signUpToWorkspace,
|
signUpToWorkspace,
|
||||||
authFlowUserEmail,
|
authFlowUserEmail,
|
||||||
internalPassword,
|
internalPassword,
|
||||||
setMockMode,
|
|
||||||
navigate,
|
navigate,
|
||||||
data?.checkUserExists.exists,
|
data?.checkUserExists.exists,
|
||||||
|
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import { useGetCurrentUserQuery } from '~/generated/graphql';
|
import { useGetCurrentUserQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
export function UserProvider({ children }: React.PropsWithChildren) {
|
export function UserProvider({ children }: React.PropsWithChildren) {
|
||||||
const [, setCurrentUser] = useRecoilState(currentUserState);
|
const [, setCurrentUser] = useRecoilState(currentUserState);
|
||||||
const { data, loading } = useGetCurrentUserQuery();
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const isLogged = useIsLogged();
|
||||||
|
|
||||||
|
const { data, loading } = useGetCurrentUserQuery({
|
||||||
|
skip: !isLogged,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user