diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json
index d757119da..238b85cae 100644
--- a/packages/twenty-front/package.json
+++ b/packages/twenty-front/package.json
@@ -54,7 +54,6 @@
"buffer": "^6.0.3",
"docx": "^9.1.0",
"file-saver": "^2.0.5",
- "recoil-sync": "^0.2.0",
"transliteration": "^2.3.5",
"twenty-shared": "workspace:*",
"twenty-ui": "workspace:*"
diff --git a/packages/twenty-front/src/modules/app/components/App.tsx b/packages/twenty-front/src/modules/app/components/App.tsx
index 6ee92bd1f..fbc5ed335 100644
--- a/packages/twenty-front/src/modules/app/components/App.tsx
+++ b/packages/twenty-front/src/modules/app/components/App.tsx
@@ -9,35 +9,32 @@ import { i18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { HelmetProvider } from 'react-helmet-async';
import { RecoilRoot } from 'recoil';
-import { RecoilURLSyncJSON } from 'recoil-sync';
-import { initialI18nActivate } from '~/utils/i18n/initialI18nActivate';
import { IconsProvider } from 'twenty-ui/display';
+import { initialI18nActivate } from '~/utils/i18n/initialI18nActivate';
initialI18nActivate();
export const App = () => {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
index 9cad8927d..01ccee726 100644
--- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
+++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx
@@ -26,6 +26,7 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
import { isDefined } from 'twenty-shared/utils';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
+import { useInitializeQueryParamState } from '~/modules/app/hooks/useInitializeQueryParamState';
// TODO: break down into smaller functions and / or hooks
// - moved usePageChangeEffectNavigateLocation into dedicated hook
@@ -44,6 +45,8 @@ export const PageChangeEffect = () => {
const eventTracker = useEventTracker();
+ const { initializeQueryParamState } = useInitializeQueryParamState();
+
//TODO: refactor useResetTableRowSelection hook to not throw when the argument `recordTableId` is an empty string
// - replace CoreObjectNamePlural.Person
const objectNamePlural =
@@ -64,10 +67,12 @@ export const PageChangeEffect = () => {
}, [location, previousLocation, executeTasksOnAnyLocationChange]);
useEffect(() => {
+ initializeQueryParamState();
+
if (isDefined(pageChangeEffectNavigateLocation)) {
navigate(pageChangeEffectNavigateLocation);
}
- }, [navigate, pageChangeEffectNavigateLocation]);
+ }, [navigate, pageChangeEffectNavigateLocation, initializeQueryParamState]);
useEffect(() => {
const isLeavingRecordIndexPage = !!matchPath(
diff --git a/packages/twenty-front/src/modules/app/hooks/useInitializeQueryParamState.ts b/packages/twenty-front/src/modules/app/hooks/useInitializeQueryParamState.ts
new file mode 100644
index 000000000..d443ae906
--- /dev/null
+++ b/packages/twenty-front/src/modules/app/hooks/useInitializeQueryParamState.ts
@@ -0,0 +1,66 @@
+import { useRecoilCallback } from 'recoil';
+
+import { isQueryParamInitializedState } from '@/app/states/isQueryParamInitializedState';
+import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
+import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
+import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
+
+// 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 isInitialized = snapshot
+ .getLoadable(isQueryParamInitializedState)
+ .getValue();
+
+ if (!isInitialized) {
+ const handlers = {
+ billingCheckoutSession: (value: string) => {
+ try {
+ const parsedValue = JSON.parse(decodeURIComponent(value));
+
+ if (
+ typeof parsedValue === 'object' &&
+ parsedValue !== null &&
+ 'plan' in parsedValue &&
+ 'interval' in parsedValue &&
+ 'requirePaymentMethod' in 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);
+ }
+ }
+
+ set(isQueryParamInitializedState, true);
+ }
+ },
+ [],
+ );
+
+ return { initializeQueryParamState };
+};
diff --git a/packages/twenty-front/src/modules/app/states/isQueryParamInitializedState.ts b/packages/twenty-front/src/modules/app/states/isQueryParamInitializedState.ts
new file mode 100644
index 000000000..cd4d50739
--- /dev/null
+++ b/packages/twenty-front/src/modules/app/states/isQueryParamInitializedState.ts
@@ -0,0 +1,6 @@
+import { createState } from 'twenty-ui/utilities';
+
+export const isQueryParamInitializedState = createState({
+ key: 'isQueryParamInitializedState',
+ defaultValue: false,
+});
diff --git a/packages/twenty-front/src/modules/auth/components/AuthModal.tsx b/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
index 8277ee828..b461837e0 100644
--- a/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
+++ b/packages/twenty-front/src/modules/auth/components/AuthModal.tsx
@@ -10,18 +10,10 @@ const StyledContent = styled.div`
type AuthModalProps = {
children: React.ReactNode;
- isOpenAnimated?: boolean;
};
-export const AuthModal = ({
- children,
- isOpenAnimated = true,
-}: AuthModalProps) => (
-
+export const AuthModal = ({ children }: AuthModalProps) => (
+
{children}
diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
index c0cb3d4a3..9c104d7a3 100644
--- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
+++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx
@@ -4,12 +4,10 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin';
-import { animateModalState } from '@/auth/states/animateModalState';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { useLingui } from '@lingui/react/macro';
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
-import { useSetRecoilState } from 'recoil';
import { useNavigateApp } from '~/hooks/useNavigateApp';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent';
@@ -21,7 +19,6 @@ export const VerifyEmailEffect = () => {
const [searchParams] = useSearchParams();
const [isError, setIsError] = useState(false);
- const setAnimateModal = useSetRecoilState(animateModalState);
const email = searchParams.get('email');
const emailVerificationToken = searchParams.get('emailVerificationToken');
@@ -52,7 +49,6 @@ export const VerifyEmailEffect = () => {
const workspaceUrl = getWorkspaceUrl(workspaceUrls);
if (workspaceUrl.slice(0, -1) !== window.location.origin) {
- setAnimateModal(false);
return await redirectToWorkspaceDomain(workspaceUrl, AppPath.Verify, {
loginToken: loginToken.token,
});
diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
index 4dae0dafb..438e0aa92 100644
--- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
+++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts
@@ -41,7 +41,6 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi
import { currentUserState } from '../states/currentUserState';
import { tokenPairState } from '../states/tokenPairState';
-import { animateModalState } from '@/auth/states/animateModalState';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import {
SignInUpStep,
@@ -115,7 +114,6 @@ export const useAuth = () => {
const goToRecoilSnapshot = useGotoRecoilSnapshot();
const setDateTimeFormat = useSetRecoilState(dateTimeFormatState);
- const setAnimateModal = useSetRecoilState(animateModalState);
const [, setSearchParams] = useSearchParams();
@@ -422,7 +420,6 @@ export const useAuth = () => {
}
if (isMultiWorkspaceEnabled) {
- setAnimateModal(false);
return await redirectToWorkspaceDomain(
getWorkspaceUrl(signUpResult.data.signUp.workspace.workspaceUrls),
isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify,
@@ -446,7 +443,6 @@ export const useAuth = () => {
handleGetAuthTokensFromLoginToken,
setSignInUpStep,
setSearchParams,
- setAnimateModal,
isEmailVerificationRequired,
redirectToWorkspaceDomain,
],
diff --git a/packages/twenty-front/src/modules/auth/states/animateModalState.ts b/packages/twenty-front/src/modules/auth/states/animateModalState.ts
deleted file mode 100644
index 6c2cd96da..000000000
--- a/packages/twenty-front/src/modules/auth/states/animateModalState.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { urlSyncEffect } from 'recoil-sync';
-import { createState } from 'twenty-ui/utilities';
-
-export const animateModalState = createState({
- key: 'animateModalState',
- defaultValue: true,
- effects: [
- urlSyncEffect({
- itemKey: 'animateModal',
- refine: (value: unknown) => {
- if (typeof value === 'boolean') {
- return {
- type: 'success',
- value: value as boolean,
- warnings: [],
- } as const;
- }
- return {
- type: 'failure',
- message: 'Invalid animateModalState',
- path: [] as any,
- } as const;
- },
- }),
- ],
-});
diff --git a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
index dcb24afeb..b3bd10538 100644
--- a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
+++ b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
@@ -1,34 +1,8 @@
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
-import { syncEffect } from 'recoil-sync';
import { createState } from 'twenty-ui/utilities';
export const billingCheckoutSessionState = createState({
key: 'billingCheckoutSessionState',
defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
- effects: [
- syncEffect({
- itemKey: 'billingCheckoutSession',
- refine: (value: unknown) => {
- if (
- typeof value === 'object' &&
- value !== null &&
- 'plan' in value &&
- 'interval' in value &&
- 'requirePaymentMethod' in value
- ) {
- return {
- type: 'success',
- value: value as BillingCheckoutSession,
- warnings: [],
- } as const;
- }
- return {
- type: 'failure',
- message: 'Invalid BillingCheckoutSessionState',
- path: [] as any,
- } as const;
- },
- }),
- ],
});
diff --git a/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts b/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts
index 9996de191..b4fea3aa1 100644
--- a/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts
+++ b/packages/twenty-front/src/modules/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates.ts
@@ -1,4 +1,3 @@
-import { animateModalState } from '@/auth/states/animateModalState';
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
import { useRecoilCallback } from 'recoil';
@@ -7,7 +6,6 @@ export const useBuildSearchParamsFromUrlSyncedStates = () => {
const buildSearchParamsFromUrlSyncedStates = useRecoilCallback(
({ snapshot }) =>
async () => {
- const animateModal = snapshot.getLoadable(animateModalState).getValue();
const billingCheckoutSession = snapshot
.getLoadable(billingCheckoutSessionState)
.getValue();
@@ -18,7 +16,6 @@ export const useBuildSearchParamsFromUrlSyncedStates = () => {
billingCheckoutSession: JSON.stringify(billingCheckoutSession),
}
: {}),
- ...(animateModal === false ? { animateModal: 'false' } : {}),
};
return output;
diff --git a/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts b/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts
index 8e4e4abae..c6210ab8e 100644
--- a/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts
+++ b/packages/twenty-front/src/modules/settings/admin-panel/hooks/useImpersonationRedirect.ts
@@ -1,19 +1,15 @@
-import { animateModalState } from '@/auth/states/animateModalState';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
import { AppPath } from '@/types/AppPath';
-import { useSetRecoilState } from 'recoil';
import { WorkspaceUrls } from '~/generated/graphql';
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
export const useImpersonationRedirect = () => {
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
- const setAnimateModal = useSetRecoilState(animateModalState);
const executeImpersonationRedirect = async (
workspaceUrls: WorkspaceUrls,
loginToken: string,
) => {
- setAnimateModal(false);
return await redirectToWorkspaceDomain(
getWorkspaceUrl(workspaceUrls),
AppPath.Verify,
diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
index 150c1e9a9..cad149e21 100644
--- a/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/modal/components/Modal.tsx
@@ -174,7 +174,6 @@ export type ModalProps = React.PropsWithChildren & {
className?: string;
hotkeyScope?: ModalHotkeyScope;
onEnter?: () => void;
- isOpenAnimated?: boolean;
modalVariant?: ModalVariants;
} & (
| { isClosable: true; onClose: () => void }
@@ -197,7 +196,6 @@ export const Modal = ({
isClosable = false,
onClose,
modalVariant = 'primary',
- isOpenAnimated = true,
}: ModalProps) => {
const isMobile = useIsMobile();
const modalRef = useRef(null);
@@ -263,7 +261,7 @@ export const Modal = ({
ref={modalRef}
size={size}
padding={padding}
- initial={isOpenAnimated ? 'hidden' : 'visible'}
+ initial="hidden"
animate="visible"
exit="exit"
layout
diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx
index 72b57946f..f8a436a53 100644
--- a/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/page/components/DefaultLayout.tsx
@@ -1,5 +1,4 @@
import { AuthModal } from '@/auth/components/AuthModal';
-import { animateModalState } from '@/auth/states/animateModalState';
import { CommandMenuRouter } from '@/command-menu/components/CommandMenuRouter';
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import { AppFullScreenErrorFallback } from '@/error-handler/components/AppFullScreenErrorFallback';
@@ -19,7 +18,6 @@ import { Global, css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
import { Outlet } from 'react-router-dom';
-import { useRecoilValue } from 'recoil';
import { useScreenSize } from 'twenty-ui/utilities';
const StyledLayout = styled.div`
@@ -65,7 +63,6 @@ export const DefaultLayout = () => {
const windowsWidth = useScreenSize().width;
const showAuthModal = useShowAuthModal();
const useShowFullScreen = useShowFullscreen();
- const animateModal = useRecoilValue(animateModalState);
return (
<>
@@ -111,7 +108,7 @@ export const DefaultLayout = () => {
-
+
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
index da422e79d..bebc4de78 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
@@ -1,7 +1,6 @@
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import { useAuth } from '@/auth/hooks/useAuth';
-import { animateModalState } from '@/auth/states/animateModalState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { Workspaces, workspacesState } from '@/auth/states/workspaces';
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
@@ -67,7 +66,6 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
const setMultiWorkspaceDropdownState = useSetRecoilState(
multiWorkspaceDropdownState,
);
- const setAnimateModal = useSetRecoilState(animateModalState);
const handleChange = async (workspace: Workspaces[0]) => {
redirectToWorkspaceDomain(getWorkspaceUrl(workspace.workspaceUrls));
@@ -76,7 +74,6 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
const createWorkspace = () => {
signUpInNewWorkspaceMutation({
onCompleted: async (data) => {
- setAnimateModal(false);
return await redirectToWorkspaceDomain(
getWorkspaceUrl(data.signUpInNewWorkspace.workspace.workspaceUrls),
AppPath.Verify,
diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
index 890c0704b..2bdeb3ce8 100644
--- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
+++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
@@ -172,7 +172,7 @@ export const ChooseYourPlan = () => {
?.baseProduct.name;
return (
-
+
{isDefined(baseProductPrice) && isDefined(billing) ? (
<>
diff --git a/yarn.lock b/yarn.lock
index 8b1dfc268..a0a0282d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16495,13 +16495,6 @@ __metadata:
languageName: node
linkType: hard
-"@recoiljs/refine@npm:^0.1.1":
- version: 0.1.1
- resolution: "@recoiljs/refine@npm:0.1.1"
- checksum: 10c0/27ca1c4ea500b1b99a3af5ee48d6749310e5138e83b87ddfb41304e2222fa64567acb985f340334ab73980202ab277a0f133c40817fbec786076c06bfb3f5363
- languageName: node
- linkType: hard
-
"@redis/bloom@npm:1.2.0, @redis/bloom@npm:^1.2.0":
version: 1.2.0
resolution: "@redis/bloom@npm:1.2.0"
@@ -50073,18 +50066,6 @@ __metadata:
languageName: node
linkType: hard
-"recoil-sync@npm:^0.2.0":
- version: 0.2.0
- resolution: "recoil-sync@npm:0.2.0"
- dependencies:
- "@recoiljs/refine": "npm:^0.1.1"
- transit-js: "npm:^0.8.874"
- peerDependencies:
- recoil: ">=0.7.3"
- checksum: 10c0/f3a671a3cfcecadb5bbb22d47b3b040721be1013ee91d4fa0570a64ca1707eb68e818b291a82e133551adf4a2dd2fc9a0715d550c1f1e3db82f3b8d9169e2a5a
- languageName: node
- linkType: hard
-
"recoil@npm:^0.7.7":
version: 0.7.7
resolution: "recoil@npm:0.7.7"
@@ -54388,13 +54369,6 @@ __metadata:
languageName: node
linkType: hard
-"transit-js@npm:^0.8.874":
- version: 0.8.874
- resolution: "transit-js@npm:0.8.874"
- checksum: 10c0/6ca0b413f1e3780a4a56b9bbde54b67a2ffefda1c052f8dc68bf28c1a3df4c29baa6f17f60484c9f2fb056a8c7dcd1fe04d24e091fb99afe1525e4d4406ad58a
- languageName: node
- linkType: hard
-
"transliteration@npm:^2.3.5":
version: 2.3.5
resolution: "transliteration@npm:2.3.5"
@@ -55005,7 +54979,6 @@ __metadata:
eslint-plugin-unused-imports: "npm:^3.0.0"
file-saver: "npm:^2.0.5"
optionator: "npm:^0.9.1"
- recoil-sync: "npm:^0.2.0"
transliteration: "npm:^2.3.5"
twenty-shared: "workspace:*"
twenty-ui: "workspace:*"