Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15d34da5ff | |||
| 674f4353cd | |||
| c1a7726c33 |
@@ -5,6 +5,7 @@ import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCu
|
||||
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -49,7 +50,9 @@ export const usePageChangeEffectNavigateLocation = () => {
|
||||
const objectMetadataItem = objectMetadataItems?.find(
|
||||
(objectMetadataItem) => objectMetadataItem.namePlural === objectNamePlural,
|
||||
);
|
||||
const verifyEmailRedirectPath = useRecoilValue(verifyEmailRedirectPathState);
|
||||
const verifyEmailRedirectPath = useRecoilValueV2(
|
||||
verifyEmailRedirectPathState,
|
||||
);
|
||||
|
||||
if (
|
||||
(!isLoggedIn || (isLoggedIn && !isOnAWorkspace)) &&
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/r
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -136,7 +137,7 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
forceRegisteredActionsByKeyState,
|
||||
);
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
const isFeatureFlagEnabled = (featureFlagKey: FeatureFlagKey) => {
|
||||
const featureFlag = currentWorkspace?.featureFlags?.find(
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { format } from 'date-fns';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import {
|
||||
CalendarChannelVisibility,
|
||||
type TimelineCalendarEvent,
|
||||
@@ -85,7 +85,7 @@ export const CalendarEventRow = ({
|
||||
className,
|
||||
}: CalendarEventRowProps) => {
|
||||
const theme = useTheme();
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const { openCalendarEventInCommandMenu } =
|
||||
useOpenCalendarEventInCommandMenu();
|
||||
|
||||
|
||||
+17
-16
@@ -2,12 +2,14 @@ import { gql, InMemoryCache } from '@apollo/client';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { SnackBarComponentInstanceContext } from '@/ui/feedback/snack-bar-manager/contexts/SnackBarComponentInstanceContext';
|
||||
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
|
||||
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
||||
@@ -111,26 +113,27 @@ const task = {
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<MockedProvider cache={cache}>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>
|
||||
<MockedProvider cache={cache}>
|
||||
<JestObjectMetadataItemSetter>
|
||||
<SnackBarComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'snack-bar-manager' }}
|
||||
>
|
||||
{children}
|
||||
</SnackBarComponentInstanceContext.Provider>
|
||||
</JestObjectMetadataItemSetter>
|
||||
</MockedProvider>
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useActivityTargetObjectRecords', () => {
|
||||
it('return targetObjects', async () => {
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, mockWorkspaceMembers[0]);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
@@ -145,7 +148,6 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
|
||||
return {
|
||||
activityTargetObjectRecords,
|
||||
setCurrentWorkspaceMember,
|
||||
setObjectMetadataItems,
|
||||
setRecordFromStore,
|
||||
};
|
||||
@@ -154,7 +156,6 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
result.current.setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
result.current.setRecordFromStore(task);
|
||||
});
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@@ -92,7 +93,7 @@ export const EventRow = ({
|
||||
event,
|
||||
mainObjectMetadataItem,
|
||||
}: EventRowProps) => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
|
||||
const allowRequestsToTwentyIcons = useRecoilValue(
|
||||
allowRequestsToTwentyIconsState,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { EditorContent } from '@tiptap/react';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { LightButton } from 'twenty-ui/input';
|
||||
|
||||
import { DropZone } from '@/activities/files/components/DropZone';
|
||||
@@ -23,6 +22,7 @@ import { useAIChatFileUpload } from '@/ai/hooks/useAIChatFileUpload';
|
||||
import { useAgentChatContextOrThrow } from '@/ai/hooks/useAgentChatContextOrThrow';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledContainer = styled.div<{ isDraggingFile: boolean }>`
|
||||
background: ${({ theme }) => theme.background.primary};
|
||||
@@ -144,7 +144,7 @@ export const AIChatTab = () => {
|
||||
const hasMessages = messages.length > 0;
|
||||
|
||||
const { uploadFiles } = useAIChatFileUpload();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
const smartModelLabel = useAiModelLabel(currentWorkspace?.smartModel, false);
|
||||
|
||||
const { editor, handleSendAndClear } = useAIChatEditor({
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useGetBrowsingContext } from '@/ai/hooks/useBrowsingContext';
|
||||
import { agentChatSelectedFilesStateV2 } from '@/ai/states/agentChatSelectedFilesStateV2';
|
||||
import { agentChatUploadedFilesStateV2 } from '@/ai/states/agentChatUploadedFilesStateV2';
|
||||
@@ -23,7 +21,7 @@ import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { cookieStorage } from '~/utils/cookie-storage';
|
||||
|
||||
export const useAgentChat = (uiMessages: ExtendedUIMessage[]) => {
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const setTokenPair = useSetRecoilStateV2(tokenPairState);
|
||||
const setAgentChatUsage = useSetRecoilStateV2(agentChatUsageStateV2);
|
||||
|
||||
const { getBrowsingContext } = useGetBrowsingContext();
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
import { InMemoryCache, type NormalizedCacheObject } from '@apollo/client';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { ApolloFactory, type Options } from '@/apollo/services/apollo.factory';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
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 { appVersionState } from '@/client-config/states/appVersionState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
|
||||
import { isMatchingLocation } from '~/utils/isMatchingLocation';
|
||||
|
||||
import { ApolloFactory, type Options } from '@/apollo/services/apollo.factory';
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { appVersionState } from '@/client-config/states/appVersionState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useApolloFactory = (options: Partial<Options<any>> = {}) => {
|
||||
// eslint-disable-next-line twenty/no-state-useref
|
||||
const apolloRef = useRef<ApolloFactory<NormalizedCacheObject> | null>(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
const setTokenPair = useSetRecoilStateV2(tokenPairState);
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilStateV2(
|
||||
currentWorkspaceState,
|
||||
);
|
||||
const appVersion = useRecoilValueV2(appVersionState);
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilStateV2(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentUserWorkspace = useSetRecoilState(currentUserWorkspaceState);
|
||||
const setCurrentUser = useSetRecoilStateV2(currentUserState);
|
||||
const setCurrentUserWorkspace = useSetRecoilStateV2(
|
||||
currentUserWorkspaceState,
|
||||
);
|
||||
|
||||
const setPreviousUrl = useSetRecoilStateV2(previousUrlState);
|
||||
const location = useLocation();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useCreateAppRouter } from '@/app/hooks/useCreateAppRouter';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const AppRouter = () => {
|
||||
// We want to disable logic function settings but keep the code for now
|
||||
const isFunctionSettingsEnabled = false;
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentUser = useRecoilValueV2(currentUserState);
|
||||
|
||||
const isAdminPageEnabled =
|
||||
(currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ??
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
matchPath,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import {
|
||||
setSessionId,
|
||||
useEventTracker,
|
||||
@@ -15,6 +6,15 @@ import { useExecuteTasksOnAnyLocationChange } from '@/app/hooks/useExecuteTasksO
|
||||
import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
matchPath,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
@@ -93,7 +93,7 @@ export const PageChangeEffect = () => {
|
||||
const { executeTasksOnAnyLocationChange } =
|
||||
useExecuteTasksOnAnyLocationChange();
|
||||
|
||||
const isAppEffectRedirectEnabled = useRecoilValue(
|
||||
const isAppEffectRedirectEnabled = useRecoilValueV2(
|
||||
isAppEffectRedirectEnabledState,
|
||||
);
|
||||
|
||||
@@ -366,7 +366,7 @@ export const PageChangeEffect = () => {
|
||||
}, [eventTracker, location.pathname]);
|
||||
|
||||
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
||||
const isCaptchaScriptLoaded = useRecoilValue(isCaptchaScriptLoadedState);
|
||||
const isCaptchaScriptLoaded = useRecoilValueV2(isCaptchaScriptLoadedState);
|
||||
|
||||
useEffect(() => {
|
||||
if (isCaptchaScriptLoaded && isCaptchaRequiredForPath(location.pathname)) {
|
||||
|
||||
@@ -1,63 +1,60 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
||||
import { type BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
||||
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
// Initialize state that are hydrated from query parameters
|
||||
// We used to use recoil-sync to do this, but it was causing issues with Firefox
|
||||
export const useInitializeQueryParamState = () => {
|
||||
const initializeQueryParamState = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
() => {
|
||||
const handlers = {
|
||||
billingCheckoutSession: (value: string) => {
|
||||
const billingCheckoutSession = snapshot
|
||||
.getLoadable(billingCheckoutSessionState)
|
||||
.getValue();
|
||||
const initializeQueryParamState = useCallback(() => {
|
||||
const handlers = {
|
||||
billingCheckoutSession: (value: string) => {
|
||||
const billingCheckoutSession = jotaiStore.get(
|
||||
billingCheckoutSessionState.atom,
|
||||
);
|
||||
|
||||
try {
|
||||
const parsedValue = JSON.parse(decodeURIComponent(value));
|
||||
try {
|
||||
const parsedValue = JSON.parse(decodeURIComponent(value));
|
||||
|
||||
if (
|
||||
typeof parsedValue === 'object' &&
|
||||
parsedValue !== null &&
|
||||
'plan' in parsedValue &&
|
||||
'interval' in parsedValue &&
|
||||
'requirePaymentMethod' in parsedValue &&
|
||||
!deepEqual(billingCheckoutSession, parsedValue)
|
||||
) {
|
||||
set(
|
||||
billingCheckoutSessionState,
|
||||
parsedValue as BillingCheckoutSession,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
'Failed to parse billingCheckoutSession from URL',
|
||||
error,
|
||||
);
|
||||
set(
|
||||
billingCheckoutSessionState,
|
||||
BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
|
||||
for (const [paramName, handler] of Object.entries(handlers)) {
|
||||
const value = queryParams.get(paramName);
|
||||
if (value !== null) {
|
||||
handler(value);
|
||||
if (
|
||||
typeof parsedValue === 'object' &&
|
||||
parsedValue !== null &&
|
||||
'plan' in parsedValue &&
|
||||
'interval' in parsedValue &&
|
||||
'requirePaymentMethod' in parsedValue &&
|
||||
!deepEqual(billingCheckoutSession, parsedValue)
|
||||
) {
|
||||
jotaiStore.set(
|
||||
billingCheckoutSessionState.atom,
|
||||
parsedValue as BillingCheckoutSession,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
'Failed to parse billingCheckoutSession from URL',
|
||||
error,
|
||||
);
|
||||
jotaiStore.set(
|
||||
billingCheckoutSessionState.atom,
|
||||
BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
||||
);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
|
||||
for (const [paramName, handler] of Object.entries(handlers)) {
|
||||
const value = queryParams.get(paramName);
|
||||
if (value !== null) {
|
||||
handler(value);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { initializeQueryParamState };
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isAppEffectRedirectEnabledState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isAppEffectRedirectEnabledState = createStateV2<boolean>({
|
||||
key: 'isAppEffectRedirectEnabledState',
|
||||
defaultValue: true,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const verifyEmailRedirectPathState = createState<string | undefined>({
|
||||
export const verifyEmailRedirectPathState = createStateV2<string | undefined>({
|
||||
key: 'verifyEmailRedirectPathState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { AuthContext } from '@/auth/contexts/AuthContext';
|
||||
import { currentWorkspaceDeletedMembersState } from '@/auth/states/currentWorkspaceDeletedMembersState';
|
||||
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const AuthProvider = ({ children }: React.PropsWithChildren) => {
|
||||
const currentWorkspaceMembers = useRecoilValue(currentWorkspaceMembersState);
|
||||
const currentWorkspaceDeletedMembers = useRecoilValue(
|
||||
const currentWorkspaceMembers = useRecoilValueV2(
|
||||
currentWorkspaceMembersState,
|
||||
);
|
||||
const currentWorkspaceDeletedMembers = useRecoilValueV2(
|
||||
currentWorkspaceDeletedMembersState,
|
||||
);
|
||||
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@ import { useCurrentUserWorkspaceTwoFactorAuthentication } from '@/settings/two-f
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -19,7 +18,7 @@ export const TwoFactorAuthenticationSetupEffect = () => {
|
||||
|
||||
const navigate = useNavigateApp();
|
||||
const { origin } = useOrigin();
|
||||
const loginToken = useRecoilValue(loginTokenState);
|
||||
const loginToken = useRecoilValueV2(loginTokenState);
|
||||
const qrCode = useRecoilValueV2(qrCodeState);
|
||||
const setQrCodeState = useSetRecoilStateV2(qrCodeState);
|
||||
|
||||
|
||||
@@ -12,11 +12,12 @@ import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
|
||||
import { EmailVerificationSent } from '@/auth/sign-in-up/components/EmailVerificationSent';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
|
||||
export const VerifyEmailEffect = () => {
|
||||
const {
|
||||
@@ -29,7 +30,7 @@ export const VerifyEmailEffect = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
const setVerifyEmailRedirectPath = useSetRecoilState(
|
||||
const setVerifyEmailRedirectPath = useSetRecoilStateV2(
|
||||
verifyEmailRedirectPathState,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/i
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { type ReactNode, act } from 'react';
|
||||
@@ -158,7 +159,7 @@ describe('useAuth', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const client = useApolloClient();
|
||||
const workspaceAuthProviders = useRecoilValue(
|
||||
const workspaceAuthProviders = useRecoilValueV2(
|
||||
workspaceAuthProvidersState,
|
||||
);
|
||||
const billing = useRecoilValue(billingState);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const isLogged = useIsLogged();
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const setTokenPair = useSetRecoilStateV2(tokenPairState);
|
||||
|
||||
return {
|
||||
isLogged,
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
useGotoRecoilSnapshot,
|
||||
useRecoilCallback,
|
||||
useRecoilValue,
|
||||
useSetRecoilState,
|
||||
} from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
@@ -29,6 +28,9 @@ import {
|
||||
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
import { isAppEffectRedirectEnabledState } from '@/app/states/isAppEffectRedirectEnabledState';
|
||||
import { useSignUpInNewWorkspace } from '@/auth/sign-in-up/hooks/useSignUpInNewWorkspace';
|
||||
@@ -71,15 +73,15 @@ import { cookieStorage } from '~/utils/cookie-storage';
|
||||
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
|
||||
|
||||
export const useAuth = () => {
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const setLoginToken = useSetRecoilState(loginTokenState);
|
||||
const setIsAppEffectRedirectEnabled = useSetRecoilState(
|
||||
const setTokenPair = useSetRecoilStateV2(tokenPairState);
|
||||
const setLoginToken = useSetRecoilStateV2(loginTokenState);
|
||||
const setIsAppEffectRedirectEnabled = useSetRecoilStateV2(
|
||||
isAppEffectRedirectEnabledState,
|
||||
);
|
||||
|
||||
const { origin } = useOrigin();
|
||||
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
|
||||
const isCaptchaScriptLoaded = useRecoilValue(isCaptchaScriptLoadedState);
|
||||
const isCaptchaScriptLoaded = useRecoilValueV2(isCaptchaScriptLoadedState);
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const isEmailVerificationRequired = useRecoilValue(
|
||||
isEmailVerificationRequiredState,
|
||||
@@ -89,7 +91,7 @@ export const useAuth = () => {
|
||||
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
|
||||
const { createWorkspace } = useSignUpInNewWorkspace();
|
||||
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
const { redirect } = useRedirect();
|
||||
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
||||
|
||||
@@ -106,7 +108,7 @@ export const useAuth = () => {
|
||||
useVerifyEmailAndGetWorkspaceAgnosticTokenMutation();
|
||||
const [getAuthTokensFromOtp] = useGetAuthTokensFromOtpMutation();
|
||||
|
||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||
const workspacePublicData = useRecoilValueV2(workspacePublicDataState);
|
||||
|
||||
const { setLastAuthenticateWorkspaceDomain } =
|
||||
useLastAuthenticatedWorkspaceDomain();
|
||||
@@ -123,7 +125,7 @@ export const useAuth = () => {
|
||||
const { loadMockedObjectMetadataItems } = useLoadMockedObjectMetadataItems();
|
||||
|
||||
const clearSession = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const sseClient = getSnapshotValue(snapshot, sseClientState);
|
||||
|
||||
@@ -131,9 +133,9 @@ export const useAuth = () => {
|
||||
|
||||
const emptySnapshot = snapshot_UNSTABLE();
|
||||
|
||||
const authProvidersValue = snapshot
|
||||
.getLoadable(workspaceAuthProvidersState)
|
||||
.getValue();
|
||||
const authProvidersValue = jotaiStore.get(
|
||||
workspaceAuthProvidersState.atom,
|
||||
);
|
||||
const billing = snapshot.getLoadable(billingState).getValue();
|
||||
const isDeveloperDefaultSignInPrefilled = snapshot
|
||||
.getLoadable(isDeveloperDefaultSignInPrefilledState)
|
||||
@@ -143,26 +145,28 @@ export const useAuth = () => {
|
||||
const clientConfigApiStatus = snapshot
|
||||
.getLoadable(clientConfigApiStatusState)
|
||||
.getValue();
|
||||
const isCurrentUserLoaded = snapshot
|
||||
.getLoadable(isCurrentUserLoadedState)
|
||||
.getValue();
|
||||
const isCurrentUserLoadedValue = jotaiStore.get(
|
||||
isCurrentUserLoadedState.atom,
|
||||
);
|
||||
const isMultiWorkspaceEnabled = snapshot
|
||||
.getLoadable(isMultiWorkspaceEnabledState)
|
||||
.getValue();
|
||||
const domainConfiguration = snapshot
|
||||
.getLoadable(domainConfigurationState)
|
||||
.getValue();
|
||||
const domainConfigurationValue = jotaiStore.get(
|
||||
domainConfigurationState.atom,
|
||||
);
|
||||
const apiConfig = snapshot.getLoadable(apiConfigState).getValue();
|
||||
const sentryConfig = snapshot.getLoadable(sentryConfigState).getValue();
|
||||
const workspacePublicData = snapshot
|
||||
.getLoadable(workspacePublicDataState)
|
||||
.getValue();
|
||||
const lastAuthenticatedMethod = snapshot
|
||||
.getLoadable(lastAuthenticatedMethodState)
|
||||
.getValue();
|
||||
const workspacePublicDataValue = jotaiStore.get(
|
||||
workspacePublicDataState.atom,
|
||||
);
|
||||
const lastAuthenticatedMethod = jotaiStore.get(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
);
|
||||
const isCaptchaScriptLoadedValue = jotaiStore.get(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
);
|
||||
|
||||
const initialSnapshot = emptySnapshot.map(({ set }) => {
|
||||
set(workspaceAuthProvidersState, authProvidersValue);
|
||||
set(billingState, billing);
|
||||
set(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
@@ -172,12 +176,8 @@ export const useAuth = () => {
|
||||
set(captchaState, captcha);
|
||||
set(apiConfigState, apiConfig);
|
||||
set(sentryConfigState, sentryConfig);
|
||||
set(workspacePublicDataState, workspacePublicData);
|
||||
set(clientConfigApiStatusState, clientConfigApiStatus);
|
||||
set(isCurrentUserLoadedState, isCurrentUserLoaded);
|
||||
set(isMultiWorkspaceEnabledState, isMultiWorkspaceEnabled);
|
||||
set(domainConfigurationState, domainConfiguration);
|
||||
set(isCaptchaScriptLoadedState, isCaptchaScriptLoaded);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
@@ -186,7 +186,18 @@ export const useAuth = () => {
|
||||
|
||||
goToRecoilSnapshot(initialSnapshot);
|
||||
|
||||
set(lastAuthenticatedMethodState, lastAuthenticatedMethod);
|
||||
jotaiStore.set(workspaceAuthProvidersState.atom, authProvidersValue);
|
||||
jotaiStore.set(workspacePublicDataState.atom, workspacePublicDataValue);
|
||||
jotaiStore.set(isCurrentUserLoadedState.atom, isCurrentUserLoadedValue);
|
||||
jotaiStore.set(domainConfigurationState.atom, domainConfigurationValue);
|
||||
jotaiStore.set(
|
||||
isCaptchaScriptLoadedState.atom,
|
||||
isCaptchaScriptLoadedValue,
|
||||
);
|
||||
jotaiStore.set(
|
||||
lastAuthenticatedMethodState.atom,
|
||||
lastAuthenticatedMethod,
|
||||
);
|
||||
|
||||
await client.clearStore();
|
||||
setLastAuthenticateWorkspaceDomain(null);
|
||||
@@ -199,7 +210,6 @@ export const useAuth = () => {
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
loadMockedObjectMetadataItems,
|
||||
navigate,
|
||||
isCaptchaScriptLoaded,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
|
||||
export const useIsLogged = (): boolean => {
|
||||
const [tokenPair] = useRecoilState(tokenPairState);
|
||||
const [tokenPair] = useRecoilStateV2(tokenPairState);
|
||||
return !!tokenPair;
|
||||
};
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import {
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import { OnboardingModalCircularIcon } from '@/onboarding/components/OnboardingModalCircularIcon';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import {
|
||||
IconGmail,
|
||||
IconMail,
|
||||
@@ -89,7 +89,7 @@ export const EmailVerificationSent = ({
|
||||
email: string | null;
|
||||
isError?: boolean;
|
||||
}) => {
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
|
||||
const { handleResendEmailVerificationToken, loading: isLoading } =
|
||||
useHandleResendEmailVerificationToken();
|
||||
|
||||
+2
-3
@@ -5,7 +5,6 @@ import styled from '@emotion/styled';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { motion } from 'framer-motion';
|
||||
import { FormProvider } from 'react-hook-form';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ClickToActionLink, UndecoratedLink } from 'twenty-ui/navigation';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
@@ -126,12 +125,12 @@ const StyledActionLinkContainer = styled.div`
|
||||
|
||||
export const SignInUpGlobalScopeForm = () => {
|
||||
const authProviders = useRecoilValueV2(authProvidersState);
|
||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||
const signInUpStep = useRecoilValueV2(signInUpStepState);
|
||||
const { buildWorkspaceUrl } = useBuildWorkspaceUrl();
|
||||
const { signOut } = useAuth();
|
||||
|
||||
const { createWorkspace } = useSignUpInNewWorkspace();
|
||||
const availableWorkspaces = useRecoilValue(availableWorkspacesState);
|
||||
const availableWorkspaces = useRecoilValueV2(availableWorkspacesState);
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
|
||||
+3
-3
@@ -12,9 +12,9 @@ import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthPro
|
||||
import styled from '@emotion/styled';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { FormProvider } from 'react-hook-form';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { HorizontalSeparator } from 'twenty-ui/display';
|
||||
import { ClickToActionLink } from 'twenty-ui/navigation';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||
@@ -23,8 +23,8 @@ const StyledContentContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const SignInUpWorkspaceScopeForm = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthBypassProviders = useRecoilValue(
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
const workspaceAuthBypassProviders = useRecoilValueV2(
|
||||
workspaceAuthBypassProvidersState,
|
||||
);
|
||||
const { shouldOfferBypass, shouldUseBypass } = useWorkspaceBypass();
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@ import {
|
||||
import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser';
|
||||
import { useEffect } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SignInUpGlobalScopeFormEffect = () => {
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { setAuthTokens } = useAuth();
|
||||
const { loadCurrentUser } = useLoadCurrentUser();
|
||||
|
||||
+2
-2
@@ -3,13 +3,13 @@
|
||||
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
|
||||
import { guessSSOIdentityProviderIconByUrl } from '@/settings/security/utils/guessSSOIdentityProviderIconByUrl';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import React from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { HorizontalSeparator } from 'twenty-ui/display';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||
@@ -17,7 +17,7 @@ const StyledContentContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const SignInUpSSOIdentityProviderSelection = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
|
||||
const { redirectToSSOLoginPage } = useSSO();
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import QRCode from 'react-qr-code';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { Loader } from 'twenty-ui/feedback';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
@@ -71,7 +71,7 @@ export const SignInUpTwoFactorAuthenticationProvision = () => {
|
||||
const theme = useTheme();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const qrCode = useRecoilValueV2(qrCodeState);
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
|
||||
const handleClick = () => {
|
||||
setSignInUpStep(SignInUpStep.TwoFactorAuthenticationVerification);
|
||||
|
||||
+4
-3
@@ -18,11 +18,12 @@ import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { OTPInput, type SlotProps } from 'input-otp';
|
||||
import { useState } from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
import { ClickToActionLink } from 'twenty-ui/navigation';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
|
||||
const StyledMainContentContainer = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||
@@ -186,8 +187,8 @@ export const SignInUpTOTPVerification = () => {
|
||||
const navigate = useNavigateApp();
|
||||
const { readCaptchaToken } = useReadCaptchaToken();
|
||||
const { isCaptchaReady } = useCaptcha();
|
||||
const loginToken = useRecoilValue(loginTokenState);
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const loginToken = useRecoilValueV2(loginTokenState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
const { t } = useLingui();
|
||||
|
||||
const { form } = useTwoFactorAuthenticationForm();
|
||||
|
||||
+8
-4
@@ -19,10 +19,12 @@ import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Loader } from 'twenty-ui/feedback';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledForm = styled.form`
|
||||
align-items: center;
|
||||
@@ -39,13 +41,15 @@ export const SignInUpWithCredentials = ({
|
||||
const { t } = useLingui();
|
||||
const form = useFormContext<Form>();
|
||||
|
||||
const [signInUpStep, setSignInUpStep] = useRecoilState(signInUpStepState);
|
||||
const [signInUpStep, setSignInUpStep] = useRecoilStateV2(signInUpStepState);
|
||||
const [showErrors, setShowErrors] = useState(false);
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const isRequestingCaptchaToken = useRecoilValue(
|
||||
const isRequestingCaptchaToken = useRecoilValueV2(
|
||||
isRequestingCaptchaTokenState,
|
||||
);
|
||||
const lastAuthenticatedMethod = useRecoilValue(lastAuthenticatedMethodState);
|
||||
const lastAuthenticatedMethod = useRecoilValueV2(
|
||||
lastAuthenticatedMethodState,
|
||||
);
|
||||
const hasMultipleAuthMethods = useHasMultipleAuthMethods();
|
||||
|
||||
const {
|
||||
|
||||
+5
-5
@@ -10,9 +10,10 @@ import { type SocialSSOSignInUpActionType } from '@/auth/types/socialSSOSignInUp
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { memo } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { HorizontalSeparator, IconGoogle } from 'twenty-ui/display';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { LastUsedPill } from './LastUsedPill';
|
||||
import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
|
||||
|
||||
@@ -29,10 +30,9 @@ export const SignInUpWithGoogle = ({
|
||||
isGlobalScope?: boolean;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] = useRecoilState(
|
||||
lastAuthenticatedMethodState,
|
||||
);
|
||||
const signInUpStep = useRecoilValueV2(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] =
|
||||
useRecoilStateV2(lastAuthenticatedMethodState);
|
||||
const { signInWithGoogle } = useSignInWithGoogle();
|
||||
const hasMultipleAuthMethods = useHasMultipleAuthMethods();
|
||||
|
||||
|
||||
+5
-5
@@ -9,9 +9,10 @@ import { AuthenticatedMethod } from '@/auth/types/AuthenticatedMethod.enum';
|
||||
import { type SocialSSOSignInUpActionType } from '@/auth/types/socialSSOSignInUp.type';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { HorizontalSeparator, IconMicrosoft } from 'twenty-ui/display';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { LastUsedPill } from './LastUsedPill';
|
||||
import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
|
||||
|
||||
@@ -25,10 +26,9 @@ export const SignInUpWithMicrosoft = ({
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] = useRecoilState(
|
||||
lastAuthenticatedMethodState,
|
||||
);
|
||||
const signInUpStep = useRecoilValueV2(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] =
|
||||
useRecoilStateV2(lastAuthenticatedMethodState);
|
||||
const { signInWithMicrosoft } = useSignInWithMicrosoft();
|
||||
const hasMultipleAuthMethods = useHasMultipleAuthMethods();
|
||||
|
||||
|
||||
+8
-7
@@ -7,9 +7,11 @@ import {
|
||||
} from '@/auth/states/signInUpStepState';
|
||||
import { AuthenticatedMethod } from '@/auth/types/AuthenticatedMethod.enum';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { HorizontalSeparator, IconLock } from 'twenty-ui/display';
|
||||
import { MainButton } from 'twenty-ui/input';
|
||||
@@ -19,12 +21,11 @@ import { StyledSSOButtonContainer } from './SignInUpSSOButtonStyles';
|
||||
export const SignInUpWithSSO = () => {
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] = useRecoilState(
|
||||
lastAuthenticatedMethodState,
|
||||
);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
const signInUpStep = useRecoilValueV2(signInUpStepState);
|
||||
const [lastAuthenticatedMethod, setLastAuthenticatedMethod] =
|
||||
useRecoilStateV2(lastAuthenticatedMethodState);
|
||||
const hasMultipleAuthMethods = useHasMultipleAuthMethods();
|
||||
|
||||
const { redirectToSSOLoginPage } = useSSO();
|
||||
|
||||
+6
-4
@@ -7,8 +7,10 @@ import {
|
||||
import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCaptchaTokenState';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
@@ -21,9 +23,9 @@ enum LoadingStatus {
|
||||
}
|
||||
|
||||
export const SignInUpWorkspaceScopeFormEffect = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
|
||||
const isRequestingCaptchaToken = useRecoilValue(
|
||||
const isRequestingCaptchaToken = useRecoilValueV2(
|
||||
isRequestingCaptchaTokenState,
|
||||
);
|
||||
|
||||
@@ -38,7 +40,7 @@ export const SignInUpWorkspaceScopeFormEffect = () => {
|
||||
const { signInUpStep, continueWithEmail, continueWithCredentials } =
|
||||
useSignInUp(form);
|
||||
|
||||
const setSignInUpStep = useSetRecoilState(signInUpStepState);
|
||||
const setSignInUpStep = useSetRecoilStateV2(signInUpStepState);
|
||||
|
||||
useEffect(() => {
|
||||
if (!workspaceAuthProviders) {
|
||||
|
||||
+17
-9
@@ -1,11 +1,14 @@
|
||||
import { i18n } from '@lingui/core';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode, createElement } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
|
||||
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import {
|
||||
type PublicWorkspaceDataOutput,
|
||||
@@ -20,16 +23,21 @@ jest.mock('~/generated-metadata/graphql');
|
||||
dynamicActivate(SOURCE_LOCALE);
|
||||
|
||||
const renderHooks = () => {
|
||||
jotaiStore.set(workspacePublicDataState.atom, {
|
||||
id: 'workspace-id',
|
||||
} as PublicWorkspaceDataOutput);
|
||||
|
||||
const { result } = renderHook(() => useHandleResetPassword(), {
|
||||
wrapper: ({ children }) =>
|
||||
RecoilRoot({
|
||||
initializeState: ({ set }) => {
|
||||
set(workspacePublicDataState, {
|
||||
id: 'workspace-id',
|
||||
} as PublicWorkspaceDataOutput);
|
||||
},
|
||||
children: I18nProvider({ i18n, children }),
|
||||
}),
|
||||
wrapper: ({ children }: { children: ReactNode }) =>
|
||||
createElement(
|
||||
JotaiProvider,
|
||||
{ store: jotaiStore },
|
||||
createElement(
|
||||
RecoilRoot,
|
||||
null as any,
|
||||
createElement(I18nProvider, { i18n }, children),
|
||||
),
|
||||
),
|
||||
});
|
||||
return { result };
|
||||
};
|
||||
|
||||
@@ -5,14 +5,14 @@ import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useEmailPasswordResetLinkMutation } from '~/generated-metadata/graphql';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useHandleResetPassword = () => {
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
const [emailPasswordResetLink] = useEmailPasswordResetLinkMutation();
|
||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const workspacePublicData = useRecoilValueV2(workspacePublicDataState);
|
||||
const currentUser = useRecoilValueV2(currentUserState);
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useHasMultipleAuthMethods = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
|
||||
if (!workspaceAuthProviders) {
|
||||
return false;
|
||||
|
||||
@@ -18,23 +18,23 @@ import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCu
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { buildAppPathWithQueryParams } from '~/utils/buildAppPathWithQueryParams';
|
||||
import { isMatchingLocation } from '~/utils/isMatchingLocation';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
|
||||
export const useSignInUp = (form: UseFormReturn<Form>) => {
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { t } = useLingui();
|
||||
|
||||
const [signInUpStep, setSignInUpStep] = useRecoilState(signInUpStepState);
|
||||
const [signInUpStep, setSignInUpStep] = useRecoilStateV2(signInUpStepState);
|
||||
const [signInUpMode, setSignInUpMode] = useRecoilStateV2(signInUpModeState);
|
||||
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
|
||||
const { isCaptchaReady } = useCaptcha();
|
||||
const setLastAuthenticatedMethod = useSetRecoilState(
|
||||
const setLastAuthenticatedMethod = useSetRecoilStateV2(
|
||||
lastAuthenticatedMethodState,
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
|
||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const makeValidationSchema = (signInUpStep: SignInUpStep) =>
|
||||
z
|
||||
@@ -34,7 +35,7 @@ const makeValidationSchema = (signInUpStep: SignInUpStep) =>
|
||||
|
||||
export type Form = z.infer<ReturnType<typeof makeValidationSchema>>;
|
||||
export const useSignInUpForm = () => {
|
||||
const signInUpStep = useRecoilValue(signInUpStepState);
|
||||
const signInUpStep = useRecoilValueV2(signInUpStepState);
|
||||
|
||||
const validationSchema = makeValidationSchema(signInUpStep); // Create schema based on the current step
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@ import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type SocialSSOSignInUpActionType } from '@/auth/types/socialSSOSignInUp.type';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useSignInWithMicrosoft = () => {
|
||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||
const [searchParams] = useSearchParams();
|
||||
const workspacePersonalInviteToken =
|
||||
searchParams.get('inviteToken') ?? undefined;
|
||||
const billingCheckoutSession = useRecoilValue(billingCheckoutSessionState);
|
||||
const billingCheckoutSession = useRecoilValueV2(billingCheckoutSessionState);
|
||||
|
||||
const { signInWithMicrosoft } = useAuth();
|
||||
return {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
|
||||
import { workspaceAuthBypassProvidersState } from '@/workspace/states/workspaceAuthBypassProvidersState';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { workspaceBypassModeState } from '@/workspace/states/workspaceBypassModeState';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useWorkspaceBypass = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthBypassProviders = useRecoilValue(
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
const workspaceAuthBypassProviders = useRecoilValueV2(
|
||||
workspaceAuthBypassProvidersState,
|
||||
);
|
||||
const [workspaceBypassMode, setWorkspaceBypassMode] = useRecoilState(
|
||||
const [workspaceBypassMode, setWorkspaceBypassMode] = useRecoilStateV2(
|
||||
workspaceBypassModeState,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
|
||||
@@ -16,7 +16,7 @@ export const useWorkspaceFromInviteHash = () => {
|
||||
const { enqueueErrorSnackBar, enqueueInfoSnackBar } = useSnackBar();
|
||||
const navigate = useNavigateApp();
|
||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
const [initiallyLoggedIn] = useState(isDefined(currentWorkspace));
|
||||
|
||||
const { data: workspaceFromInviteHash, loading } =
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type AvailableWorkspaces } from '~/generated-metadata/graphql';
|
||||
|
||||
export const availableWorkspacesState = createState<AvailableWorkspaces>({
|
||||
export const availableWorkspacesState = createStateV2<AvailableWorkspaces>({
|
||||
key: 'availableWorkspacesState',
|
||||
defaultValue: {
|
||||
availableWorkspacesForSignIn: [],
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { type BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
||||
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const billingCheckoutSessionState = createState<BillingCheckoutSession>({
|
||||
key: 'billingCheckoutSessionState',
|
||||
defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
||||
});
|
||||
export const billingCheckoutSessionState =
|
||||
createStateV2<BillingCheckoutSession>({
|
||||
key: 'billingCheckoutSessionState',
|
||||
defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type User } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentUser = Pick<
|
||||
@@ -15,7 +15,7 @@ export type CurrentUser = Pick<
|
||||
| 'hasPassword'
|
||||
>;
|
||||
|
||||
export const currentUserState = createState<CurrentUser | null>({
|
||||
export const currentUserState = createStateV2<CurrentUser | null>({
|
||||
key: 'currentUserState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type ObjectPermissions } from 'twenty-shared/types';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type UserWorkspace } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentUserWorkspace = Pick<
|
||||
@@ -10,7 +10,7 @@ export type CurrentUserWorkspace = Pick<
|
||||
};
|
||||
|
||||
export const currentUserWorkspaceState =
|
||||
createState<CurrentUserWorkspace | null>({
|
||||
createStateV2<CurrentUserWorkspace | null>({
|
||||
key: 'currentUserWorkspaceState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type DeletedWorkspaceMember } from '~/generated-metadata/graphql';
|
||||
|
||||
export const currentWorkspaceDeletedMembersState = createState<
|
||||
export const currentWorkspaceDeletedMembersState = createStateV2<
|
||||
DeletedWorkspaceMember[]
|
||||
>({
|
||||
key: 'currentWorkspaceDeletedMembersState',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export type CurrentWorkspaceMember = Omit<
|
||||
WorkspaceMember,
|
||||
@@ -7,7 +7,7 @@ export type CurrentWorkspaceMember = Omit<
|
||||
>;
|
||||
|
||||
export const currentWorkspaceMemberState =
|
||||
createState<CurrentWorkspaceMember | null>({
|
||||
createStateV2<CurrentWorkspaceMember | null>({
|
||||
key: 'currentWorkspaceMemberState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type PartialWorkspaceMember } from '@/settings/roles/types/RoleWithPartialMembers';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const currentWorkspaceMembersState = createState<
|
||||
export const currentWorkspaceMembersState = createStateV2<
|
||||
PartialWorkspaceMember[]
|
||||
>({
|
||||
key: 'currentWorkspaceMembersState',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import {
|
||||
type Application,
|
||||
type Role,
|
||||
@@ -43,7 +43,7 @@ export type CurrentWorkspace = Pick<
|
||||
workspaceCustomApplication: Pick<Application, 'id'> | null;
|
||||
};
|
||||
|
||||
export const currentWorkspaceState = createState<CurrentWorkspace | null>({
|
||||
export const currentWorkspaceState = createStateV2<CurrentWorkspace | null>({
|
||||
key: 'currentWorkspaceState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isCurrentUserLoadedState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isCurrentUserLoadedState = createStateV2<boolean>({
|
||||
key: 'isCurrentUserLoadedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { selector } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { tokenPairState } from './tokenPairState';
|
||||
|
||||
export const isImpersonatingState = selector<boolean>({
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { createSelectorV2 } from '@/ui/utilities/state/jotai/utils/createSelectorV2';
|
||||
|
||||
export const isImpersonatingState = createSelectorV2<boolean>({
|
||||
key: 'isImpersonatingState',
|
||||
get: ({ get }) => {
|
||||
const tokenPair = get(tokenPairState);
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { type AuthenticatedMethod } from '@/auth/types/AuthenticatedMethod.enum';
|
||||
import { localStorageEffect } from '~/utils/recoil/localStorageEffect';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
const LAST_AUTHENTICATED_METHOD_STORAGE_KEY = 'lastAuthenticatedMethodState';
|
||||
|
||||
export const lastAuthenticatedMethodState = atom<AuthenticatedMethod | null>({
|
||||
key: LAST_AUTHENTICATED_METHOD_STORAGE_KEY,
|
||||
default: null,
|
||||
effects: [localStorageEffect()],
|
||||
});
|
||||
export const lastAuthenticatedMethodState =
|
||||
createStateV2<AuthenticatedMethod | null>({
|
||||
key: 'lastAuthenticatedMethodState',
|
||||
defaultValue: null,
|
||||
useLocalStorage: true,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type AuthToken } from '~/generated-metadata/graphql';
|
||||
|
||||
export const loginTokenState = createState<AuthToken['token'] | null>({
|
||||
export const loginTokenState = createStateV2<AuthToken['token'] | null>({
|
||||
key: 'loginTokenState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
// Temporary bridge: reads from Jotai store for migrated state.
|
||||
// Will be fully migrated to Jotai in PR 6.
|
||||
export const objectPermissionsFamilySelector = selectorFamily<
|
||||
{
|
||||
canRead: boolean;
|
||||
@@ -13,7 +16,9 @@ export const objectPermissionsFamilySelector = selectorFamily<
|
||||
get:
|
||||
({ objectNameSingular }) =>
|
||||
({ get }) => {
|
||||
const currentUserWorkspace = get(currentUserWorkspaceState);
|
||||
const currentUserWorkspace = jotaiStore.get(
|
||||
currentUserWorkspaceState.atom,
|
||||
);
|
||||
const objectMetadataItems = get(objectMetadataItemsState);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export enum SignInUpStep {
|
||||
Init = 'init',
|
||||
Email = 'email',
|
||||
@@ -10,7 +10,7 @@ export enum SignInUpStep {
|
||||
TwoFactorAuthenticationProvision = 'TwoFactorAuthenticationProvision',
|
||||
}
|
||||
|
||||
export const signInUpStepState = createState<SignInUpStep>({
|
||||
export const signInUpStepState = createStateV2<SignInUpStep>({
|
||||
key: 'signInUpStepState',
|
||||
defaultValue: SignInUpStep.Init,
|
||||
});
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
import { type AuthTokenPair } from '~/generated-metadata/graphql';
|
||||
import { cookieStorageEffect } from '~/utils/recoil/cookieStorageEffect';
|
||||
|
||||
export const tokenPairState = createState<AuthTokenPair | null>({
|
||||
export const tokenPairState = createStateV2<AuthTokenPair | null>({
|
||||
key: 'tokenPairState',
|
||||
defaultValue: null,
|
||||
effects: [
|
||||
cookieStorageEffect(
|
||||
'tokenPair',
|
||||
{},
|
||||
{
|
||||
validateInitFn: (payload: AuthTokenPair) =>
|
||||
Boolean(payload['accessOrWorkspaceAgnosticToken']),
|
||||
},
|
||||
),
|
||||
],
|
||||
useCookieStorage: {
|
||||
cookieKey: 'tokenPair',
|
||||
validateInitFn: (payload: AuthTokenPair) =>
|
||||
Boolean(payload['accessOrWorkspaceAgnosticToken']),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type PublicWorkspaceDataOutput } from '~/generated-metadata/graphql';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const workspacePublicDataState =
|
||||
createState<PublicWorkspaceDataOutput | null>({
|
||||
createStateV2<PublicWorkspaceDataOutput | null>({
|
||||
key: 'workspacePublicDataState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { SettingsBillingCreditsSection } from '@/billing/components/SettingsBillingCreditsSection';
|
||||
@@ -16,13 +15,14 @@ import {
|
||||
useBillingPortalSessionQuery,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { useGetWorkflowNodeExecutionUsage } from '@/billing/hooks/useGetWorkflowNodeExecutionUsage';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const SettingsBillingContent = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { redirect } = useRedirect();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
const subscriptions = currentWorkspace?.billingSubscriptions;
|
||||
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
H2Title,
|
||||
@@ -144,7 +144,7 @@ export const SettingsBillingSubscriptionInfo = ({
|
||||
|
||||
const [cancelSwitchMeteredPrice] = useCancelSwitchMeteredPriceMutation();
|
||||
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspace = useSetRecoilStateV2(currentWorkspaceState);
|
||||
|
||||
const isTrialPeriod = subscriptionStatus === SubscriptionStatus.Trialing;
|
||||
|
||||
|
||||
+2
-2
@@ -7,6 +7,7 @@ import {
|
||||
type MeteredBillingPrice,
|
||||
} from '@/billing/types/billing-price-tiers.type';
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
@@ -14,7 +15,6 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { findOrThrow, isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
@@ -48,7 +48,7 @@ export const MeteredPriceSelector = ({
|
||||
const { currentMeteredBillingPrice } = useCurrentMetered();
|
||||
const { formatNumber } = useNumberFormat();
|
||||
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilStateV2(
|
||||
currentWorkspaceState,
|
||||
);
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ import { beautifyExactDate } from '~/utils/date-utils';
|
||||
import { useCurrentPlan } from '@/billing/hooks/useCurrentPlan';
|
||||
import { useCurrentMetered } from '@/billing/hooks/useCurrentMetered';
|
||||
import { useCurrentBillingFlags } from '@/billing/hooks/useCurrentBillingFlags';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useBillingWording = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
assertIsDefinedOrThrow(currentWorkspace);
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
BillingPlanKey,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
|
||||
|
||||
export const useCurrentBillingFlags = () => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
assertIsDefinedOrThrow(currentWorkspace);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useCurrentPlan } from '@/billing/hooks/useCurrentPlan';
|
||||
import type { MeteredBillingPrice } from '@/billing/types/billing-price-tiers.type';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { assertIsDefinedOrThrow, findOrThrow } from 'twenty-shared/utils';
|
||||
import {
|
||||
BillingProductKey,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
export const useCurrentMetered = () => {
|
||||
const { currentPlan } = useCurrentPlan();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
assertIsDefinedOrThrow(currentWorkspace);
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { BillingPlanKey } from '~/generated-metadata/graphql';
|
||||
import { assertIsDefinedOrThrow, findOrThrow } from 'twenty-shared/utils';
|
||||
import { usePlans } from './usePlans';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useCurrentPlan = () => {
|
||||
const { listPlans } = usePlans();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
assertIsDefinedOrThrow(currentWorkspace);
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useEndSubscriptionTrialPeriodMutation } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useEndSubscriptionTrialPeriod = () => {
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [endSubscriptionTrialPeriod] = useEndSubscriptionTrialPeriodMutation();
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilStateV2(
|
||||
currentWorkspaceState,
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useHasNextBillingPhase = () => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
const hasNextBillingPhase =
|
||||
currentWorkspace?.currentBillingSubscription?.phases.length === 2;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useNextBillingPhase = () => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
|
||||
const nextBillingPhase =
|
||||
currentWorkspace?.currentBillingSubscription?.phases[1];
|
||||
|
||||
+5
-5
@@ -1,19 +1,19 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { getCaptchaUrlByProvider } from '@/captcha/utils/getCaptchaUrlByProvider';
|
||||
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
|
||||
import { useCaptcha } from '@/client-config/hooks/useCaptcha';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
|
||||
import { CaptchaDriverType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const CaptchaProviderScriptLoaderEffect = () => {
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const setIsCaptchaScriptLoaded = useSetRecoilState(
|
||||
const setIsCaptchaScriptLoaded = useSetRecoilStateV2(
|
||||
isCaptchaScriptLoadedState,
|
||||
);
|
||||
const { isCaptchaScriptLoaded, isCaptchaConfigured } = useCaptcha();
|
||||
|
||||
+19
-18
@@ -1,13 +1,26 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { captchaTokenState } from '@/captcha/states/captchaTokenState';
|
||||
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useReadCaptchaToken', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(captchaTokenState.atom, undefined);
|
||||
});
|
||||
|
||||
it('should return undefined when no token exists', async () => {
|
||||
const { result } = renderHook(() => useReadCaptchaToken(), {
|
||||
wrapper: RecoilRoot,
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -17,23 +30,11 @@ describe('useReadCaptchaToken', () => {
|
||||
});
|
||||
|
||||
it('should return the token when it exists', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const hook = useReadCaptchaToken();
|
||||
return hook;
|
||||
},
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(captchaTokenState, 'test-token');
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
),
|
||||
},
|
||||
);
|
||||
jotaiStore.set(captchaTokenState.atom, 'test-token');
|
||||
|
||||
const { result } = renderHook(() => useReadCaptchaToken(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const token = await result.current.readCaptchaToken();
|
||||
|
||||
+33
-30
@@ -1,14 +1,27 @@
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { type ReactNode } from 'react';
|
||||
import { type MutableSnapshot, RecoilRoot } from 'recoil';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCaptchaTokenState';
|
||||
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { CaptchaDriverType } from '~/generated-metadata/graphql';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { type Captcha, CaptchaDriverType } from '~/generated-metadata/graphql';
|
||||
|
||||
jest.mock('@/captcha/utils/isCaptchaRequiredForPath');
|
||||
|
||||
const createWrapper =
|
||||
(initializeRecoilState?: (mutableSnapshot: MutableSnapshot) => void) =>
|
||||
({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot initializeState={initializeRecoilState}>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useRequestFreshCaptchaToken', () => {
|
||||
const mockGrecaptchaExecute = jest.fn();
|
||||
const mockTurnstileRender = jest.fn();
|
||||
@@ -47,7 +60,7 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
(isCaptchaRequiredForPath as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const { result } = renderHook(() => useRequestFreshCaptchaToken(), {
|
||||
wrapper: RecoilRoot,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -61,7 +74,7 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
|
||||
it('should not request a token if captcha provider is undefined', async () => {
|
||||
const { result } = renderHook(() => useRequestFreshCaptchaToken(), {
|
||||
wrapper: RecoilRoot,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -74,20 +87,15 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
});
|
||||
|
||||
it('should request a token from Google reCAPTCHA when provider is GOOGLE_RECAPTCHA', async () => {
|
||||
jotaiStore.set(isRequestingCaptchaTokenState.atom, false);
|
||||
|
||||
const { result } = renderHook(() => useRequestFreshCaptchaToken(), {
|
||||
wrapper: ({ children }) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(captchaState, {
|
||||
provider: CaptchaDriverType.GOOGLE_RECAPTCHA,
|
||||
siteKey: 'google-site-key',
|
||||
});
|
||||
set(isRequestingCaptchaTokenState, false);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
),
|
||||
wrapper: createWrapper(({ set }) => {
|
||||
set(captchaState, {
|
||||
provider: CaptchaDriverType.GOOGLE_RECAPTCHA,
|
||||
siteKey: 'google-site-key',
|
||||
} as Captcha);
|
||||
}),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -104,20 +112,15 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
});
|
||||
|
||||
it('should request a token from Turnstile when provider is TURNSTILE', async () => {
|
||||
jotaiStore.set(isRequestingCaptchaTokenState.atom, false);
|
||||
|
||||
const { result } = renderHook(() => useRequestFreshCaptchaToken(), {
|
||||
wrapper: ({ children }) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(captchaState, {
|
||||
provider: CaptchaDriverType.TURNSTILE,
|
||||
siteKey: 'turnstile-site-key',
|
||||
});
|
||||
set(isRequestingCaptchaTokenState, false);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
),
|
||||
wrapper: createWrapper(({ set }) => {
|
||||
set(captchaState, {
|
||||
provider: CaptchaDriverType.TURNSTILE,
|
||||
siteKey: 'turnstile-site-key',
|
||||
} as Captcha);
|
||||
}),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { captchaTokenState } from '@/captcha/states/captchaTokenState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useReadCaptchaToken = () => {
|
||||
const readCaptchaToken = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const existingCaptchaToken = snapshot
|
||||
.getLoadable(captchaTokenState)
|
||||
.getValue();
|
||||
const readCaptchaToken = useCallback(() => {
|
||||
const existingCaptchaToken = jotaiStore.get(captchaTokenState.atom);
|
||||
|
||||
if (isDefined(existingCaptchaToken)) {
|
||||
return existingCaptchaToken;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
if (isDefined(existingCaptchaToken)) {
|
||||
return existingCaptchaToken;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { readCaptchaToken };
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { captchaTokenState } from '@/captcha/states/captchaTokenState';
|
||||
import { isRequestingCaptchaTokenState } from '@/captcha/states/isRequestingCaptchaTokenState';
|
||||
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils';
|
||||
import { CaptchaDriverType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useRequestFreshCaptchaToken = () => {
|
||||
const setCaptchaToken = useSetRecoilState(captchaTokenState);
|
||||
const setIsRequestingCaptchaToken = useSetRecoilState(
|
||||
const setCaptchaToken = useSetRecoilStateV2(captchaTokenState);
|
||||
const setIsRequestingCaptchaToken = useSetRecoilStateV2(
|
||||
isRequestingCaptchaTokenState,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const captchaTokenState = createState<string | undefined>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const captchaTokenState = createStateV2<string | undefined>({
|
||||
key: 'captchaTokenState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isCaptchaScriptLoadedState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isCaptchaScriptLoadedState = createStateV2<boolean>({
|
||||
key: 'isCaptchaScriptLoadedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isRequestingCaptchaTokenState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isRequestingCaptchaTokenState = createStateV2<boolean>({
|
||||
key: 'isRequestingCaptchaTokenState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
+7
-8
@@ -1,19 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { isLoadingTokensFromExtensionState } from '@/chrome-extension-sidecar/states/isLoadingTokensFromExtensionState';
|
||||
import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState';
|
||||
import { isInFrame } from '~/utils/isInIframe';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isInFrame } from '~/utils/isInIframe';
|
||||
|
||||
export const ChromeExtensionSidecarEffect = () => {
|
||||
const navigate = useNavigate();
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
const setTokenPair = useSetRecoilStateV2(tokenPairState);
|
||||
const chromeExtensionId = useRecoilValueV2(chromeExtensionIdState);
|
||||
const setIsLoadingTokensFromExtension = useSetRecoilState(
|
||||
const setIsLoadingTokensFromExtension = useSetRecoilStateV2(
|
||||
isLoadingTokensFromExtensionState,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const isLoadingTokensFromExtensionState = createState<boolean | null>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const isLoadingTokensFromExtensionState = createStateV2<boolean | null>({
|
||||
key: 'isLoadingTokensFromExtensionState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -2,15 +2,16 @@ import { captchaTokenState } from '@/captcha/states/captchaTokenState';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCaptcha = () => {
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const captchaToken = useRecoilValue(captchaTokenState);
|
||||
const captchaToken = useRecoilValueV2(captchaTokenState);
|
||||
const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState);
|
||||
const isCaptchaScriptLoaded = useRecoilValue(isCaptchaScriptLoadedState);
|
||||
const isCaptchaScriptLoaded = useRecoilValueV2(isCaptchaScriptLoadedState);
|
||||
|
||||
const isClientConfigLoaded = clientConfigApiStatus.isLoadedOnce;
|
||||
const isSiteKeyDefined = isDefined(captcha?.siteKey);
|
||||
|
||||
@@ -45,7 +45,7 @@ type UseClientConfigResult = {
|
||||
|
||||
export const useClientConfig = (): UseClientConfigResult => {
|
||||
const setIsAnalyticsEnabled = useSetRecoilStateV2(isAnalyticsEnabledState);
|
||||
const setDomainConfiguration = useSetRecoilState(domainConfigurationState);
|
||||
const setDomainConfiguration = useSetRecoilStateV2(domainConfigurationState);
|
||||
const setAuthProviders = useSetRecoilStateV2(authProvidersState);
|
||||
const setAiModels = useSetRecoilStateV2(aiModelsState);
|
||||
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@ import { expect, userEvent, within } from 'storybook/test';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
@@ -72,11 +73,11 @@ const meta: Meta<typeof CommandMenu> = {
|
||||
component: CommandMenuRouter,
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentUserWorkspace = useSetRecoilState(
|
||||
const setCurrentWorkspace = useSetRecoilStateV2(currentWorkspaceState);
|
||||
const setCurrentUserWorkspace = useSetRecoilStateV2(
|
||||
currentUserWorkspaceState,
|
||||
);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
const setCurrentWorkspaceMember = useSetRecoilStateV2(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setIsCommandMenuOpened = useSetRecoilState(
|
||||
@@ -138,7 +139,7 @@ export const LimitedPermissions: Story = {
|
||||
},
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const setCurrentUserWorkspace = useSetRecoilState(
|
||||
const setCurrentUserWorkspace = useSetRecoilStateV2(
|
||||
currentUserWorkspaceState,
|
||||
);
|
||||
setCurrentUserWorkspace(
|
||||
|
||||
+16
-18
@@ -1,27 +1,25 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
||||
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
export const useBuildSearchParamsFromUrlSyncedStates = () => {
|
||||
const buildSearchParamsFromUrlSyncedStates = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const billingCheckoutSession = snapshot
|
||||
.getLoadable(billingCheckoutSessionState)
|
||||
.getValue();
|
||||
const buildSearchParamsFromUrlSyncedStates = useCallback(async () => {
|
||||
const billingCheckoutSession = jotaiStore.get(
|
||||
billingCheckoutSessionState.atom,
|
||||
);
|
||||
|
||||
const output = {
|
||||
...(billingCheckoutSession !== BILLING_CHECKOUT_SESSION_DEFAULT_VALUE
|
||||
? {
|
||||
billingCheckoutSession: JSON.stringify(billingCheckoutSession),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const output = {
|
||||
...(billingCheckoutSession !== BILLING_CHECKOUT_SESSION_DEFAULT_VALUE
|
||||
? {
|
||||
billingCheckoutSession: JSON.stringify(billingCheckoutSession),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
return output;
|
||||
},
|
||||
[],
|
||||
);
|
||||
return output;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
buildSearchParamsFromUrlSyncedStates,
|
||||
|
||||
+8
-6
@@ -4,9 +4,11 @@ import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWork
|
||||
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
|
||||
import { useOrigin } from '@/domain-manager/hooks/useOrigin';
|
||||
import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectToDefaultDomain';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { workspaceAuthBypassProvidersState } from '@/workspace/states/workspaceAuthBypassProvidersState';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useGetPublicWorkspaceDataByDomainQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -14,15 +16,15 @@ export const useGetPublicWorkspaceDataByDomain = () => {
|
||||
const { isDefaultDomain } = useIsCurrentLocationOnDefaultDomain();
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const { origin } = useOrigin();
|
||||
const setWorkspaceAuthProviders = useSetRecoilState(
|
||||
const setWorkspaceAuthProviders = useSetRecoilStateV2(
|
||||
workspaceAuthProvidersState,
|
||||
);
|
||||
const setWorkspaceAuthBypassProviders = useSetRecoilState(
|
||||
const setWorkspaceAuthBypassProviders = useSetRecoilStateV2(
|
||||
workspaceAuthBypassProvidersState,
|
||||
);
|
||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||
const workspacePublicData = useRecoilValueV2(workspacePublicDataState);
|
||||
const { redirectToDefaultDomain } = useRedirectToDefaultDomain();
|
||||
const setWorkspacePublicDataState = useSetRecoilState(
|
||||
const setWorkspacePublicDataState = useSetRecoilStateV2(
|
||||
workspacePublicDataState,
|
||||
);
|
||||
const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState);
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { useReadDefaultDomainFromConfiguration } from '@/domain-manager/hooks/useReadDefaultDomainFromConfiguration';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -8,7 +9,7 @@ export const useIsCurrentLocationOnAWorkspace = () => {
|
||||
const { defaultDomain } = useReadDefaultDomainFromConfiguration();
|
||||
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||
const domainConfiguration = useRecoilValueV2(domainConfigurationState);
|
||||
|
||||
if (
|
||||
isMultiWorkspaceEnabled &&
|
||||
|
||||
+4
-3
@@ -1,10 +1,11 @@
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { lastAuthenticatedWorkspaceDomainState } from '@/domain-manager/states/lastAuthenticatedWorkspaceDomainState';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
|
||||
export const useLastAuthenticatedWorkspaceDomain = () => {
|
||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||
const setLastAuthenticatedWorkspaceDomain = useSetRecoilState(
|
||||
const domainConfiguration = useRecoilValueV2(domainConfigurationState);
|
||||
const setLastAuthenticatedWorkspaceDomain = useSetRecoilStateV2(
|
||||
lastAuthenticatedWorkspaceDomainState,
|
||||
);
|
||||
const setLastAuthenticateWorkspaceDomainWithCookieAttributes = (
|
||||
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const useReadDefaultDomainFromConfiguration = () => {
|
||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||
const domainConfiguration = useRecoilValueV2(domainConfigurationState);
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
|
||||
const defaultDomain = isMultiWorkspaceEnabled
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type ClientConfig } from '@/client-config/types/ClientConfig';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const domainConfigurationState = createState<
|
||||
export const domainConfigurationState = createStateV2<
|
||||
Pick<ClientConfig, 'frontDomain' | 'defaultSubdomain'>
|
||||
>({
|
||||
key: 'domainConfiguration',
|
||||
|
||||
+8
-8
@@ -1,7 +1,6 @@
|
||||
import { cookieStorageEffect } from '~/utils/recoil/cookieStorageEffect';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const lastAuthenticatedWorkspaceDomainState = createState<
|
||||
export const lastAuthenticatedWorkspaceDomainState = createStateV2<
|
||||
| {
|
||||
workspaceUrl: string;
|
||||
workspaceId: string;
|
||||
@@ -13,9 +12,10 @@ export const lastAuthenticatedWorkspaceDomainState = createState<
|
||||
>({
|
||||
key: 'lastAuthenticateWorkspaceDomain',
|
||||
defaultValue: null,
|
||||
effects: [
|
||||
cookieStorageEffect('lastAuthenticateWorkspaceDomain', {
|
||||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), // 1 year
|
||||
}),
|
||||
],
|
||||
useCookieStorage: {
|
||||
cookieKey: 'lastAuthenticateWorkspaceDomain',
|
||||
attributes: {
|
||||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useEffect, useState } 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 { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
@@ -12,9 +12,9 @@ import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
export const SentryInitEffect = () => {
|
||||
const sentryConfig = useRecoilValue(sentryConfigState);
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentUser = useRecoilValueV2(currentUserState);
|
||||
const currentWorkspace = useRecoilValueV2(currentWorkspaceState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
|
||||
const [isSentryInitialized, setIsSentryInitialized] = useState(false);
|
||||
const [isSentryInitializing, setIsSentryInitializing] = useState(false);
|
||||
|
||||
+2
-2
@@ -13,14 +13,14 @@ import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { isNavigationSectionOpenFamilyState } from '@/ui/navigation/navigation-drawer/states/isNavigationSectionOpenFamilyState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconFolderPlus } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
|
||||
export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
const { favoritesByFolder } = useFavoritesByFolder();
|
||||
|
||||
|
||||
+3
-5
@@ -5,6 +5,7 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
@@ -46,13 +47,10 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
|
||||
describe('useCreateFavorite', () => {
|
||||
it('should create favorite successfully', async () => {
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, mockWorkspaceMember);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
|
||||
+3
-5
@@ -4,6 +4,7 @@ import { useSetRecoilState } from 'recoil';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
@@ -23,13 +24,10 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
|
||||
describe('useDeleteFavorite', () => {
|
||||
it('should delete favorite successfully', async () => {
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, mockWorkspaceMember);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { prefetchFavoritesState } from '@/prefetch/states/prefetchFavoritesState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
@@ -20,16 +21,13 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
|
||||
describe('useFavorites', () => {
|
||||
it('should fetch and sort favorites successfully', () => {
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, mockWorkspaceMember);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setPrefetchFavorites = useSetRecoilState(prefetchFavoritesState);
|
||||
setPrefetchFavorites(initialFavorites);
|
||||
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
|
||||
+3
-5
@@ -11,6 +11,7 @@ import { createFolderDroppableId } from '@/favorites/utils/createFolderDroppable
|
||||
import { createFolderHeaderDroppableId } from '@/favorites/utils/createFolderHeaderDroppableId';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { prefetchFavoritesState } from '@/prefetch/states/prefetchFavoritesState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
|
||||
import {
|
||||
@@ -33,17 +34,14 @@ describe('useHandleFavoriteDragAndDrop', () => {
|
||||
};
|
||||
|
||||
const setupHook = () => {
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, mockWorkspaceMember);
|
||||
|
||||
return renderHook(
|
||||
() => {
|
||||
const setPrefetchFavorites = useSetRecoilState(prefetchFavoritesState);
|
||||
setPrefetchFavorites(initialFavorites as Favorite[]);
|
||||
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);
|
||||
|
||||
setCurrentWorkspaceMember(mockWorkspaceMember);
|
||||
setMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { type Favorite } from '@/favorites/types/Favorite';
|
||||
import { prefetchFavoritesState } from '@/prefetch/states/prefetchFavoritesState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
type PrefetchedFavoritesData = {
|
||||
@@ -10,7 +11,7 @@ type PrefetchedFavoritesData = {
|
||||
};
|
||||
|
||||
export const usePrefetchedFavoritesData = (): PrefetchedFavoritesData => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMemberId = currentWorkspaceMember?.id;
|
||||
const prefetchFavorites = useRecoilValue(prefetchFavoritesState);
|
||||
|
||||
|
||||
+4
-1
@@ -1,6 +1,7 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { type FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
||||
import { prefetchFavoriteFoldersState } from '@/prefetch/states/prefetchFavoriteFoldersState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
type PrefetchedFavoritesFoldersData = {
|
||||
@@ -10,7 +11,9 @@ type PrefetchedFavoritesFoldersData = {
|
||||
|
||||
export const usePrefetchedFavoritesFoldersData =
|
||||
(): PrefetchedFavoritesFoldersData => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const currentWorkspaceMemberId = currentWorkspaceMember?.id;
|
||||
|
||||
const prefetchFavoriteFolders = useRecoilValue(
|
||||
|
||||
+2
-2
@@ -1,4 +1,3 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import {
|
||||
type FrontComponentExecutionContext,
|
||||
type FrontComponentHostCommunicationApi,
|
||||
@@ -6,13 +5,14 @@ import {
|
||||
import { type AppPath } from 'twenty-shared/types';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
export const useFrontComponentExecutionContext = (): {
|
||||
executionContext: FrontComponentExecutionContext;
|
||||
frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi;
|
||||
} => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentUser = useRecoilValueV2(currentUserState);
|
||||
const navigateApp = useNavigateApp();
|
||||
|
||||
const navigate: FrontComponentHostCommunicationApi['navigate'] = async (
|
||||
|
||||
+3
-3
@@ -3,13 +3,13 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
|
||||
import { isImpersonatingState } from '@/auth/states/isImpersonatingState';
|
||||
import { InformationBanner } from '@/information-banner/components/InformationBanner';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLogout } from 'twenty-ui/display';
|
||||
|
||||
export const InformationBannerIsImpersonating = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const isImpersonating = useRecoilValue(isImpersonatingState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const isImpersonating = useRecoilValueV2(isImpersonatingState);
|
||||
|
||||
const { signOut } = useAuth();
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { type InformationBannerKeys } from '@/information-banner/types/InformationBannerKeys';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useAccountToReconnect = (key: InformationBannerKeys) => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentUser = useRecoilValueV2(currentUserState);
|
||||
|
||||
const userVars = currentUser?.userVars;
|
||||
|
||||
|
||||
+32
-26
@@ -1,6 +1,7 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { RecoilRoot, type MutableSnapshot } from 'recoil';
|
||||
import { type MutableSnapshot, RecoilRoot } from 'recoil';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||
@@ -8,6 +9,7 @@ import { NumberFormat } from '@/localization/constants/NumberFormat';
|
||||
import { TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { useFormatPreferences } from '@/localization/hooks/useFormatPreferences';
|
||||
import { workspaceMemberFormatPreferencesState } from '@/localization/states/workspaceMemberFormatPreferencesState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { detectCalendarStartDay } from '@/localization/utils/detection/detectCalendarStartDay';
|
||||
import { detectDateFormat } from '@/localization/utils/detection/detectDateFormat';
|
||||
import { detectNumberFormat } from '@/localization/utils/detection/detectNumberFormat';
|
||||
@@ -73,19 +75,30 @@ const mockInitialPreferences = {
|
||||
calendarStartDay: CalendarStartDay.MONDAY,
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||
const initializeState = ({ set }: MutableSnapshot) => {
|
||||
set(currentWorkspaceMemberState, mockCurrentWorkspaceMember);
|
||||
set(workspaceMemberFormatPreferencesState, mockInitialPreferences);
|
||||
};
|
||||
const createWrapper =
|
||||
(initializeRecoilState?: (mutableSnapshot: MutableSnapshot) => void) =>
|
||||
({ children }: { children: ReactNode }) => {
|
||||
const defaultInitState = ({ set }: MutableSnapshot) => {
|
||||
set(workspaceMemberFormatPreferencesState, mockInitialPreferences);
|
||||
initializeRecoilState?.({ set } as MutableSnapshot);
|
||||
};
|
||||
|
||||
return <RecoilRoot initializeState={initializeState}>{children}</RecoilRoot>;
|
||||
};
|
||||
return (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot initializeState={defaultInitState}>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useFormatPreferences', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
jotaiStore.set(
|
||||
currentWorkspaceMemberState.atom,
|
||||
mockCurrentWorkspaceMember,
|
||||
);
|
||||
|
||||
mockUseUpdateOneRecord.mockReturnValue({
|
||||
updateOneRecord: mockUpdateOneRecord,
|
||||
});
|
||||
@@ -106,7 +119,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should return format preferences and update functions', () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current).toHaveProperty('formatPreferences');
|
||||
@@ -123,7 +136,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should return current format preferences', () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.formatPreferences).toEqual(mockInitialPreferences);
|
||||
@@ -131,7 +144,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should update single format preference successfully', async () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
const newTimeZone = 'Europe/London';
|
||||
@@ -152,7 +165,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should handle SYSTEM values by detecting actual format', async () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
mockDetectTimeZone.mockReturnValue('America/Chicago');
|
||||
@@ -174,7 +187,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should update multiple format preferences successfully', async () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
const updates = {
|
||||
@@ -198,17 +211,10 @@ describe('useFormatPreferences', () => {
|
||||
});
|
||||
|
||||
it('should not update preferences when user is not logged in', async () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: ({ children }: { children: ReactNode }) => {
|
||||
const initializeState = ({ set }: MutableSnapshot) => {
|
||||
set(currentWorkspaceMemberState, null);
|
||||
set(workspaceMemberFormatPreferencesState, mockInitialPreferences);
|
||||
};
|
||||
jotaiStore.set(currentWorkspaceMemberState.atom, null);
|
||||
|
||||
return (
|
||||
<RecoilRoot initializeState={initializeState}>{children}</RecoilRoot>
|
||||
);
|
||||
},
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -220,7 +226,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should handle update errors gracefully', async () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
const error = new Error('Update failed');
|
||||
@@ -238,7 +244,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should initialize format preferences from workspace member', () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -250,7 +256,7 @@ describe('useFormatPreferences', () => {
|
||||
|
||||
it('should not initialize format preferences when workspace member is null', () => {
|
||||
const { result } = renderHook(() => useFormatPreferences(), {
|
||||
wrapper: Wrapper,
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { DateFormat } from '@/localization/constants/DateFormat';
|
||||
@@ -18,6 +18,8 @@ import { getFormatPreferencesFromWorkspaceMember } from '@/localization/utils/fo
|
||||
import { getWorkspaceMemberUpdateFromFormatPreferences } from '@/localization/utils/format-preferences/getWorkspaceMemberUpdateFromFormatPreferences';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { CalendarStartDay } from 'twenty-shared/constants';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
@@ -28,8 +30,8 @@ export const useFormatPreferences = () => {
|
||||
workspaceMemberFormatPreferences,
|
||||
setWorkspaceMemberFormatPreferences,
|
||||
] = useRecoilState(workspaceMemberFormatPreferencesState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilStateV2(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
|
||||
+3
-4
@@ -1,10 +1,8 @@
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconFolderPlus } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CurrentWorkspaceMemberOrphanNavigationMenuItems } from '@/navigation-menu-item/components/CurrentWorkspaceMemberOrphanNavigationMenuItems';
|
||||
import { NavigationMenuItemFolders } from '@/navigation-menu-item/components/NavigationMenuItemFolders';
|
||||
import { NavigationMenuItemSkeletonLoader } from '@/navigation-menu-item/components/NavigationMenuItemSkeletonLoader';
|
||||
@@ -19,9 +17,10 @@ import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { isNavigationSectionOpenFamilyState } from '@/ui/navigation/navigation-drawer/states/isNavigationSectionOpenFamilyState';
|
||||
import { useFamilyRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilyRecoilValueV2';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const CurrentWorkspaceMemberNavigationMenuItemFolders = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
const { navigationMenuItemsSorted } = useSortedNavigationMenuItems();
|
||||
const { userNavigationMenuItemsByFolder } = useNavigationMenuItemsByFolder();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user