Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93a35a50f1 | |||
| 98e47b6d1c | |||
| 8c98c2d752 | |||
| 4c635534ea | |||
| d7715c459f | |||
| 98c395f013 | |||
| 5f3821aaca | |||
| 0df4821e41 | |||
| f7c00991e2 | |||
| bdaaf8dcb5 | |||
| 4494aef51f | |||
| cb7deae98f | |||
| 3aa63a1c26 | |||
| e769b28dd5 | |||
| 15d34da5ff | |||
| 674f4353cd | |||
| c1a7726c33 |
+3
-3
@@ -3,7 +3,7 @@ import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePat
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsWorkspaceActivationStatusEqualsTo';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { AppPath, SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
|
||||
@@ -63,14 +63,14 @@ const setupMockUseParams = (objectNamePlural?: string) => {
|
||||
.mockReturnValueOnce({ objectNamePlural: objectNamePlural ?? '' });
|
||||
};
|
||||
|
||||
jest.mock('recoil');
|
||||
jest.mock('@/ui/utilities/state/jotai/hooks/useRecoilValueV2');
|
||||
const setupMockRecoil = (
|
||||
objectNamePlural?: string,
|
||||
verifyEmailRedirectPath?: string,
|
||||
calendarBookingPageId?: string | null,
|
||||
) => {
|
||||
jest
|
||||
.mocked(useRecoilValue)
|
||||
.mocked(useRecoilValueV2)
|
||||
.mockReturnValueOnce(calendarBookingPageId ?? 'mock-calendar-id')
|
||||
.mockReturnValueOnce([{ namePlural: objectNamePlural ?? '' }])
|
||||
.mockReturnValueOnce(verifyEmailRedirectPath);
|
||||
|
||||
@@ -5,9 +5,9 @@ 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';
|
||||
import { AppPath, SettingsPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
@@ -23,7 +23,7 @@ export const usePageChangeEffectNavigateLocation = () => {
|
||||
);
|
||||
const { defaultHomePagePath } = useDefaultHomePagePath();
|
||||
const location = useLocation();
|
||||
const calendarBookingPageId = useRecoilValue(calendarBookingPageIdState);
|
||||
const calendarBookingPageId = useRecoilValueV2(calendarBookingPageIdState);
|
||||
|
||||
const someMatchingLocationOf = (appPaths: AppPath[]): boolean =>
|
||||
appPaths.some((appPath) => isMatchingLocation(location, appPath));
|
||||
@@ -45,11 +45,13 @@ export const usePageChangeEffectNavigateLocation = () => {
|
||||
];
|
||||
|
||||
const objectNamePlural = useParams().objectNamePlural ?? '';
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
const objectMetadataItem = objectMetadataItems?.find(
|
||||
(objectMetadataItem) => objectMetadataItem.namePlural === objectNamePlural,
|
||||
);
|
||||
const verifyEmailRedirectPath = useRecoilValue(verifyEmailRedirectPathState);
|
||||
const verifyEmailRedirectPath = useRecoilValueV2(
|
||||
verifyEmailRedirectPathState,
|
||||
);
|
||||
|
||||
if (
|
||||
(!isLoggedIn || (isLoggedIn && !isOnAWorkspace)) &&
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ActionMenuContextProvider = ({
|
||||
@@ -23,7 +23,7 @@ export const ActionMenuContextProvider = ({
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
|
||||
const objectMetadataItem =
|
||||
objectMetadataItemOverride ??
|
||||
|
||||
+21
-25
@@ -16,9 +16,11 @@ import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPe
|
||||
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { useCallback, useContext, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -104,31 +106,25 @@ export const useShouldActionBeRegisteredParams = ({
|
||||
|
||||
const isSelectAll = contextStoreTargetedRecordsRule.mode === 'exclusion';
|
||||
|
||||
const getObjectReadPermission = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(objectMetadataNameSingular: string) => {
|
||||
return snapshot
|
||||
.getLoadable(
|
||||
objectPermissionsFamilySelector({
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
}),
|
||||
)
|
||||
.getValue().canRead;
|
||||
},
|
||||
const getObjectReadPermission = useCallback(
|
||||
(objectMetadataNameSingular: string) => {
|
||||
return jotaiStore.get(
|
||||
objectPermissionsFamilySelector.selectorFamily({
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
}),
|
||||
).canRead;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const getObjectWritePermission = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(objectMetadataNameSingular: string) => {
|
||||
return snapshot
|
||||
.getLoadable(
|
||||
objectPermissionsFamilySelector({
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
}),
|
||||
)
|
||||
.getValue().canUpdate;
|
||||
},
|
||||
const getObjectWritePermission = useCallback(
|
||||
(objectMetadataNameSingular: string) => {
|
||||
return jotaiStore.get(
|
||||
objectPermissionsFamilySelector.selectorFamily({
|
||||
objectNameSingular: objectMetadataNameSingular,
|
||||
}),
|
||||
).canUpdate;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -136,7 +132,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();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { type ActivityTargetableObject } from '@/activities/types/ActivityTarget
|
||||
import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
import { ActivityList } from '@/activities/components/ActivityList';
|
||||
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
|
||||
@@ -135,7 +135,7 @@ export const AttachmentList = ({
|
||||
const [previewedAttachment, setPreviewedAttachment] =
|
||||
useState<Attachment | null>(null);
|
||||
|
||||
const isAttachmentPreviewEnabled = useRecoilValue(
|
||||
const isAttachmentPreviewEnabled = useRecoilValueV2(
|
||||
isAttachmentPreviewEnabledState,
|
||||
);
|
||||
|
||||
|
||||
+22
-22
@@ -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,30 +113,32 @@ 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]);
|
||||
|
||||
jotaiStore.set(
|
||||
objectMetadataItemsState.atom,
|
||||
generatedMockObjectMetadataItems,
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
|
||||
const setRecordFromStore = useSetRecoilState(
|
||||
recordStoreFamilyState(task.id),
|
||||
);
|
||||
@@ -145,8 +149,6 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
|
||||
return {
|
||||
activityTargetObjectRecords,
|
||||
setCurrentWorkspaceMember,
|
||||
setObjectMetadataItems,
|
||||
setRecordFromStore,
|
||||
};
|
||||
},
|
||||
@@ -154,8 +156,6 @@ describe('useActivityTargetObjectRecords', () => {
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentWorkspaceMember(mockWorkspaceMembers[0]);
|
||||
result.current.setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
result.current.setRecordFromStore(task);
|
||||
});
|
||||
|
||||
|
||||
+6
-9
@@ -1,11 +1,12 @@
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import gql from 'graphql-tag';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { mockedTasks } from '~/testing/mock-data/tasks';
|
||||
@@ -72,6 +73,10 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
const mockObjectMetadataItems = generatedMockObjectMetadataItems;
|
||||
|
||||
describe('useOpenCreateActivityDrawer', () => {
|
||||
beforeEach(() => {
|
||||
jotaiStore.set(objectMetadataItemsState.atom, mockObjectMetadataItems);
|
||||
});
|
||||
|
||||
it('works as expected', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
@@ -79,13 +84,9 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
activityObjectNameSingular: CoreObjectNameSingular.Note,
|
||||
});
|
||||
const viewableRecordId = useRecoilValue(viewableRecordIdState);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
return {
|
||||
openActivityRightDrawer,
|
||||
viewableRecordId,
|
||||
setObjectMetadataItems,
|
||||
};
|
||||
},
|
||||
{
|
||||
@@ -93,10 +94,6 @@ describe('useOpenCreateActivityDrawer', () => {
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setObjectMetadataItems(mockObjectMetadataItems);
|
||||
});
|
||||
|
||||
expect(result.current.viewableRecordId).toBeNull();
|
||||
await act(async () => {
|
||||
result.current.openActivityRightDrawer({
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import { type TaskTarget } from '@/activities/types/TaskTarget';
|
||||
import { getActivityTargetObjectRecords } from '@/activities/utils/getActivityTargetObjectRecords';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { type Nullable } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -14,7 +15,7 @@ export const useActivityTargetObjectRecords = (
|
||||
activityRecordId?: string,
|
||||
activityTargets?: Nullable<NoteTarget[] | TaskTarget[]>,
|
||||
) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
|
||||
const activity = useRecoilValue(
|
||||
recordStoreFamilyState(activityRecordId ?? ''),
|
||||
|
||||
+2
-2
@@ -1,4 +1,3 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type RecordGqlOperationOrderBy } from 'twenty-shared/types';
|
||||
|
||||
import { findActivityTargetsOperationSignatureFactory } from '@/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory';
|
||||
@@ -10,6 +9,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
@@ -32,7 +32,7 @@ export const useActivityTargetsForTargetableObjects = ({
|
||||
activityTargetsOrderByVariables: RecordGqlOperationOrderBy;
|
||||
limit: number;
|
||||
}) => {
|
||||
const objectMetadataItems = useRecoilValue<ObjectMetadataItem[]>(
|
||||
const objectMetadataItems = useRecoilValueV2<ObjectMetadataItem[]>(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
const isNoteTargetMigrated = useIsFeatureEnabled(
|
||||
|
||||
+4
-4
@@ -1,5 +1,6 @@
|
||||
import { type ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useMultipleRecordPickerOpen } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerOpen';
|
||||
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
||||
@@ -24,14 +25,13 @@ export const useOpenActivityTargetCellEditMode = () => {
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const openActivityTargetCellEditMode = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
({ set }) =>
|
||||
({
|
||||
recordPickerInstanceId,
|
||||
activityTargetObjectRecords,
|
||||
}: OpenActivityTargetCellEditModeProps) => {
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable(objectMetadataItemsState)
|
||||
.getValue()
|
||||
const objectMetadataItems = jotaiStore
|
||||
.get(objectMetadataItemsState.atom)
|
||||
.filter(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.isSearchable &&
|
||||
|
||||
+4
-4
@@ -5,7 +5,6 @@ import { getActivityTargetFieldNameForObject } from '@/activities/utils/getActiv
|
||||
import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { searchRecordStoreFamilyState } from '@/object-record/record-picker/multiple-record-picker/states/searchRecordStoreComponentFamilyState';
|
||||
@@ -16,6 +15,7 @@ import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
type UpdateActivityTargetFromCellProps = {
|
||||
recordPickerInstanceId: string;
|
||||
@@ -79,9 +79,9 @@ export const useUpdateActivityTargetFromCell = ({
|
||||
? 'task'
|
||||
: 'note';
|
||||
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable<ObjectMetadataItem[]>(objectMetadataItemsState)
|
||||
.getValue();
|
||||
const objectMetadataItems = jotaiStore.get(
|
||||
objectMetadataItemsState.atom,
|
||||
);
|
||||
|
||||
const pickedObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
|
||||
+3
-2
@@ -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,9 +93,9 @@ export const EventRow = ({
|
||||
event,
|
||||
mainObjectMetadataItem,
|
||||
}: EventRowProps) => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspaceMember = useRecoilValueV2(currentWorkspaceMemberState);
|
||||
|
||||
const allowRequestsToTwentyIcons = useRecoilValue(
|
||||
const allowRequestsToTwentyIcons = useRecoilValueV2(
|
||||
allowRequestsToTwentyIconsState,
|
||||
);
|
||||
|
||||
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
export const useLinkedObjectObjectMetadataItem = (id: string | null) => {
|
||||
const objectMetadataItems: ObjectMetadataItem[] = useRecoilValue(
|
||||
const objectMetadataItems: ObjectMetadataItem[] = useRecoilValueV2(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -41,9 +41,7 @@ export const useGetBrowsingContext = () => {
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable(objectMetadataItemsState)
|
||||
.getValue();
|
||||
const objectMetadataItems = store.get(objectMetadataItemsState.atom);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataItemId,
|
||||
@@ -115,13 +113,11 @@ export const useGetBrowsingContext = () => {
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const currentView = snapshot
|
||||
.getLoadable(
|
||||
coreViewFromViewIdFamilySelector({
|
||||
viewId: currentViewId ?? '',
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
const currentView = store.get(
|
||||
coreViewFromViewIdFamilySelector.selectorFamily({
|
||||
viewId: currentViewId ?? '',
|
||||
}),
|
||||
);
|
||||
|
||||
if (!currentView) {
|
||||
return null;
|
||||
|
||||
@@ -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';
|
||||
@@ -92,7 +92,7 @@ export const PageChangeEffect = () => {
|
||||
const { executeTasksOnAnyLocationChange } =
|
||||
useExecuteTasksOnAnyLocationChange();
|
||||
|
||||
const isAppEffectRedirectEnabled = useRecoilValue(
|
||||
const isAppEffectRedirectEnabled = useRecoilValueV2(
|
||||
isAppEffectRedirectEnabledState,
|
||||
);
|
||||
|
||||
@@ -365,7 +365,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 { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -41,7 +42,7 @@ export const VerifyEmailEffect = () => {
|
||||
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
|
||||
const { verifyLoginToken } = useVerifyLogin();
|
||||
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
|
||||
const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState);
|
||||
const clientConfigApiStatus = useRecoilValueV2(clientConfigApiStatusState);
|
||||
|
||||
const { t } = useLingui();
|
||||
useEffect(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
@@ -17,7 +17,7 @@ export const VerifyLoginTokenEffect = () => {
|
||||
const navigate = useNavigateApp();
|
||||
const { verifyLoginToken } = useVerifyLogin();
|
||||
|
||||
const { isSaved: clientConfigLoaded } = useRecoilValue(
|
||||
const { isSaved: clientConfigLoaded } = useRecoilValueV2(
|
||||
clientConfigApiStatusState,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ 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';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import {
|
||||
email,
|
||||
@@ -158,15 +159,15 @@ describe('useAuth', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const client = useApolloClient();
|
||||
const workspaceAuthProviders = useRecoilValue(
|
||||
const workspaceAuthProviders = useRecoilValueV2(
|
||||
workspaceAuthProvidersState,
|
||||
);
|
||||
const billing = useRecoilValue(billingState);
|
||||
const isDeveloperDefaultSignInPrefilled = useRecoilValue(
|
||||
const billing = useRecoilValueV2(billingState);
|
||||
const isDeveloperDefaultSignInPrefilled = useRecoilValueV2(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
);
|
||||
const supportChat = useRecoilValue(supportChatState);
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(
|
||||
const supportChat = useRecoilValueV2(supportChatState);
|
||||
const isMultiWorkspaceEnabled = useRecoilValueV2(
|
||||
isMultiWorkspaceEnabledState,
|
||||
);
|
||||
return {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -4,14 +4,9 @@ import {
|
||||
snapshot_UNSTABLE,
|
||||
useGotoRecoilSnapshot,
|
||||
useRecoilCallback,
|
||||
useRecoilValue,
|
||||
useSetRecoilState,
|
||||
} from 'recoil';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
|
||||
import { billingState } from '@/client-config/states/billingState';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import {
|
||||
useCheckUserExistsLazyQuery,
|
||||
@@ -28,7 +23,9 @@ import {
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
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';
|
||||
@@ -47,11 +44,8 @@ import {
|
||||
} from '@/auth/utils/availableWorkspacesUtils';
|
||||
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
|
||||
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
|
||||
import { apiConfigState } from '@/client-config/states/apiConfigState';
|
||||
import { captchaState } from '@/client-config/states/captchaState';
|
||||
import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState';
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||
import { useOrigin } from '@/domain-manager/hooks/useOrigin';
|
||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
@@ -71,17 +65,19 @@ 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 isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const isEmailVerificationRequired = useRecoilValue(
|
||||
const isCaptchaScriptLoaded = useRecoilValueV2(isCaptchaScriptLoadedState);
|
||||
const isMultiWorkspaceEnabled = useRecoilValueV2(
|
||||
isMultiWorkspaceEnabledState,
|
||||
);
|
||||
const isEmailVerificationRequired = useRecoilValueV2(
|
||||
isEmailVerificationRequiredState,
|
||||
);
|
||||
const { loadCurrentUser } = useLoadCurrentUser();
|
||||
@@ -89,7 +85,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 +102,7 @@ export const useAuth = () => {
|
||||
useVerifyEmailAndGetWorkspaceAgnosticTokenMutation();
|
||||
const [getAuthTokensFromOtp] = useGetAuthTokensFromOtpMutation();
|
||||
|
||||
const workspacePublicData = useRecoilValue(workspacePublicDataState);
|
||||
const workspacePublicData = useRecoilValueV2(workspacePublicDataState);
|
||||
|
||||
const { setLastAuthenticateWorkspaceDomain } =
|
||||
useLastAuthenticatedWorkspaceDomain();
|
||||
@@ -123,7 +119,7 @@ export const useAuth = () => {
|
||||
const { loadMockedObjectMetadataItems } = useLoadMockedObjectMetadataItems();
|
||||
|
||||
const clearSession = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const sseClient = getSnapshotValue(snapshot, sseClientState);
|
||||
|
||||
@@ -131,53 +127,26 @@ export const useAuth = () => {
|
||||
|
||||
const emptySnapshot = snapshot_UNSTABLE();
|
||||
|
||||
const authProvidersValue = snapshot
|
||||
.getLoadable(workspaceAuthProvidersState)
|
||||
.getValue();
|
||||
const billing = snapshot.getLoadable(billingState).getValue();
|
||||
const isDeveloperDefaultSignInPrefilled = snapshot
|
||||
.getLoadable(isDeveloperDefaultSignInPrefilledState)
|
||||
.getValue();
|
||||
const supportChat = snapshot.getLoadable(supportChatState).getValue();
|
||||
const captcha = snapshot.getLoadable(captchaState).getValue();
|
||||
const clientConfigApiStatus = snapshot
|
||||
.getLoadable(clientConfigApiStatusState)
|
||||
.getValue();
|
||||
const isCurrentUserLoaded = snapshot
|
||||
.getLoadable(isCurrentUserLoadedState)
|
||||
.getValue();
|
||||
const isMultiWorkspaceEnabled = snapshot
|
||||
.getLoadable(isMultiWorkspaceEnabledState)
|
||||
.getValue();
|
||||
const domainConfiguration = snapshot
|
||||
.getLoadable(domainConfigurationState)
|
||||
.getValue();
|
||||
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 authProvidersValue = jotaiStore.get(
|
||||
workspaceAuthProvidersState.atom,
|
||||
);
|
||||
const isCurrentUserLoadedValue = jotaiStore.get(
|
||||
isCurrentUserLoadedState.atom,
|
||||
);
|
||||
const domainConfigurationValue = jotaiStore.get(
|
||||
domainConfigurationState.atom,
|
||||
);
|
||||
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,
|
||||
isDeveloperDefaultSignInPrefilled,
|
||||
);
|
||||
set(supportChatState, supportChat);
|
||||
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);
|
||||
const initialSnapshot = emptySnapshot.map(() => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
@@ -186,7 +155,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 +179,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
-5
@@ -19,10 +19,11 @@ 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 { 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 +40,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 captcha = useRecoilValueV2(captchaState);
|
||||
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
-5
@@ -7,8 +7,9 @@ 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 { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
@@ -21,13 +22,13 @@ enum LoadingStatus {
|
||||
}
|
||||
|
||||
export const SignInUpWorkspaceScopeFormEffect = () => {
|
||||
const workspaceAuthProviders = useRecoilValue(workspaceAuthProvidersState);
|
||||
const workspaceAuthProviders = useRecoilValueV2(workspaceAuthProvidersState);
|
||||
|
||||
const isRequestingCaptchaToken = useRecoilValue(
|
||||
const isRequestingCaptchaToken = useRecoilValueV2(
|
||||
isRequestingCaptchaTokenState,
|
||||
);
|
||||
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const captcha = useRecoilValueV2(captchaState);
|
||||
|
||||
const [loadingStatus, setLoadingStatus] = useState<LoadingStatus>(
|
||||
LoadingStatus.Loading,
|
||||
@@ -38,7 +39,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 };
|
||||
};
|
||||
|
||||
+19
-14
@@ -1,8 +1,13 @@
|
||||
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||
import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState';
|
||||
import {
|
||||
jotaiStore,
|
||||
resetJotaiStore,
|
||||
} from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
const TestWrapper = ({
|
||||
@@ -13,24 +18,24 @@ const TestWrapper = ({
|
||||
children: ReactNode;
|
||||
initialEntry?: string;
|
||||
isDeveloperDefaultSignInPrefilled?: boolean;
|
||||
}) => (
|
||||
<MemoryRouter initialEntries={[initialEntry]}>
|
||||
<RecoilRoot
|
||||
initializeState={(snapshot) => {
|
||||
snapshot.set(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
isDeveloperDefaultSignInPrefilled,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecoilRoot>
|
||||
</MemoryRouter>
|
||||
);
|
||||
}) => {
|
||||
jotaiStore.set(
|
||||
isDeveloperDefaultSignInPrefilledState.atom,
|
||||
isDeveloperDefaultSignInPrefilled,
|
||||
);
|
||||
return (
|
||||
<MemoryRouter initialEntries={[initialEntry]}>
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useSignInUpForm', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
resetJotaiStore();
|
||||
});
|
||||
|
||||
it('should initialize the form with default values', async () => {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { t } from '@lingui/core/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
@@ -13,6 +12,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,11 +34,11 @@ 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
|
||||
|
||||
const isDeveloperDefaultSignInPrefilled = useRecoilValue(
|
||||
const isDeveloperDefaultSignInPrefilled = useRecoilValueV2(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
);
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
@@ -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,8 +1,8 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { createFamilySelectorV2 } from '@/ui/utilities/state/jotai/utils/createFamilySelectorV2';
|
||||
|
||||
export const objectPermissionsFamilySelector = selectorFamily<
|
||||
export const objectPermissionsFamilySelector = createFamilySelectorV2<
|
||||
{
|
||||
canRead: boolean;
|
||||
canUpdate: boolean;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -6,9 +6,9 @@ import { createReactInlineContentSpec } from '@blocknote/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Chip, ChipVariant } from 'twenty-ui/components';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledRecordChip = styled(RecordChip)`
|
||||
height: auto;
|
||||
@@ -30,7 +30,7 @@ const LegacyMentionRenderer = ({
|
||||
recordId: string;
|
||||
objectMetadataId: string;
|
||||
}) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const objectMetadataItems = useRecoilValueV2(objectMetadataItemsState);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataId,
|
||||
|
||||
+6
-6
@@ -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 { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
|
||||
import { CaptchaDriverType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const CaptchaProviderScriptLoaderEffect = () => {
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const setIsCaptchaScriptLoaded = useSetRecoilState(
|
||||
const captcha = useRecoilValueV2(captchaState);
|
||||
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();
|
||||
|
||||
+26
-29
@@ -1,14 +1,23 @@
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { 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 = ({ children }: { children: ReactNode }) => (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</JotaiProvider>
|
||||
);
|
||||
|
||||
describe('useRequestFreshCaptchaToken', () => {
|
||||
const mockGrecaptchaExecute = jest.fn();
|
||||
const mockTurnstileRender = jest.fn();
|
||||
@@ -47,7 +56,7 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
(isCaptchaRequiredForPath as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const { result } = renderHook(() => useRequestFreshCaptchaToken(), {
|
||||
wrapper: RecoilRoot,
|
||||
wrapper: createWrapper,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -61,7 +70,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 +83,14 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
});
|
||||
|
||||
it('should request a token from Google reCAPTCHA when provider is GOOGLE_RECAPTCHA', async () => {
|
||||
jotaiStore.set(isRequestingCaptchaTokenState.atom, false);
|
||||
jotaiStore.set(captchaState.atom, {
|
||||
provider: CaptchaDriverType.GOOGLE_RECAPTCHA,
|
||||
siteKey: 'google-site-key',
|
||||
} as Captcha);
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
@@ -104,20 +107,14 @@ describe('useRequestFreshCaptchaToken', () => {
|
||||
});
|
||||
|
||||
it('should request a token from Turnstile when provider is TURNSTILE', async () => {
|
||||
jotaiStore.set(isRequestingCaptchaTokenState.atom, false);
|
||||
jotaiStore.set(captchaState.atom, {
|
||||
provider: CaptchaDriverType.TURNSTILE,
|
||||
siteKey: 'turnstile-site-key',
|
||||
} as Captcha);
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,61 +1,58 @@
|
||||
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 { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { useCallback } from 'react';
|
||||
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,
|
||||
);
|
||||
|
||||
const requestFreshCaptchaToken = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
if (!isCaptchaRequiredForPath(window.location.pathname)) {
|
||||
return;
|
||||
}
|
||||
const requestFreshCaptchaToken = useCallback(async () => {
|
||||
if (!isCaptchaRequiredForPath(window.location.pathname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const captcha = snapshot.getLoadable(captchaState).getValue();
|
||||
const captcha = jotaiStore.get(captchaState.atom);
|
||||
|
||||
if (!isDefined(captcha)) {
|
||||
return;
|
||||
}
|
||||
if (!isDefined(captcha)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertIsDefinedOrThrow(captcha);
|
||||
assertIsDefinedOrThrow(captcha);
|
||||
|
||||
setIsRequestingCaptchaToken(true);
|
||||
setIsRequestingCaptchaToken(true);
|
||||
|
||||
let captchaWidget: any;
|
||||
switch (captcha.provider) {
|
||||
case CaptchaDriverType.GOOGLE_RECAPTCHA:
|
||||
window.grecaptcha
|
||||
.execute(captcha.siteKey, {
|
||||
action: 'submit',
|
||||
})
|
||||
.then((token: string) => {
|
||||
setCaptchaToken(token);
|
||||
setIsRequestingCaptchaToken(false);
|
||||
});
|
||||
break;
|
||||
case CaptchaDriverType.TURNSTILE:
|
||||
captchaWidget = window.turnstile.render('#captcha-widget', {
|
||||
sitekey: captcha.siteKey,
|
||||
});
|
||||
window.turnstile.execute(captchaWidget, {
|
||||
callback: (token: string) => {
|
||||
setCaptchaToken(token);
|
||||
setIsRequestingCaptchaToken(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[setCaptchaToken, setIsRequestingCaptchaToken],
|
||||
);
|
||||
let captchaWidget: any;
|
||||
switch (captcha.provider) {
|
||||
case CaptchaDriverType.GOOGLE_RECAPTCHA:
|
||||
window.grecaptcha
|
||||
.execute(captcha.siteKey, {
|
||||
action: 'submit',
|
||||
})
|
||||
.then((token: string) => {
|
||||
setCaptchaToken(token);
|
||||
setIsRequestingCaptchaToken(false);
|
||||
});
|
||||
break;
|
||||
case CaptchaDriverType.TURNSTILE:
|
||||
captchaWidget = window.turnstile.render('#captcha-widget', {
|
||||
sitekey: captcha.siteKey,
|
||||
});
|
||||
window.turnstile.execute(captchaWidget, {
|
||||
callback: (token: string) => {
|
||||
setCaptchaToken(token);
|
||||
setIsRequestingCaptchaToken(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [setCaptchaToken, setIsRequestingCaptchaToken]);
|
||||
|
||||
return { requestFreshCaptchaToken };
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { AppFullScreenErrorFallback } from '@/error-handler/components/AppFullScreenErrorFallback';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { isErrored, error } = useRecoilValue(clientConfigApiStatusState);
|
||||
const { isErrored, error } = useRecoilValueV2(clientConfigApiStatusState);
|
||||
const { t } = useLingui();
|
||||
|
||||
return isErrored && error instanceof Error ? (
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import { useClientConfig } from '@/client-config/hooks/useClientConfig';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const ClientConfigProviderEffect = () => {
|
||||
const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilState(
|
||||
const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilStateV2(
|
||||
clientConfigApiStatusState,
|
||||
);
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@ 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 { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCaptcha = () => {
|
||||
const captcha = useRecoilValue(captchaState);
|
||||
const captchaToken = useRecoilValue(captchaTokenState);
|
||||
const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState);
|
||||
const isCaptchaScriptLoaded = useRecoilValue(isCaptchaScriptLoadedState);
|
||||
const captcha = useRecoilValueV2(captchaState);
|
||||
const captchaToken = useRecoilValueV2(captchaTokenState);
|
||||
const clientConfigApiStatus = useRecoilValueV2(clientConfigApiStatusState);
|
||||
const isCaptchaScriptLoaded = useRecoilValueV2(isCaptchaScriptLoadedState);
|
||||
|
||||
const isClientConfigLoaded = clientConfigApiStatus.isLoadedOnce;
|
||||
const isSiteKeyDefined = isDefined(captcha?.siteKey);
|
||||
|
||||
@@ -26,14 +26,14 @@ import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { type ClientConfig } from '@/client-config/types/ClientConfig';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { isAttachmentPreviewEnabledStateV2 } from '@/client-config/states/isAttachmentPreviewEnabledStateV2';
|
||||
import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState';
|
||||
import { getClientConfig } from '@/client-config/utils/getClientConfig';
|
||||
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { useRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilStateV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
|
||||
type UseClientConfigResult = {
|
||||
data: { clientConfig: ClientConfig } | undefined;
|
||||
@@ -45,35 +45,35 @@ 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);
|
||||
|
||||
const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState(
|
||||
const setIsDeveloperDefaultSignInPrefilled = useSetRecoilStateV2(
|
||||
isDeveloperDefaultSignInPrefilledState,
|
||||
);
|
||||
const setIsMultiWorkspaceEnabled = useSetRecoilState(
|
||||
const setIsMultiWorkspaceEnabled = useSetRecoilStateV2(
|
||||
isMultiWorkspaceEnabledState,
|
||||
);
|
||||
const setIsEmailVerificationRequired = useSetRecoilState(
|
||||
const setIsEmailVerificationRequired = useSetRecoilStateV2(
|
||||
isEmailVerificationRequiredState,
|
||||
);
|
||||
|
||||
const setBilling = useSetRecoilState(billingState);
|
||||
const setSupportChat = useSetRecoilState(supportChatState);
|
||||
const setBilling = useSetRecoilStateV2(billingState);
|
||||
const setSupportChat = useSetRecoilStateV2(supportChatState);
|
||||
|
||||
const setSentryConfig = useSetRecoilState(sentryConfigState);
|
||||
const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilState(
|
||||
const setSentryConfig = useSetRecoilStateV2(sentryConfigState);
|
||||
const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilStateV2(
|
||||
clientConfigApiStatusState,
|
||||
);
|
||||
|
||||
const setCaptcha = useSetRecoilState(captchaState);
|
||||
const setCaptcha = useSetRecoilStateV2(captchaState);
|
||||
|
||||
const setChromeExtensionId = useSetRecoilStateV2(chromeExtensionIdState);
|
||||
|
||||
const setApiConfig = useSetRecoilState(apiConfigState);
|
||||
const setApiConfig = useSetRecoilStateV2(apiConfigState);
|
||||
|
||||
const setCanManageFeatureFlags = useSetRecoilState(
|
||||
const setCanManageFeatureFlags = useSetRecoilStateV2(
|
||||
canManageFeatureFlagsState,
|
||||
);
|
||||
|
||||
@@ -81,57 +81,55 @@ export const useClientConfig = (): UseClientConfigResult => {
|
||||
labPublicFeatureFlagsStateV2,
|
||||
);
|
||||
|
||||
const setMicrosoftMessagingEnabled = useSetRecoilState(
|
||||
const setMicrosoftMessagingEnabled = useSetRecoilStateV2(
|
||||
isMicrosoftMessagingEnabledState,
|
||||
);
|
||||
|
||||
const setMicrosoftCalendarEnabled = useSetRecoilState(
|
||||
const setMicrosoftCalendarEnabled = useSetRecoilStateV2(
|
||||
isMicrosoftCalendarEnabledState,
|
||||
);
|
||||
|
||||
const setGoogleMessagingEnabled = useSetRecoilState(
|
||||
const setGoogleMessagingEnabled = useSetRecoilStateV2(
|
||||
isGoogleMessagingEnabledState,
|
||||
);
|
||||
|
||||
const setGoogleCalendarEnabled = useSetRecoilState(
|
||||
const setGoogleCalendarEnabled = useSetRecoilStateV2(
|
||||
isGoogleCalendarEnabledState,
|
||||
);
|
||||
|
||||
const setIsAttachmentPreviewEnabled = useSetRecoilState(
|
||||
const setIsAttachmentPreviewEnabled = useSetRecoilStateV2(
|
||||
isAttachmentPreviewEnabledState,
|
||||
);
|
||||
|
||||
const setIsConfigVariablesInDbEnabled = useSetRecoilState(
|
||||
const setIsConfigVariablesInDbEnabled = useSetRecoilStateV2(
|
||||
isConfigVariablesInDbEnabledState,
|
||||
);
|
||||
|
||||
const setCalendarBookingPageId = useSetRecoilState(
|
||||
const setCalendarBookingPageId = useSetRecoilStateV2(
|
||||
calendarBookingPageIdState,
|
||||
);
|
||||
|
||||
const setIsImapSmtpCaldavEnabled = useSetRecoilState(
|
||||
const setIsImapSmtpCaldavEnabled = useSetRecoilStateV2(
|
||||
isImapSmtpCaldavEnabledState,
|
||||
);
|
||||
const setIsEmailingDomainsEnabled = useSetRecoilState(
|
||||
const setIsEmailingDomainsEnabled = useSetRecoilStateV2(
|
||||
isEmailingDomainsEnabledState,
|
||||
);
|
||||
|
||||
const setAllowRequestsToTwentyIcons = useSetRecoilState(
|
||||
const setAllowRequestsToTwentyIcons = useSetRecoilStateV2(
|
||||
allowRequestsToTwentyIconsState,
|
||||
);
|
||||
|
||||
const setIsCloudflareIntegrationEnabled = useSetRecoilState(
|
||||
const setIsCloudflareIntegrationEnabled = useSetRecoilStateV2(
|
||||
isCloudflareIntegrationEnabledState,
|
||||
);
|
||||
|
||||
const setIsClickHouseConfigured = useSetRecoilState(
|
||||
const setIsClickHouseConfigured = useSetRecoilStateV2(
|
||||
isClickHouseConfiguredState,
|
||||
);
|
||||
|
||||
const setAppVersion = useSetRecoilStateV2(appVersionState);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const fetchClientConfig = useCallback(async () => {
|
||||
setClientConfigApiStatus((prev) => ({
|
||||
...prev,
|
||||
@@ -193,7 +191,7 @@ export const useClientConfig = (): UseClientConfigResult => {
|
||||
setGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled);
|
||||
setGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled);
|
||||
setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled);
|
||||
store.set(
|
||||
jotaiStore.set(
|
||||
isAttachmentPreviewEnabledStateV2.atom,
|
||||
clientConfig?.isAttachmentPreviewEnabled,
|
||||
);
|
||||
@@ -254,7 +252,6 @@ export const useClientConfig = (): UseClientConfigResult => {
|
||||
setSentryConfig,
|
||||
setSupportChat,
|
||||
setAllowRequestsToTwentyIcons,
|
||||
store,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const allowRequestsToTwentyIconsState = createState<boolean>({
|
||||
export const allowRequestsToTwentyIconsState = createStateV2<boolean>({
|
||||
key: 'allowRequestsToTwentyIcons',
|
||||
defaultValue: true,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type ApiConfig } from '~/generated-metadata/graphql';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const apiConfigState = createState<ApiConfig | null>({
|
||||
export const apiConfigState = createStateV2<ApiConfig | null>({
|
||||
key: 'apiConfigState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Billing } from '~/generated-metadata/graphql';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const billingState = createState<Billing | null>({
|
||||
export const billingState = createStateV2<Billing | null>({
|
||||
key: 'billingState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const calendarBookingPageIdState = createState<string | null>({
|
||||
export const calendarBookingPageIdState = createStateV2<string | null>({
|
||||
key: 'calendarBookingPageIdState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
export const canManageFeatureFlagsState = createState<boolean>({
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
export const canManageFeatureFlagsState = createStateV2<boolean>({
|
||||
key: 'canManageFeatureFlagsState',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Captcha } from '~/generated-metadata/graphql';
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
import { createStateV2 } from '@/ui/utilities/state/jotai/utils/createStateV2';
|
||||
|
||||
export const captchaState = createState<Captcha | null>({
|
||||
export const captchaState = createStateV2<Captcha | null>({
|
||||
key: 'captchaState',
|
||||
defaultValue: null,
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
type ClientConfigApiStatus = {
|
||||
isLoadedOnce: boolean;
|
||||
@@ -10,7 +10,7 @@ type ClientConfigApiStatus = {
|
||||
data?: { clientConfig: ClientConfig };
|
||||
};
|
||||
|
||||
export const clientConfigApiStatusState = createState<ClientConfigApiStatus>({
|
||||
export const clientConfigApiStatusState = createStateV2<ClientConfigApiStatus>({
|
||||
key: 'clientConfigApiStatus',
|
||||
defaultValue: {
|
||||
isLoadedOnce: false,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user