## Context With the new permissions system, we now need to hide some items from the settings navigation and gate some routes so they can't be accessed directly. To avoid having to set permission gates in all the component pages, I'm introducing wrapper at the route level and in the Navigation. This is not required and is mostly for pages that are strictly mapped to a single permission, for the rest we still need to use the different hooks manually but it should avoid a bit of boilerplate for most of the cases. - currentUserWorkspaceState to access settingsPermissions - SettingsProtectedRouteWrapper in the router that can take a settingFeature or a featureFlag as a gate logic, if the currentUser does not have access to the settingFeature or the featureFlag is not enabled they will be redirected to the profile page. - SettingsNavigationItemWrapper & SettingsNavigationSectionWrapper. The former will check the same logic as SettingsProtectedRouteWrapper and not display the item if needed. The later will check if all SettingsNavigationItemWrapper are not visible and hide itself if that's the case. - useHasSettingsPermission to get a specific permission state for the current user - useSettingsPermissionMap to get a map of all permissions with their values for the current user - useFeatureFlagsMap same but for featureFlags
121 lines
4.3 KiB
TypeScript
121 lines
4.3 KiB
TypeScript
import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
|
import { useMemo, useRef } from 'react';
|
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
|
|
|
import { currentUserState } from '@/auth/states/currentUserState';
|
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|
import { previousUrlState } from '@/auth/states/previousUrlState';
|
|
import { tokenPairState } from '@/auth/states/tokenPairState';
|
|
import { workspacesState } from '@/auth/states/workspaces';
|
|
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
|
import { isDefined } from 'twenty-shared';
|
|
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
|
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
|
|
|
|
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
|
import { AppPath } from '@/types/AppPath';
|
|
import { ApolloFactory, Options } from '../services/apollo.factory';
|
|
|
|
export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
|
|
// eslint-disable-next-line @nx/workspace-no-state-useref
|
|
const apolloRef = useRef<ApolloFactory<NormalizedCacheObject> | null>(null);
|
|
const [isDebugMode] = useRecoilState(isDebugModeState);
|
|
|
|
const navigate = useNavigate();
|
|
const { isMatchingLocation } = useIsMatchingLocation();
|
|
const [tokenPair, setTokenPair] = useRecoilState(tokenPairState);
|
|
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
|
currentWorkspaceState,
|
|
);
|
|
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
|
const setCurrentUser = useSetRecoilState(currentUserState);
|
|
const setCurrentWorkspaceMember = useSetRecoilState(
|
|
currentWorkspaceMemberState,
|
|
);
|
|
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
|
|
|
|
const setWorkspaces = useSetRecoilState(workspacesState);
|
|
const [, setPreviousUrl] = useRecoilState(previousUrlState);
|
|
const location = useLocation();
|
|
|
|
const apolloClient = useMemo(() => {
|
|
apolloRef.current = new ApolloFactory({
|
|
uri: `${REACT_APP_SERVER_BASE_URL}/graphql`,
|
|
cache: new InMemoryCache({
|
|
typePolicies: {
|
|
RemoteTable: {
|
|
keyFields: ['name'],
|
|
},
|
|
},
|
|
}),
|
|
headers: {
|
|
...(currentWorkspace?.metadataVersion && {
|
|
'X-Schema-Version': `${currentWorkspace.metadataVersion}`,
|
|
}),
|
|
},
|
|
defaultOptions: {
|
|
query: {
|
|
fetchPolicy: 'cache-first',
|
|
},
|
|
},
|
|
connectToDevTools: isDebugMode,
|
|
// We don't want to re-create the client on token change or it will cause infinite loop
|
|
initialTokenPair: tokenPair,
|
|
currentWorkspaceMember: currentWorkspaceMember,
|
|
onTokenPairChange: (tokenPair) => {
|
|
setTokenPair(tokenPair);
|
|
},
|
|
onUnauthenticatedError: () => {
|
|
setTokenPair(null);
|
|
setCurrentUser(null);
|
|
setCurrentWorkspaceMember(null);
|
|
setCurrentWorkspace(null);
|
|
setCurrentUserWorkspace(null);
|
|
setWorkspaces([]);
|
|
if (
|
|
!isMatchingLocation(AppPath.Verify) &&
|
|
!isMatchingLocation(AppPath.SignInUp) &&
|
|
!isMatchingLocation(AppPath.Invite) &&
|
|
!isMatchingLocation(AppPath.ResetPassword)
|
|
) {
|
|
setPreviousUrl(`${location.pathname}${location.search}`);
|
|
navigate(AppPath.SignInUp);
|
|
}
|
|
},
|
|
extraLinks: [],
|
|
isDebugMode,
|
|
// Override options
|
|
...options,
|
|
});
|
|
|
|
return apolloRef.current.getClient();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [
|
|
setTokenPair,
|
|
setCurrentUser,
|
|
setCurrentWorkspaceMember,
|
|
setCurrentWorkspace,
|
|
setWorkspaces,
|
|
isDebugMode,
|
|
currentWorkspace?.metadataVersion,
|
|
setPreviousUrl,
|
|
]);
|
|
|
|
useUpdateEffect(() => {
|
|
if (isDefined(apolloRef.current)) {
|
|
apolloRef.current.updateTokenPair(tokenPair);
|
|
}
|
|
}, [tokenPair]);
|
|
|
|
useUpdateEffect(() => {
|
|
if (isDefined(apolloRef.current)) {
|
|
apolloRef.current.updateWorkspaceMember(currentWorkspaceMember);
|
|
}
|
|
}, [currentWorkspaceMember]);
|
|
|
|
return apolloClient;
|
|
};
|