From 53647b30378989a60adff5b1bc467a069383585b Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 6 May 2024 11:20:44 -0600
Subject: [PATCH 01/23] refactor: Use async launch darkly

---
 apps/web/src/Providers.tsx | 57 +++++++++++++++++++++++++-------------
 1 file changed, 38 insertions(+), 19 deletions(-)

diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index 8b9a0b1eed9..bbd955820c4 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,8 +1,9 @@
+import { Loader } from '@mantine/core';
 import { CONTEXT_PATH, LAUNCH_DARKLY_CLIENT_SIDE_ID, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { withLDProvider } from 'launchdarkly-react-client-sdk';
-import { PropsWithChildren } from 'react';
+import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
+import { PropsWithChildren, useEffect, useRef, useState } from 'react';
 import { HelmetProvider } from 'react-helmet-async';
 import { BrowserRouter } from 'react-router-dom';
 import { api } from './api/api.client';
@@ -22,28 +23,46 @@ const queryClient = new QueryClient({
   },
 });
 
+type GenericProvider = ({ children }: { children: React.ReactNode }) => JSX.Element;
+
 /**
  * Centralized Provider hierarchy.
  */
 const Providers: React.FC<PropsWithChildren<{}>> = ({ children }) => {
+  const LDProvider = useRef<GenericProvider>((props) => <>{props.children}</>);
+  const [isLDReady, setIsLDReady] = useState<boolean>(false);
+
+  useEffect(() => {
+    const fetchLDProvider = async () => {
+      LDProvider.current = await asyncWithLDProvider({
+        clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
+        reactOptions: {
+          useCamelCaseFlagKeys: false,
+        },
+        deferInitialization: true,
+      });
+      setIsLDReady(true);
+    };
+    fetchLDProvider();
+  });
+
+  if (!isLDReady) {
+    return <Loader size={32} />;
+  }
+
   return (
-    <SegmentProvider>
-      <HelmetProvider>
-        <BrowserRouter basename={CONTEXT_PATH}>
-          <QueryClientProvider client={queryClient}>
-            <AuthProvider>{children}</AuthProvider>
-          </QueryClientProvider>
-        </BrowserRouter>
-      </HelmetProvider>
-    </SegmentProvider>
+    <LDProvider.current>
+      <SegmentProvider>
+        <HelmetProvider>
+          <BrowserRouter basename={CONTEXT_PATH}>
+            <QueryClientProvider client={queryClient}>
+              <AuthProvider>{children}</AuthProvider>
+            </QueryClientProvider>
+          </BrowserRouter>
+        </HelmetProvider>
+      </SegmentProvider>
+    </LDProvider.current>
   );
 };
 
-export default Sentry.withProfiler(
-  withLDProvider({
-    clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
-    reactOptions: {
-      useCamelCaseFlagKeys: false,
-    },
-  })(Providers)
-);
+export default Sentry.withProfiler(Providers);

From 4200cd7188559a0e0982956ab4239732b1fb9390 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 6 May 2024 21:37:19 -0600
Subject: [PATCH 02/23] refactor: LaunchDarklyProvider

---
 apps/web/src/LaunchDarklyProvider.tsx         | 94 +++++++++++++++++++
 apps/web/src/Providers.tsx                    | 52 +++-------
 .../shared-web/src/hooks/useAuthController.ts |  5 +-
 .../shared-web/src/providers/AuthProvider.tsx |  1 -
 4 files changed, 110 insertions(+), 42 deletions(-)
 create mode 100644 apps/web/src/LaunchDarklyProvider.tsx

diff --git a/apps/web/src/LaunchDarklyProvider.tsx b/apps/web/src/LaunchDarklyProvider.tsx
new file mode 100644
index 00000000000..1d360474fb3
--- /dev/null
+++ b/apps/web/src/LaunchDarklyProvider.tsx
@@ -0,0 +1,94 @@
+import { Loader } from '@mantine/core';
+import { LoadingOverlay } from '@novu/design-system';
+import { IOrganizationEntity } from '@novu/shared';
+import { LAUNCH_DARKLY_CLIENT_SIDE_ID, useAuthContext, useFeatureFlags } from '@novu/shared-web';
+import { asyncWithLDProvider, useLDClient } from 'launchdarkly-react-client-sdk';
+import { PropsWithChildren, useEffect, useRef, useState } from 'react';
+
+type GenericProvider = ({ children }: { children: React.ReactNode }) => JSX.Element;
+const DEFAULT_GENERIC_PROVIDER: GenericProvider = (props) => <>{props.children}</>;
+
+export interface ILaunchDarklyProviderProps {
+  organization?: IOrganizationEntity;
+}
+
+/**
+ * @requires AuthProvider must be wrapped in the AuthProvider.
+ */
+export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProviderProps>> = ({ children }) => {
+  const LDProvider = useRef<GenericProvider>(DEFAULT_GENERIC_PROVIDER);
+  const [isLDReady, setIsLDReady] = useState<boolean>(false);
+
+  const authContext = useAuthContext();
+  if (!authContext) {
+    throw new Error('LaunchDarklyProvider must be used within AuthProvider!');
+  }
+  const { currentOrganization } = authContext;
+  // const ldClient = useFeatureFlags();
+  // eslint-disable-next-line multiline-comment-style
+  // useEffect(() => {
+  //   console.log({ org: authContext.currentOrganization, ldClient });
+  //   if (!authContext.currentOrganization || !ldClient) {
+  //     return;
+  //   }
+  //   console.log('Reidentify', authContext.currentOrganization);
+  //   ldClient.identify({
+  //     kind: 'organization',
+  //     key: authContext.currentOrganization._id,
+  //     name: authContext.currentOrganization.name,
+  //   });
+  // }, [authContext.currentOrganization, ldClient]);
+
+  useEffect(() => {
+    const fetchLDProvider = async () => {
+      if (!currentOrganization) {
+        return;
+      }
+
+      LDProvider.current = await asyncWithLDProvider({
+        clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
+        context: {
+          kind: 'organization',
+          key: currentOrganization._id,
+          name: currentOrganization.name,
+        },
+        reactOptions: {
+          useCamelCaseFlagKeys: false,
+        },
+        // deferInitialization: true,
+      });
+      setIsLDReady(true);
+    };
+    fetchLDProvider();
+  }, [setIsLDReady, currentOrganization]);
+
+  /**
+   * Current issues:
+   * - This breaks login since there's no org -- can we match against "isUnprotectedUrl"?
+   * -
+   */
+
+  // eslint-disable-next-line multiline-comment-style
+  // if (!isLDReady) {
+  //   return (
+  //     <LoadingOverlay visible>
+  //       <></>
+  //     </LoadingOverlay>
+  //   );
+  // }
+
+  return (
+    <LDProvider.current>
+      <LaunchDarklyClientWrapper org={currentOrganization}>{children}</LaunchDarklyClientWrapper>
+    </LDProvider.current>
+  );
+};
+
+/**
+ * Refreshes feature flags on org change using the LaunchDarkly client from the provider.
+ */
+function LaunchDarklyClientWrapper({ children, org }: PropsWithChildren<{ org?: IOrganizationEntity }>) {
+  useFeatureFlags(org);
+
+  return <>{children}</>;
+}
diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index bbd955820c4..7a8db136079 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,13 +1,12 @@
-import { Loader } from '@mantine/core';
-import { CONTEXT_PATH, LAUNCH_DARKLY_CLIENT_SIDE_ID, SegmentProvider } from '@novu/shared-web';
+import { CONTEXT_PATH, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
-import { PropsWithChildren, useEffect, useRef, useState } from 'react';
+import { PropsWithChildren } from 'react';
 import { HelmetProvider } from 'react-helmet-async';
 import { BrowserRouter } from 'react-router-dom';
 import { api } from './api/api.client';
 import { AuthProvider } from './components/providers/AuthProvider';
+import { LaunchDarklyProvider } from './LaunchDarklyProvider';
 
 const defaultQueryFn = async ({ queryKey }: { queryKey: string }) => {
   const response = await api.get(`${queryKey[0]}`);
@@ -23,45 +22,22 @@ const queryClient = new QueryClient({
   },
 });
 
-type GenericProvider = ({ children }: { children: React.ReactNode }) => JSX.Element;
-
 /**
  * Centralized Provider hierarchy.
  */
 const Providers: React.FC<PropsWithChildren<{}>> = ({ children }) => {
-  const LDProvider = useRef<GenericProvider>((props) => <>{props.children}</>);
-  const [isLDReady, setIsLDReady] = useState<boolean>(false);
-
-  useEffect(() => {
-    const fetchLDProvider = async () => {
-      LDProvider.current = await asyncWithLDProvider({
-        clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
-        reactOptions: {
-          useCamelCaseFlagKeys: false,
-        },
-        deferInitialization: true,
-      });
-      setIsLDReady(true);
-    };
-    fetchLDProvider();
-  });
-
-  if (!isLDReady) {
-    return <Loader size={32} />;
-  }
-
   return (
-    <LDProvider.current>
-      <SegmentProvider>
-        <HelmetProvider>
-          <BrowserRouter basename={CONTEXT_PATH}>
-            <QueryClientProvider client={queryClient}>
-              <AuthProvider>{children}</AuthProvider>
-            </QueryClientProvider>
-          </BrowserRouter>
-        </HelmetProvider>
-      </SegmentProvider>
-    </LDProvider.current>
+    <SegmentProvider>
+      <QueryClientProvider client={queryClient}>
+        <AuthProvider>
+          <LaunchDarklyProvider>
+            <HelmetProvider>
+              <BrowserRouter basename={CONTEXT_PATH}>{children}</BrowserRouter>
+            </HelmetProvider>
+          </LaunchDarklyProvider>
+        </AuthProvider>
+      </QueryClientProvider>
+    </SegmentProvider>
   );
 };
 
diff --git a/libs/shared-web/src/hooks/useAuthController.ts b/libs/shared-web/src/hooks/useAuthController.ts
index a76f86c5b43..2c006214d67 100644
--- a/libs/shared-web/src/hooks/useAuthController.ts
+++ b/libs/shared-web/src/hooks/useAuthController.ts
@@ -1,7 +1,6 @@
 import { useEffect, useCallback, useState } from 'react';
 import axios from 'axios';
 import jwtDecode from 'jwt-decode';
-import { useNavigate } from 'react-router-dom';
 import { useQuery, useQueryClient } from '@tanstack/react-query';
 import * as Sentry from '@sentry/react';
 import type { IJwtPayload, IOrganizationEntity, IUserEntity } from '@novu/shared';
@@ -41,7 +40,6 @@ export function getToken(): string {
 export function useAuthController() {
   const segment = useSegment();
   const queryClient = useQueryClient();
-  const navigate = useNavigate();
   const [token, setToken] = useState<string | null>(() => {
     const initialToken = getToken();
     applyToken(initialToken);
@@ -122,7 +120,8 @@ export function useAuthController() {
   const logout = () => {
     setTokenCallback(null);
     queryClient.clear();
-    navigate('/auth/login');
+    // avoid usage of react-router here to prevent needing AuthProvider to be wrapped in the BrowserRouter
+    window.location.assign('/auth/login');
     segment.reset();
   };
 
diff --git a/libs/shared-web/src/providers/AuthProvider.tsx b/libs/shared-web/src/providers/AuthProvider.tsx
index 04c72b2cc72..04d7312ebc4 100644
--- a/libs/shared-web/src/providers/AuthProvider.tsx
+++ b/libs/shared-web/src/providers/AuthProvider.tsx
@@ -28,7 +28,6 @@ export const useAuthContext = (): UserContext => useContext(AuthContext);
 
 export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
   const { token, setToken, user, organization, isUserLoading, logout, jwtPayload, organizations } = useAuthController();
-  useFeatureFlags(organization);
 
   return (
     <AuthContext.Provider

From 1066caf2022d802e03e45b7692572d61533d8490 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 11:27:44 -0600
Subject: [PATCH 03/23] refactor: LD Provider with checks

---
 apps/web/src/LaunchDarklyProvider.tsx         | 69 ++++++++-----------
 apps/web/src/Providers.tsx                    | 21 +++++-
 libs/shared-web/src/constants/index.ts        |  1 +
 .../src/constants/unprotected-routes.const.ts | 12 ++++
 libs/shared-web/src/hooks/useFeatureFlags.ts  |  2 +-
 libs/shared-web/src/index.ts                  |  1 +
 .../src/utils/checkIsUnprotectedPathname.ts   |  5 ++
 libs/shared-web/src/utils/index.ts            |  1 +
 8 files changed, 71 insertions(+), 41 deletions(-)
 create mode 100644 libs/shared-web/src/constants/unprotected-routes.const.ts
 create mode 100644 libs/shared-web/src/utils/checkIsUnprotectedPathname.ts

diff --git a/apps/web/src/LaunchDarklyProvider.tsx b/apps/web/src/LaunchDarklyProvider.tsx
index 1d360474fb3..fa557d40e85 100644
--- a/apps/web/src/LaunchDarklyProvider.tsx
+++ b/apps/web/src/LaunchDarklyProvider.tsx
@@ -1,43 +1,41 @@
-import { Loader } from '@mantine/core';
-import { LoadingOverlay } from '@novu/design-system';
 import { IOrganizationEntity } from '@novu/shared';
-import { LAUNCH_DARKLY_CLIENT_SIDE_ID, useAuthContext, useFeatureFlags } from '@novu/shared-web';
-import { asyncWithLDProvider, useLDClient } from 'launchdarkly-react-client-sdk';
-import { PropsWithChildren, useEffect, useRef, useState } from 'react';
+import {
+  LAUNCH_DARKLY_CLIENT_SIDE_ID,
+  useAuthContext,
+  useFeatureFlags,
+  checkIsUnprotectedPathname,
+} from '@novu/shared-web';
+import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
+import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
 
-type GenericProvider = ({ children }: { children: React.ReactNode }) => JSX.Element;
-const DEFAULT_GENERIC_PROVIDER: GenericProvider = (props) => <>{props.children}</>;
+/** A provider with children required */
+type GenericLDProvider = Awaited<ReturnType<typeof asyncWithLDProvider>>;
+
+/** Simply renders the children */
+const DEFAULT_GENERIC_PROVIDER: GenericLDProvider = ({ children }) => <>{children}</>;
 
 export interface ILaunchDarklyProviderProps {
-  organization?: IOrganizationEntity;
+  /** Renders when LaunchDarkly is enabled and is awaiting initialization */
+  fallbackDisplay: ReactNode;
 }
 
 /**
+ * Async provider for feature flags.
+ *
  * @requires AuthProvider must be wrapped in the AuthProvider.
  */
-export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProviderProps>> = ({ children }) => {
-  const LDProvider = useRef<GenericProvider>(DEFAULT_GENERIC_PROVIDER);
+export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProviderProps>> = ({
+  children,
+  fallbackDisplay,
+}) => {
+  const LDProvider = useRef<GenericLDProvider>(DEFAULT_GENERIC_PROVIDER);
   const [isLDReady, setIsLDReady] = useState<boolean>(false);
 
   const authContext = useAuthContext();
   if (!authContext) {
-    throw new Error('LaunchDarklyProvider must be used within AuthProvider!');
+    throw new Error('LaunchDarklyProvider must be used within <AuthProvider>!');
   }
   const { currentOrganization } = authContext;
-  // const ldClient = useFeatureFlags();
-  // eslint-disable-next-line multiline-comment-style
-  // useEffect(() => {
-  //   console.log({ org: authContext.currentOrganization, ldClient });
-  //   if (!authContext.currentOrganization || !ldClient) {
-  //     return;
-  //   }
-  //   console.log('Reidentify', authContext.currentOrganization);
-  //   ldClient.identify({
-  //     kind: 'organization',
-  //     key: authContext.currentOrganization._id,
-  //     name: authContext.currentOrganization.name,
-  //   });
-  // }, [authContext.currentOrganization, ldClient]);
 
   useEffect(() => {
     const fetchLDProvider = async () => {
@@ -55,27 +53,16 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
         reactOptions: {
           useCamelCaseFlagKeys: false,
         },
-        // deferInitialization: true,
       });
       setIsLDReady(true);
     };
     fetchLDProvider();
   }, [setIsLDReady, currentOrganization]);
 
-  /**
-   * Current issues:
-   * - This breaks login since there's no org -- can we match against "isUnprotectedUrl"?
-   * -
-   */
-
   // eslint-disable-next-line multiline-comment-style
-  // if (!isLDReady) {
-  //   return (
-  //     <LoadingOverlay visible>
-  //       <></>
-  //     </LoadingOverlay>
-  //   );
-  // }
+  if (shouldUseLaunchDarkly() && !checkIsUnprotectedPathname(window.location.pathname) && !isLDReady) {
+    return <>{fallbackDisplay}</>;
+  }
 
   return (
     <LDProvider.current>
@@ -92,3 +79,7 @@ function LaunchDarklyClientWrapper({ children, org }: PropsWithChildren<{ org?:
 
   return <>{children}</>;
 }
+
+function shouldUseLaunchDarkly(): boolean {
+  return !!process.env.REACT_APP_LAUNCH_DARKLY_CLIENT_SIDE_ID;
+}
diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index 7a8db136079..3363d18f48d 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,3 +1,4 @@
+import { Loader } from '@mantine/core';
 import { CONTEXT_PATH, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -7,6 +8,7 @@ import { BrowserRouter } from 'react-router-dom';
 import { api } from './api/api.client';
 import { AuthProvider } from './components/providers/AuthProvider';
 import { LaunchDarklyProvider } from './LaunchDarklyProvider';
+import { css } from './styled-system/css';
 
 const defaultQueryFn = async ({ queryKey }: { queryKey: string }) => {
   const response = await api.get(`${queryKey[0]}`);
@@ -22,6 +24,23 @@ const queryClient = new QueryClient({
   },
 });
 
+const fallbackDisplay = (
+  <div
+    className={css({
+      h: '100dvh',
+      w: '100dvw',
+      display: 'grid',
+      placeItems: 'center',
+      bg: 'surface.page',
+      // Root element may not have loaded so rely on OS
+      _osDark: { bg: 'legacy.BGDark' },
+      _osLight: { bg: 'legacy.BGLight' },
+    })}
+  >
+    <Loader size={64} />
+  </div>
+);
+
 /**
  * Centralized Provider hierarchy.
  */
@@ -30,7 +49,7 @@ const Providers: React.FC<PropsWithChildren<{}>> = ({ children }) => {
     <SegmentProvider>
       <QueryClientProvider client={queryClient}>
         <AuthProvider>
-          <LaunchDarklyProvider>
+          <LaunchDarklyProvider fallbackDisplay={fallbackDisplay}>
             <HelmetProvider>
               <BrowserRouter basename={CONTEXT_PATH}>{children}</BrowserRouter>
             </HelmetProvider>
diff --git a/libs/shared-web/src/constants/index.ts b/libs/shared-web/src/constants/index.ts
index aa06998c968..75e665a2e00 100644
--- a/libs/shared-web/src/constants/index.ts
+++ b/libs/shared-web/src/constants/index.ts
@@ -1,2 +1,3 @@
 export * from './routes.enum';
 export * from './BaseEnvironmentEnum';
+export * from './unprotected-routes.const';
diff --git a/libs/shared-web/src/constants/unprotected-routes.const.ts b/libs/shared-web/src/constants/unprotected-routes.const.ts
new file mode 100644
index 00000000000..77f95518a8d
--- /dev/null
+++ b/libs/shared-web/src/constants/unprotected-routes.const.ts
@@ -0,0 +1,12 @@
+import { ROUTES } from './routes.enum';
+
+export const UNPROTECTED_ROUTES: ROUTES[] = [
+  ROUTES.AUTH_SIGNUP,
+  ROUTES.AUTH_LOGIN,
+  ROUTES.AUTH_RESET_REQUEST,
+  ROUTES.AUTH_RESET_TOKEN,
+  ROUTES.AUTH_INVITATION_TOKEN,
+  ROUTES.AUTH_APPLICATION,
+];
+
+export const UNPROTECTED_ROUTES_SET: Set<string> = new Set(UNPROTECTED_ROUTES);
diff --git a/libs/shared-web/src/hooks/useFeatureFlags.ts b/libs/shared-web/src/hooks/useFeatureFlags.ts
index 488b0441eef..afafa2b7ac5 100644
--- a/libs/shared-web/src/hooks/useFeatureFlags.ts
+++ b/libs/shared-web/src/hooks/useFeatureFlags.ts
@@ -5,7 +5,7 @@ import { useEffect } from 'react';
 
 import { FEATURE_FLAGS } from '../config';
 
-export const useFeatureFlags = (organization: IOrganizationEntity) => {
+export const useFeatureFlags = (organization?: IOrganizationEntity) => {
   const ldClient = useLDClient();
 
   useEffect(() => {
diff --git a/libs/shared-web/src/index.ts b/libs/shared-web/src/index.ts
index c903bad34fb..493e3ce358d 100644
--- a/libs/shared-web/src/index.ts
+++ b/libs/shared-web/src/index.ts
@@ -5,3 +5,4 @@ export * from './hooks';
 export * from './providers';
 export * from './constants';
 export * from './components';
+export * from './utils';
diff --git a/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts b/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts
new file mode 100644
index 00000000000..57fec771c4f
--- /dev/null
+++ b/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts
@@ -0,0 +1,5 @@
+import { UNPROTECTED_ROUTES_SET } from '../constants';
+
+export const checkIsUnprotectedPathname = (curPathname: string): boolean => {
+  return UNPROTECTED_ROUTES_SET.has(curPathname);
+};
diff --git a/libs/shared-web/src/utils/index.ts b/libs/shared-web/src/utils/index.ts
index 18c52ee5ae1..94cf7e378d0 100644
--- a/libs/shared-web/src/utils/index.ts
+++ b/libs/shared-web/src/utils/index.ts
@@ -1 +1,2 @@
 export * from './segment';
+export * from './checkIsUnprotectedPathname';

From 0e7da6ab41baba3a7d4e34bd411d8cca60adad33 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 11:35:01 -0600
Subject: [PATCH 04/23] refactor: Extract LaunchDarklyProvider to shared-web

---
 apps/web/src/Providers.tsx                    |  4 ++--
 .../src/providers}/LaunchDarklyProvider.tsx   | 21 ++++++++-----------
 libs/shared-web/src/providers/index.ts        |  1 +
 .../src/utils/checkShouldUseLaunchDarkly.ts   |  3 +++
 libs/shared-web/src/utils/index.ts            |  1 +
 5 files changed, 16 insertions(+), 14 deletions(-)
 rename {apps/web/src => libs/shared-web/src/providers}/LaunchDarklyProvider.tsx (80%)
 create mode 100644 libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts

diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index 3363d18f48d..04b26dd7968 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,5 +1,5 @@
 import { Loader } from '@mantine/core';
-import { CONTEXT_PATH, SegmentProvider } from '@novu/shared-web';
+import { CONTEXT_PATH, LaunchDarklyProvider, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 import { PropsWithChildren } from 'react';
@@ -7,7 +7,6 @@ import { HelmetProvider } from 'react-helmet-async';
 import { BrowserRouter } from 'react-router-dom';
 import { api } from './api/api.client';
 import { AuthProvider } from './components/providers/AuthProvider';
-import { LaunchDarklyProvider } from './LaunchDarklyProvider';
 import { css } from './styled-system/css';
 
 const defaultQueryFn = async ({ queryKey }: { queryKey: string }) => {
@@ -24,6 +23,7 @@ const queryClient = new QueryClient({
   },
 });
 
+/** Full-page loader that uses color-preferences for background */
 const fallbackDisplay = (
   <div
     className={css({
diff --git a/apps/web/src/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
similarity index 80%
rename from apps/web/src/LaunchDarklyProvider.tsx
rename to libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index fa557d40e85..7c75942b7de 100644
--- a/apps/web/src/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -1,12 +1,10 @@
 import { IOrganizationEntity } from '@novu/shared';
-import {
-  LAUNCH_DARKLY_CLIENT_SIDE_ID,
-  useAuthContext,
-  useFeatureFlags,
-  checkIsUnprotectedPathname,
-} from '@novu/shared-web';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
+import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
+import { useFeatureFlags } from '../hooks';
+import { checkIsUnprotectedPathname, checkShouldUseLaunchDarkly } from '../utils';
+import { useAuthContext } from './AuthProvider';
 
 /** A provider with children required */
 type GenericLDProvider = Awaited<ReturnType<typeof asyncWithLDProvider>>;
@@ -59,8 +57,11 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
     fetchLDProvider();
   }, [setIsLDReady, currentOrganization]);
 
-  // eslint-disable-next-line multiline-comment-style
-  if (shouldUseLaunchDarkly() && !checkIsUnprotectedPathname(window.location.pathname) && !isLDReady) {
+  /**
+   * For self-hosted, LD will not be enabled, so do not block initialization.
+   * Checking unprotected (routes without org-based auth) is required to ensure that such routes still load.
+   */
+  if (checkShouldUseLaunchDarkly() && !checkIsUnprotectedPathname(window.location.pathname) && !isLDReady) {
     return <>{fallbackDisplay}</>;
   }
 
@@ -79,7 +80,3 @@ function LaunchDarklyClientWrapper({ children, org }: PropsWithChildren<{ org?:
 
   return <>{children}</>;
 }
-
-function shouldUseLaunchDarkly(): boolean {
-  return !!process.env.REACT_APP_LAUNCH_DARKLY_CLIENT_SIDE_ID;
-}
diff --git a/libs/shared-web/src/providers/index.ts b/libs/shared-web/src/providers/index.ts
index 38f270699df..d844de645cf 100644
--- a/libs/shared-web/src/providers/index.ts
+++ b/libs/shared-web/src/providers/index.ts
@@ -1,2 +1,3 @@
 export * from './SegmentProvider';
 export * from './AuthProvider';
+export * from './LaunchDarklyProvider';
diff --git a/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
new file mode 100644
index 00000000000..4f6be93d4ef
--- /dev/null
+++ b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
@@ -0,0 +1,3 @@
+export const checkShouldUseLaunchDarkly = (): boolean => {
+  return !!process.env.REACT_APP_LAUNCH_DARKLY_CLIENT_SIDE_ID;
+};
diff --git a/libs/shared-web/src/utils/index.ts b/libs/shared-web/src/utils/index.ts
index 94cf7e378d0..6adc6c71ce1 100644
--- a/libs/shared-web/src/utils/index.ts
+++ b/libs/shared-web/src/utils/index.ts
@@ -1,2 +1,3 @@
 export * from './segment';
 export * from './checkIsUnprotectedPathname';
+export * from './checkShouldUseLaunchDarkly';

From 938916541e6b5da93b4c8352c5688c383c8fa01f Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 11:36:03 -0600
Subject: [PATCH 05/23] fix: Don't show /brand page when IA is enabled

---
 apps/web/src/AppRoutes.tsx      | 11 +++++++----
 apps/web/src/SettingsRoutes.tsx |  2 --
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/apps/web/src/AppRoutes.tsx b/apps/web/src/AppRoutes.tsx
index 0b5fb6b2b50..309b62c9d80 100644
--- a/apps/web/src/AppRoutes.tsx
+++ b/apps/web/src/AppRoutes.tsx
@@ -49,6 +49,7 @@ import { useSettingsRoutes } from './SettingsRoutes';
 
 export const AppRoutes = () => {
   const isImprovedOnboardingEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_IMPROVED_ONBOARDING_ENABLED);
+  const isInformationArchitectureEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_INFORMATION_ARCHITECTURE_ENABLED);
 
   return (
     <Routes>
@@ -116,10 +117,12 @@ export const AppRoutes = () => {
         <Route path={ROUTES.TEAM} element={<MembersInvitePage />} />
         <Route path={ROUTES.CHANGES} element={<PromoteChangesPage />} />
         <Route path={ROUTES.SUBSCRIBERS} element={<SubscribersList />} />
-        <Route path={ROUTES.BRAND} element={<BrandPage />}>
-          <Route path="" element={<BrandingForm />} />
-          <Route path="layouts" element={<LayoutsListPage />} />
-        </Route>
+        {!isInformationArchitectureEnabled && (
+          <Route path={ROUTES.BRAND} element={<BrandPage />}>
+            <Route path="" element={<BrandingForm />} />
+            <Route path="layouts" element={<LayoutsListPage />} />
+          </Route>
+        )}
         <Route path={ROUTES.LAYOUT} element={<LayoutsPage />}>
           <Route path="" element={<LayoutsListPage />} />
         </Route>
diff --git a/apps/web/src/SettingsRoutes.tsx b/apps/web/src/SettingsRoutes.tsx
index 1d5c75e513a..228fd5b3701 100644
--- a/apps/web/src/SettingsRoutes.tsx
+++ b/apps/web/src/SettingsRoutes.tsx
@@ -6,7 +6,6 @@ import { ProductLead } from './components/utils/ProductLead';
 import { ROUTES } from './constants/routes.enum';
 import { useFeatureFlag } from './hooks';
 import { BillingRoutes } from './pages/BillingPages';
-import { BrandingForm as BrandingFormOld } from './pages/brand/tabs';
 import { BrandingPage } from './pages/brand/tabs/v2';
 import { MembersInvitePage as MembersInvitePageNew } from './pages/invites/v2/MembersInvitePage';
 import { AccessSecurityPage, ApiKeysPage, BillingPage, TeamPage, UserProfilePage } from './pages/settings';
@@ -50,7 +49,6 @@ export const useSettingsRoutes = () => {
         <Route path="billing/*" element={<BillingRoutes />} />
         <Route path="email" element={<EmailSettings />} />
         <Route path="team" element={<MembersInvitePageNew />} />
-        <Route path="brand" element={<BrandingFormOld />} />
         <Route
           path="permissions"
           element={

From 952ec8f24cb77d36d11baa802b172e97fc100ed7 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 12:11:35 -0600
Subject: [PATCH 06/23] feat: Update loader

---
 apps/web/src/Providers.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index 04b26dd7968..6016e1ed9ad 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,4 +1,5 @@
 import { Loader } from '@mantine/core';
+import { colors } from '@novu/design-system';
 import { CONTEXT_PATH, LaunchDarklyProvider, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -37,7 +38,7 @@ const fallbackDisplay = (
       _osLight: { bg: 'legacy.BGLight' },
     })}
   >
-    <Loader size={64} />
+    <Loader size={64} variant="bars" color={colors.gradientMiddle} />
   </div>
 );
 

From e992ffd90303dc1588346e8b02b34d3c8e1deb80 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 12:46:54 -0600
Subject: [PATCH 07/23] fix: Avoid any calls to LD if disabled

---
 libs/shared-web/src/hooks/useFeatureFlags.ts         | 12 +++++++-----
 .../src/providers/LaunchDarklyProvider.tsx           | 10 ++++++----
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/libs/shared-web/src/hooks/useFeatureFlags.ts b/libs/shared-web/src/hooks/useFeatureFlags.ts
index afafa2b7ac5..8fc30303392 100644
--- a/libs/shared-web/src/hooks/useFeatureFlags.ts
+++ b/libs/shared-web/src/hooks/useFeatureFlags.ts
@@ -1,7 +1,7 @@
 import { FeatureFlagsKeysEnum, IOrganizationEntity, prepareBooleanStringFeatureFlag } from '@novu/shared';
-import { useFlags } from 'launchdarkly-react-client-sdk';
-import { useLDClient } from 'launchdarkly-react-client-sdk';
+import { useFlags, useLDClient } from 'launchdarkly-react-client-sdk';
 import { useEffect } from 'react';
+import { checkShouldUseLaunchDarkly } from '../utils';
 
 import { FEATURE_FLAGS } from '../config';
 
@@ -9,7 +9,7 @@ export const useFeatureFlags = (organization?: IOrganizationEntity) => {
   const ldClient = useLDClient();
 
   useEffect(() => {
-    if (!organization?._id) {
+    if (!checkShouldUseLaunchDarkly() || !organization?._id) {
       return;
     }
 
@@ -24,10 +24,12 @@ export const useFeatureFlags = (organization?: IOrganizationEntity) => {
 };
 
 export const useFeatureFlag = (key: FeatureFlagsKeysEnum): boolean => {
-  const { [key]: featureFlag } = useFlags();
+  /** We knowingly break the rule of hooks here to avoid making any LaunchDarkly calls when it is disabled */
+  // eslint-disable-next-line
+  const flagValue = checkShouldUseLaunchDarkly() ? useFlags()[key] : undefined;
   const fallbackValue = false;
   const value = FEATURE_FLAGS[key];
   const defaultValue = prepareBooleanStringFeatureFlag(value, fallbackValue);
 
-  return featureFlag ?? defaultValue;
+  return flagValue ?? defaultValue;
 };
diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index 7c75942b7de..e25b2fff8d6 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -36,11 +36,12 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
   const { currentOrganization } = authContext;
 
   useEffect(() => {
-    const fetchLDProvider = async () => {
-      if (!currentOrganization) {
-        return;
-      }
+    // no need to fetch if LD is disabled or there isn't an org to query against
+    if (!checkShouldUseLaunchDarkly() || !currentOrganization) {
+      return;
+    }
 
+    const fetchLDProvider = async () => {
       LDProvider.current = await asyncWithLDProvider({
         clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
         context: {
@@ -54,6 +55,7 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
       });
       setIsLDReady(true);
     };
+
     fetchLDProvider();
   }, [setIsLDReady, currentOrganization]);
 

From a98073dc828a38333b78742706cc90b6c072f69e Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Tue, 7 May 2024 13:44:24 -0600
Subject: [PATCH 08/23] refactor: Use auth state instead of routes!

---
 libs/shared-web/src/constants/index.ts               |  1 -
 .../src/constants/unprotected-routes.const.ts        | 12 ------------
 libs/shared-web/src/providers/AuthProvider.tsx       |  8 ++++++--
 .../src/providers/LaunchDarklyProvider.tsx           |  8 ++++----
 .../src/utils/checkIsUnprotectedPathname.ts          |  5 -----
 libs/shared-web/src/utils/index.ts                   |  1 -
 6 files changed, 10 insertions(+), 25 deletions(-)
 delete mode 100644 libs/shared-web/src/constants/unprotected-routes.const.ts
 delete mode 100644 libs/shared-web/src/utils/checkIsUnprotectedPathname.ts

diff --git a/libs/shared-web/src/constants/index.ts b/libs/shared-web/src/constants/index.ts
index 75e665a2e00..aa06998c968 100644
--- a/libs/shared-web/src/constants/index.ts
+++ b/libs/shared-web/src/constants/index.ts
@@ -1,3 +1,2 @@
 export * from './routes.enum';
 export * from './BaseEnvironmentEnum';
-export * from './unprotected-routes.const';
diff --git a/libs/shared-web/src/constants/unprotected-routes.const.ts b/libs/shared-web/src/constants/unprotected-routes.const.ts
deleted file mode 100644
index 77f95518a8d..00000000000
--- a/libs/shared-web/src/constants/unprotected-routes.const.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ROUTES } from './routes.enum';
-
-export const UNPROTECTED_ROUTES: ROUTES[] = [
-  ROUTES.AUTH_SIGNUP,
-  ROUTES.AUTH_LOGIN,
-  ROUTES.AUTH_RESET_REQUEST,
-  ROUTES.AUTH_RESET_TOKEN,
-  ROUTES.AUTH_INVITATION_TOKEN,
-  ROUTES.AUTH_APPLICATION,
-];
-
-export const UNPROTECTED_ROUTES_SET: Set<string> = new Set(UNPROTECTED_ROUTES);
diff --git a/libs/shared-web/src/providers/AuthProvider.tsx b/libs/shared-web/src/providers/AuthProvider.tsx
index 04d7312ebc4..6b954533fe6 100644
--- a/libs/shared-web/src/providers/AuthProvider.tsx
+++ b/libs/shared-web/src/providers/AuthProvider.tsx
@@ -1,9 +1,10 @@
 import React, { useContext } from 'react';
 import { IOrganizationEntity, IUserEntity, IJwtPayload } from '@novu/shared';
-import { useAuthController, useFeatureFlags } from '../hooks';
+import { useAuthController } from '../hooks';
 
 type UserContext = {
   token: string | null;
+  isLoggedIn: boolean;
   currentUser: IUserEntity | undefined;
   isUserLoading: boolean;
   currentOrganization: IOrganizationEntity | undefined;
@@ -15,6 +16,7 @@ type UserContext = {
 
 const AuthContext = React.createContext<UserContext>({
   token: null,
+  isLoggedIn: false,
   currentUser: undefined,
   isUserLoading: true,
   setToken: undefined as any,
@@ -27,12 +29,14 @@ const AuthContext = React.createContext<UserContext>({
 export const useAuthContext = (): UserContext => useContext(AuthContext);
 
 export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
-  const { token, setToken, user, organization, isUserLoading, logout, jwtPayload, organizations } = useAuthController();
+  const { token, setToken, user, organization, isUserLoading, logout, jwtPayload, organizations, isLoggedIn } =
+    useAuthController();
 
   return (
     <AuthContext.Provider
       value={{
         currentUser: user,
+        isLoggedIn,
         isUserLoading,
         currentOrganization: organization,
         organizations,
diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index e25b2fff8d6..8967be545de 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -3,7 +3,7 @@ import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
 import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
 import { useFeatureFlags } from '../hooks';
-import { checkIsUnprotectedPathname, checkShouldUseLaunchDarkly } from '../utils';
+import { checkShouldUseLaunchDarkly } from '../utils';
 import { useAuthContext } from './AuthProvider';
 
 /** A provider with children required */
@@ -33,7 +33,7 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
   if (!authContext) {
     throw new Error('LaunchDarklyProvider must be used within <AuthProvider>!');
   }
-  const { currentOrganization } = authContext;
+  const { currentOrganization, isLoggedIn } = authContext;
 
   useEffect(() => {
     // no need to fetch if LD is disabled or there isn't an org to query against
@@ -61,9 +61,9 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
 
   /**
    * For self-hosted, LD will not be enabled, so do not block initialization.
-   * Checking unprotected (routes without org-based auth) is required to ensure that such routes still load.
+   * Must not show the fallback if the user isn't logged-in to avoid issues with un-authenticated routes (i.e. login).
    */
-  if (checkShouldUseLaunchDarkly() && !checkIsUnprotectedPathname(window.location.pathname) && !isLDReady) {
+  if (checkShouldUseLaunchDarkly() && isLoggedIn && !isLDReady) {
     return <>{fallbackDisplay}</>;
   }
 
diff --git a/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts b/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts
deleted file mode 100644
index 57fec771c4f..00000000000
--- a/libs/shared-web/src/utils/checkIsUnprotectedPathname.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { UNPROTECTED_ROUTES_SET } from '../constants';
-
-export const checkIsUnprotectedPathname = (curPathname: string): boolean => {
-  return UNPROTECTED_ROUTES_SET.has(curPathname);
-};
diff --git a/libs/shared-web/src/utils/index.ts b/libs/shared-web/src/utils/index.ts
index 6adc6c71ce1..a8a6e27eb8a 100644
--- a/libs/shared-web/src/utils/index.ts
+++ b/libs/shared-web/src/utils/index.ts
@@ -1,3 +1,2 @@
 export * from './segment';
-export * from './checkIsUnprotectedPathname';
 export * from './checkShouldUseLaunchDarkly';

From 961a3346c64eb4cd0f147d2a8946e74f588df371 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 09:39:18 -0700
Subject: [PATCH 09/23] fix: Use config for env var

---
 libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
index 4f6be93d4ef..19f653189f2 100644
--- a/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
+++ b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
@@ -1,3 +1,5 @@
+import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
+
 export const checkShouldUseLaunchDarkly = (): boolean => {
-  return !!process.env.REACT_APP_LAUNCH_DARKLY_CLIENT_SIDE_ID;
+  return !!LAUNCH_DARKLY_CLIENT_SIDE_ID;
 };

From 00bf34eaee36f90c9c6ad5963eb82de55622e8a8 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 16:11:42 -0700
Subject: [PATCH 10/23] refactor: Export UserContext

---
 libs/shared-web/src/providers/AuthProvider.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libs/shared-web/src/providers/AuthProvider.tsx b/libs/shared-web/src/providers/AuthProvider.tsx
index 6b954533fe6..4534bf64587 100644
--- a/libs/shared-web/src/providers/AuthProvider.tsx
+++ b/libs/shared-web/src/providers/AuthProvider.tsx
@@ -2,7 +2,7 @@ import React, { useContext } from 'react';
 import { IOrganizationEntity, IUserEntity, IJwtPayload } from '@novu/shared';
 import { useAuthController } from '../hooks';
 
-type UserContext = {
+export type UserContext = {
   token: string | null;
   isLoggedIn: boolean;
   currentUser: IUserEntity | undefined;

From a34a72b6f02eee049aa6265e9f598350cea1b10d Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 16:13:29 -0700
Subject: [PATCH 11/23] refactor: Use utils for determining LD init

---
 libs/shared-web/src/hooks/useFeatureFlags.ts  |  4 +-
 .../src/providers/LaunchDarklyProvider.tsx    | 97 +++++++++++++++----
 2 files changed, 79 insertions(+), 22 deletions(-)

diff --git a/libs/shared-web/src/hooks/useFeatureFlags.ts b/libs/shared-web/src/hooks/useFeatureFlags.ts
index 8fc30303392..e828e67f352 100644
--- a/libs/shared-web/src/hooks/useFeatureFlags.ts
+++ b/libs/shared-web/src/hooks/useFeatureFlags.ts
@@ -9,11 +9,11 @@ export const useFeatureFlags = (organization?: IOrganizationEntity) => {
   const ldClient = useLDClient();
 
   useEffect(() => {
-    if (!checkShouldUseLaunchDarkly() || !organization?._id) {
+    if (!checkShouldUseLaunchDarkly() || !organization?._id || !ldClient) {
       return;
     }
 
-    ldClient?.identify({
+    ldClient.identify({
       kind: 'organization',
       key: organization._id,
       name: organization.name,
diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index 8967be545de..506800f2cb8 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -1,10 +1,10 @@
-import { IOrganizationEntity } from '@novu/shared';
+import { IOrganizationEntity, IUserEntity } from '@novu/shared';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
-import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
+import { PropsWithChildren, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
 import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
 import { useFeatureFlags } from '../hooks';
 import { checkShouldUseLaunchDarkly } from '../utils';
-import { useAuthContext } from './AuthProvider';
+import { useAuthContext, UserContext } from './AuthProvider';
 
 /** A provider with children required */
 type GenericLDProvider = Awaited<ReturnType<typeof asyncWithLDProvider>>;
@@ -33,37 +33,53 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
   if (!authContext) {
     throw new Error('LaunchDarklyProvider must be used within <AuthProvider>!');
   }
-  const { currentOrganization, isLoggedIn } = authContext;
+  const { currentOrganization, isLoggedIn, isUserLoading, currentUser } = authContext;
+
+  const { shouldWaitForLd, doesNeedOrg } = useMemo(() => checkShouldInitializeLaunchDarkly(authContext), [authContext]);
 
   useEffect(() => {
-    // no need to fetch if LD is disabled or there isn't an org to query against
-    if (!checkShouldUseLaunchDarkly() || !currentOrganization) {
+    if (!shouldWaitForLd) {
       return;
     }
 
     const fetchLDProvider = async () => {
-      LDProvider.current = await asyncWithLDProvider({
-        clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
-        context: {
-          kind: 'organization',
-          key: currentOrganization._id,
-          name: currentOrganization.name,
-        },
-        reactOptions: {
-          useCamelCaseFlagKeys: false,
-        },
-      });
-      setIsLDReady(true);
+      try {
+        LDProvider.current = await asyncWithLDProvider({
+          clientSideID: LAUNCH_DARKLY_CLIENT_SIDE_ID,
+          reactOptions: {
+            useCamelCaseFlagKeys: false,
+          },
+          // determine which context to use based on if an organization is available
+          context: currentOrganization
+            ? {
+                kind: 'organization',
+                key: currentOrganization._id,
+                name: currentOrganization.name,
+              }
+            : {
+                /**
+                 * When user is not authenticated, assigns an id to them to ensure consistent results.
+                 * https://docs.launchdarkly.com/sdk/features/anonymous#javascript
+                 */
+                kind: 'user',
+                anonymous: true,
+              },
+        });
+      } catch (err: unknown) {
+        // FIXME: what should we do here since we don't have logging?
+      } finally {
+        setIsLDReady(true);
+      }
     };
 
     fetchLDProvider();
-  }, [setIsLDReady, currentOrganization]);
+  }, [setIsLDReady, shouldWaitForLd, currentOrganization]);
 
   /**
    * For self-hosted, LD will not be enabled, so do not block initialization.
    * Must not show the fallback if the user isn't logged-in to avoid issues with un-authenticated routes (i.e. login).
    */
-  if (checkShouldUseLaunchDarkly() && isLoggedIn && !isLDReady) {
+  if ((shouldWaitForLd || (doesNeedOrg && !currentOrganization)) && !isLDReady) {
     return <>{fallbackDisplay}</>;
   }
 
@@ -82,3 +98,44 @@ function LaunchDarklyClientWrapper({ children, org }: PropsWithChildren<{ org?:
 
   return <>{children}</>;
 }
+
+function checkShouldInitializeLaunchDarkly(userCtx: UserContext): { shouldWaitForLd: boolean; doesNeedOrg?: boolean } {
+  const { isLoggedIn, currentOrganization } = userCtx;
+
+  if (!checkShouldUseLaunchDarkly()) {
+    return { shouldWaitForLd: false };
+  }
+
+  // enable feature flags for unauthenticated areas of the app
+  if (!isLoggedIn) {
+    return { shouldWaitForLd: true };
+  }
+
+  // user must be loaded -- a user can have `isLoggedIn` true when `currentUser` is undefined
+  // eslint-disable-next-line
+  // if (!currentUser) {
+  //   return { shouldWaitForLd: false };
+  // }
+
+  // allow LD to load when the user is created but still in onboarding
+  const isUserFullyRegistered = checkIsUserFullyRegistered(userCtx);
+  if (!isUserFullyRegistered) {
+    return { shouldWaitForLd: true };
+  }
+
+  // if a user is fully on-boarded, but no organization has loaded, we must wait for the organization to initialize the client.
+  return { shouldWaitForLd: !!currentOrganization, doesNeedOrg: true };
+}
+
+/**
+ * Determine if a user is fully-registered; if not, they're still in onboarding.
+ */
+function checkIsUserFullyRegistered(userCtx: UserContext): boolean {
+  /*
+   * Determine if the user has completed registration based on if they have an associated orgId.
+   * Use jobTitle as a back-up
+   */
+  const isUserFullyRegistered = !!userCtx.jwtPayload?.organizationId || !!userCtx.currentUser?.jobTitle;
+
+  return isUserFullyRegistered;
+}

From 953339e94534a3d46ab0b4aaef8c2f2e4a97ec37 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 22:50:13 -0700
Subject: [PATCH 12/23] refactor: Extract util

---
 .../src/providers/LaunchDarklyProvider.tsx     | 18 +++---------------
 .../src/utils/auth-selectors/index.ts          |  1 +
 .../selectHasUserCompletedSignUp.ts            | 13 +++++++++++++
 3 files changed, 17 insertions(+), 15 deletions(-)
 create mode 100644 libs/shared-web/src/utils/auth-selectors/index.ts
 create mode 100644 libs/shared-web/src/utils/auth-selectors/selectHasUserCompletedSignUp.ts

diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index 506800f2cb8..32f26aa47ff 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -1,6 +1,7 @@
-import { IOrganizationEntity, IUserEntity } from '@novu/shared';
+import { IOrganizationEntity } from '@novu/shared';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
+import { selectHasUserCompletedSignUp } from '../utils/auth-selectors';
 import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
 import { useFeatureFlags } from '../hooks';
 import { checkShouldUseLaunchDarkly } from '../utils';
@@ -118,7 +119,7 @@ function checkShouldInitializeLaunchDarkly(userCtx: UserContext): { shouldWaitFo
   // }
 
   // allow LD to load when the user is created but still in onboarding
-  const isUserFullyRegistered = checkIsUserFullyRegistered(userCtx);
+  const isUserFullyRegistered = selectHasUserCompletedSignUp(userCtx);
   if (!isUserFullyRegistered) {
     return { shouldWaitForLd: true };
   }
@@ -126,16 +127,3 @@ function checkShouldInitializeLaunchDarkly(userCtx: UserContext): { shouldWaitFo
   // if a user is fully on-boarded, but no organization has loaded, we must wait for the organization to initialize the client.
   return { shouldWaitForLd: !!currentOrganization, doesNeedOrg: true };
 }
-
-/**
- * Determine if a user is fully-registered; if not, they're still in onboarding.
- */
-function checkIsUserFullyRegistered(userCtx: UserContext): boolean {
-  /*
-   * Determine if the user has completed registration based on if they have an associated orgId.
-   * Use jobTitle as a back-up
-   */
-  const isUserFullyRegistered = !!userCtx.jwtPayload?.organizationId || !!userCtx.currentUser?.jobTitle;
-
-  return isUserFullyRegistered;
-}
diff --git a/libs/shared-web/src/utils/auth-selectors/index.ts b/libs/shared-web/src/utils/auth-selectors/index.ts
new file mode 100644
index 00000000000..53240e5334f
--- /dev/null
+++ b/libs/shared-web/src/utils/auth-selectors/index.ts
@@ -0,0 +1 @@
+export * from './selectHasUserCompletedSignUp';
diff --git a/libs/shared-web/src/utils/auth-selectors/selectHasUserCompletedSignUp.ts b/libs/shared-web/src/utils/auth-selectors/selectHasUserCompletedSignUp.ts
new file mode 100644
index 00000000000..31900f917cf
--- /dev/null
+++ b/libs/shared-web/src/utils/auth-selectors/selectHasUserCompletedSignUp.ts
@@ -0,0 +1,13 @@
+import { UserContext } from '../../providers';
+
+/**
+ * Determine if a user is fully-registered; if not, they're still in onboarding.
+ */
+export const selectHasUserCompletedSignUp = (userCtx: UserContext): boolean => {
+  if (!userCtx) {
+    return false;
+  }
+
+  // User has completed registration if they have an associated orgId.
+  return !!userCtx.jwtPayload?.organizationId;
+};

From 037d94600458894c4664ffcecea288c9092f078f Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 23:38:59 -0700
Subject: [PATCH 13/23] refactor: Extract selectors

---
 .../src/providers/LaunchDarklyProvider.tsx    | 43 +++----------------
 .../src/utils/auth-selectors/index.ts         |  2 +
 .../selectShouldInitializeLaunchDarkly.tsx    | 25 +++++++++++
 .../selectShouldShowLaunchDarklyFallback.tsx  | 25 +++++++++++
 4 files changed, 59 insertions(+), 36 deletions(-)
 create mode 100644 libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
 create mode 100644 libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx

diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index 32f26aa47ff..522ab21c7b0 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -1,11 +1,10 @@
 import { IOrganizationEntity } from '@novu/shared';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
-import { selectHasUserCompletedSignUp } from '../utils/auth-selectors';
 import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
 import { useFeatureFlags } from '../hooks';
-import { checkShouldUseLaunchDarkly } from '../utils';
-import { useAuthContext, UserContext } from './AuthProvider';
+import { useAuthContext } from './AuthProvider';
+import { selectShouldShowLaunchDarklyFallback, selectShouldInitializeLaunchDarkly } from '../utils/auth-selectors';
 
 /** A provider with children required */
 type GenericLDProvider = Awaited<ReturnType<typeof asyncWithLDProvider>>;
@@ -34,12 +33,12 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
   if (!authContext) {
     throw new Error('LaunchDarklyProvider must be used within <AuthProvider>!');
   }
-  const { currentOrganization, isLoggedIn, isUserLoading, currentUser } = authContext;
+  const { currentOrganization } = authContext;
 
-  const { shouldWaitForLd, doesNeedOrg } = useMemo(() => checkShouldInitializeLaunchDarkly(authContext), [authContext]);
+  const shouldInitializeLd = useMemo(() => selectShouldInitializeLaunchDarkly(authContext), [authContext]);
 
   useEffect(() => {
-    if (!shouldWaitForLd) {
+    if (!shouldInitializeLd) {
       return;
     }
 
@@ -74,13 +73,13 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
     };
 
     fetchLDProvider();
-  }, [setIsLDReady, shouldWaitForLd, currentOrganization]);
+  }, [setIsLDReady, shouldInitializeLd, currentOrganization]);
 
   /**
    * For self-hosted, LD will not be enabled, so do not block initialization.
    * Must not show the fallback if the user isn't logged-in to avoid issues with un-authenticated routes (i.e. login).
    */
-  if ((shouldWaitForLd || (doesNeedOrg && !currentOrganization)) && !isLDReady) {
+  if (selectShouldShowLaunchDarklyFallback(authContext, isLDReady)) {
     return <>{fallbackDisplay}</>;
   }
 
@@ -99,31 +98,3 @@ function LaunchDarklyClientWrapper({ children, org }: PropsWithChildren<{ org?:
 
   return <>{children}</>;
 }
-
-function checkShouldInitializeLaunchDarkly(userCtx: UserContext): { shouldWaitForLd: boolean; doesNeedOrg?: boolean } {
-  const { isLoggedIn, currentOrganization } = userCtx;
-
-  if (!checkShouldUseLaunchDarkly()) {
-    return { shouldWaitForLd: false };
-  }
-
-  // enable feature flags for unauthenticated areas of the app
-  if (!isLoggedIn) {
-    return { shouldWaitForLd: true };
-  }
-
-  // user must be loaded -- a user can have `isLoggedIn` true when `currentUser` is undefined
-  // eslint-disable-next-line
-  // if (!currentUser) {
-  //   return { shouldWaitForLd: false };
-  // }
-
-  // allow LD to load when the user is created but still in onboarding
-  const isUserFullyRegistered = selectHasUserCompletedSignUp(userCtx);
-  if (!isUserFullyRegistered) {
-    return { shouldWaitForLd: true };
-  }
-
-  // if a user is fully on-boarded, but no organization has loaded, we must wait for the organization to initialize the client.
-  return { shouldWaitForLd: !!currentOrganization, doesNeedOrg: true };
-}
diff --git a/libs/shared-web/src/utils/auth-selectors/index.ts b/libs/shared-web/src/utils/auth-selectors/index.ts
index 53240e5334f..ca4767bd396 100644
--- a/libs/shared-web/src/utils/auth-selectors/index.ts
+++ b/libs/shared-web/src/utils/auth-selectors/index.ts
@@ -1 +1,3 @@
 export * from './selectHasUserCompletedSignUp';
+export * from './selectShouldShowLaunchDarklyFallback';
+export * from './selectShouldInitializeLaunchDarkly';
diff --git a/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx b/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
new file mode 100644
index 00000000000..59d90e31a0a
--- /dev/null
+++ b/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
@@ -0,0 +1,25 @@
+import { selectHasUserCompletedSignUp } from './selectHasUserCompletedSignUp';
+import { checkShouldUseLaunchDarkly } from '../checkShouldUseLaunchDarkly';
+import { UserContext } from '../../providers/AuthProvider';
+
+/** Determine if LaunchDarkly should be initialized based on the current auth context */
+export function selectShouldInitializeLaunchDarkly(userCtx: UserContext): boolean {
+  const { isLoggedIn, currentOrganization } = userCtx;
+  // don't show fallback if LaunchDarkly isn't enabled
+  if (!checkShouldUseLaunchDarkly()) {
+    return false;
+  }
+
+  // enable feature flags for unauthenticated areas of the app
+  if (!isLoggedIn) {
+    return true;
+  }
+
+  // allow LD to load when the user is created but still in onboarding
+  if (!selectHasUserCompletedSignUp(userCtx)) {
+    return true;
+  }
+
+  // if a user is fully on-boarded, but no organization has loaded, we must wait for the organization to initialize the client.
+  return !!currentOrganization;
+}
diff --git a/libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx b/libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx
new file mode 100644
index 00000000000..96bb2f746d6
--- /dev/null
+++ b/libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx
@@ -0,0 +1,25 @@
+import { selectHasUserCompletedSignUp } from '.';
+import { checkShouldUseLaunchDarkly } from '..';
+import { UserContext } from '../../providers/AuthProvider';
+
+/** Determine if a fallback should be shown instead of the provider-wrapped application */
+export function selectShouldShowLaunchDarklyFallback(userCtx: UserContext, isLDReady: boolean): boolean {
+  const { isLoggedIn, currentOrganization } = userCtx;
+  // don't show fallback if LaunchDarkly isn't enabled
+  if (!checkShouldUseLaunchDarkly()) {
+    return false;
+  }
+
+  // don't show fallback for unauthenticated areas of the app
+  if (!isLoggedIn) {
+    return false;
+  }
+
+  // don't show fallback if user is still in onboarding
+  if (!selectHasUserCompletedSignUp(userCtx)) {
+    return false;
+  }
+
+  // if the organization is not loaded or we haven't loaded LD, show the fallback
+  return !currentOrganization || !isLDReady;
+}

From 59b0edba81310040f6280d21497e855d2968b096 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 23:48:23 -0700
Subject: [PATCH 14/23] fix: Hide new layouts route behind flag

---
 apps/web/src/AppRoutes.tsx | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/apps/web/src/AppRoutes.tsx b/apps/web/src/AppRoutes.tsx
index 309b62c9d80..514f9b219ab 100644
--- a/apps/web/src/AppRoutes.tsx
+++ b/apps/web/src/AppRoutes.tsx
@@ -117,15 +117,16 @@ export const AppRoutes = () => {
         <Route path={ROUTES.TEAM} element={<MembersInvitePage />} />
         <Route path={ROUTES.CHANGES} element={<PromoteChangesPage />} />
         <Route path={ROUTES.SUBSCRIBERS} element={<SubscribersList />} />
-        {!isInformationArchitectureEnabled && (
+        {!isInformationArchitectureEnabled ? (
           <Route path={ROUTES.BRAND} element={<BrandPage />}>
             <Route path="" element={<BrandingForm />} />
             <Route path="layouts" element={<LayoutsListPage />} />
           </Route>
+        ) : (
+          <Route path={ROUTES.LAYOUT} element={<LayoutsPage />}>
+            <Route path="" element={<LayoutsListPage />} />
+          </Route>
         )}
-        <Route path={ROUTES.LAYOUT} element={<LayoutsPage />}>
-          <Route path="" element={<LayoutsListPage />} />
-        </Route>
         <Route path="/translations/*" element={<TranslationRoutes />} />
       </Route>
     </Routes>

From c1cc6c9c286b221c57a1f53d30910079f2f02034 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Wed, 8 May 2024 23:58:55 -0700
Subject: [PATCH 15/23] refactor: Add comment + export

---
 libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts | 1 +
 libs/shared-web/src/utils/index.ts                      | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
index 19f653189f2..6cc3a28f6f0 100644
--- a/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
+++ b/libs/shared-web/src/utils/checkShouldUseLaunchDarkly.ts
@@ -1,5 +1,6 @@
 import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
 
+/** Determine if client-side LaunchDarkly should be enabled */
 export const checkShouldUseLaunchDarkly = (): boolean => {
   return !!LAUNCH_DARKLY_CLIENT_SIDE_ID;
 };
diff --git a/libs/shared-web/src/utils/index.ts b/libs/shared-web/src/utils/index.ts
index a8a6e27eb8a..6cd450c1251 100644
--- a/libs/shared-web/src/utils/index.ts
+++ b/libs/shared-web/src/utils/index.ts
@@ -1,2 +1,3 @@
 export * from './segment';
 export * from './checkShouldUseLaunchDarkly';
+export * from './auth-selectors';

From 30da61428b8493131562d1cfb51ba6d69a228c5e Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Thu, 9 May 2024 09:56:16 -0700
Subject: [PATCH 16/23] test: Auth spec

---
 apps/web/cypress/tests/auth.spec.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/apps/web/cypress/tests/auth.spec.ts b/apps/web/cypress/tests/auth.spec.ts
index 2c7b61b8577..51e0e66051c 100644
--- a/apps/web/cypress/tests/auth.spec.ts
+++ b/apps/web/cypress/tests/auth.spec.ts
@@ -1,7 +1,10 @@
 import * as capitalize from 'lodash.capitalize';
-import { JobTitleEnum, jobTitleToLabelMapper } from '@novu/shared';
+import { FeatureFlagsKeysEnum, JobTitleEnum, jobTitleToLabelMapper } from '@novu/shared';
 
 describe('User Sign-up and Login', function () {
+  beforeEach(function () {
+    cy.mockFeatureFlags({ [FeatureFlagsKeysEnum.IS_INFORMATION_ARCHITECTURE_ENABLED]: false });
+  });
   describe('Sign up', function () {
     beforeEach(function () {
       cy.clearDatabase();
@@ -206,6 +209,7 @@ describe('User Sign-up and Login', function () {
       cy.clock(date);
 
       cy.visit('/subscribers');
+      cy.waitLoadFeatureFlags();
 
       // checking if token is removed from local storage
       cy.getLocalStorage('auth_token').should('be.null');

From 43273d9355c41098b76e78a4f7558b003e57ac3a Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Fri, 10 May 2024 14:10:56 -0700
Subject: [PATCH 17/23] test: Wait for feature flags

---
 apps/web/cypress/tests/auth.spec.ts | 61 ++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 15 deletions(-)

diff --git a/apps/web/cypress/tests/auth.spec.ts b/apps/web/cypress/tests/auth.spec.ts
index 51e0e66051c..829ba9b21c3 100644
--- a/apps/web/cypress/tests/auth.spec.ts
+++ b/apps/web/cypress/tests/auth.spec.ts
@@ -13,7 +13,9 @@ describe('User Sign-up and Login', function () {
 
     it('should allow a visitor to sign-up, login, and logout', function () {
       cy.intercept('**/organization/**/switch').as('appSwitch');
-      cy.visit('/auth/signup');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/signup');
+      });
       cy.getByTestId('fullName').type('Test User');
       cy.getByTestId('email').type('example@example.com');
       cy.getByTestId('password').type('usEr_password_123!');
@@ -33,7 +35,9 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should show account already exists when signing up with already registered mail', function () {
-      cy.visit('/auth/signup');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/signup');
+      });
       cy.getByTestId('fullName').type('Test User');
       cy.getByTestId('email').type('test-user-1@example.com');
       cy.getByTestId('password').type('usEr_password_123!');
@@ -43,7 +47,9 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should show invalid email error when signing up with invalid email', function () {
-      cy.visit('/auth/signup');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/signup');
+      });
       cy.getByTestId('fullName').type('Test User');
       cy.getByTestId('email').type('test-user-1@example.c');
       cy.getByTestId('password').type('usEr_password_123!');
@@ -57,7 +63,9 @@ describe('User Sign-up and Login', function () {
       if (!isCI) return;
 
       cy.intercept('**/organization/**/switch').as('appSwitch');
-      cy.visit('/auth/signup');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/signup');
+      });
 
       cy.loginWithGitHub();
 
@@ -85,7 +93,9 @@ describe('User Sign-up and Login', function () {
       const gitHubUserEmail = Cypress.env('GITHUB_USER_EMAIL');
 
       cy.intercept('**/organization/**/switch').as('appSwitch');
-      cy.visit('/auth/signup');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/signup');
+      });
       cy.getByTestId('fullName').type('Test User');
       cy.getByTestId('email').type(gitHubUserEmail);
       cy.getByTestId('password').type('usEr_password_123!');
@@ -118,13 +128,19 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should request a password reset flow', function () {
-      cy.visit('/auth/reset/request');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/reset/request');
+      });
       cy.getByTestId('email').type(this.session.user.email);
       cy.getByTestId('submit-btn').click();
       cy.getByTestId('success-screen-reset').should('be.visible');
+
       cy.task('passwordResetToken', this.session.user._id).then((token) => {
         cy.visit('/auth/reset/' + token);
       });
+
+      // unfortunately there seems to be a timing issue in in which inputs are disabled
+      cy.wait(500);
       cy.getByTestId('password').type('A123e3e3e3!');
       cy.getByTestId('password-repeat').focus().type('A123e3e3e3!');
 
@@ -140,18 +156,24 @@ describe('User Sign-up and Login', function () {
 
     it('should redirect to the dashboard page when a token exists in query', function () {
       cy.initializeSession({ disableLocalStorage: true }).then((session) => {
-        cy.visit('/auth/login?token=' + session.token);
+        cy.waitLoadFeatureFlags(() => {
+          cy.visit('/auth/login?token=' + session.token);
+        });
         cy.location('pathname').should('equal', '/workflows');
       });
     });
 
     it('should be redirect login with no auth', function () {
-      cy.visit('/');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/');
+      });
       cy.location('pathname').should('equal', '/auth/login');
     });
 
     it('should successfully login the user', function () {
-      cy.visit('/auth/login');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/login');
+      });
 
       cy.getByTestId('email').type('test-user-1@example.com');
       cy.getByTestId('password').type('123qwe!@#');
@@ -160,7 +182,9 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should show incorrect email or password error when authenticating with bad credentials', function () {
-      cy.visit('/auth/login');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/login');
+      });
 
       cy.getByTestId('email').type('test-user-1@example.com');
       cy.getByTestId('password').type('123456');
@@ -169,7 +193,9 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should show invalid email error when authenticating with invalid email', function () {
-      cy.visit('/auth/login');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/login');
+      });
 
       cy.getByTestId('email').type('test-user-1@example.c');
       cy.getByTestId('password').type('123456');
@@ -178,7 +204,9 @@ describe('User Sign-up and Login', function () {
     });
 
     it('should show incorrect email or password error when authenticating with non-existing email', function () {
-      cy.visit('/auth/login');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/login');
+      });
 
       cy.getByTestId('email').type('test-user-1@example.de');
       cy.getByTestId('password').type('123456');
@@ -195,7 +223,9 @@ describe('User Sign-up and Login', function () {
 
     it('should logout user when auth token is expired', function () {
       // login the user
-      cy.visit('/auth/login');
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/auth/login');
+      });
       cy.getByTestId('email').type('test-user-1@example.com');
       cy.getByTestId('password').type('123qwe!@#');
       cy.getByTestId('submit-btn').click();
@@ -208,8 +238,9 @@ describe('User Sign-up and Login', function () {
       const date = new Date(Date.now() + THIRTY_DAYS + ONE_MINUTE);
       cy.clock(date);
 
-      cy.visit('/subscribers');
-      cy.waitLoadFeatureFlags();
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit('/subscribers');
+      });
 
       // checking if token is removed from local storage
       cy.getLocalStorage('auth_token').should('be.null');

From 2ab55aba7ccbb8f7f646dfecf798e5909662bed3 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Sun, 12 May 2024 10:14:50 -0700
Subject: [PATCH 18/23] refactor: PR Feedback, small comments

---
 libs/shared-web/src/hooks/useAuthController.ts           | 2 +-
 libs/shared-web/src/providers/LaunchDarklyProvider.tsx   | 4 +++-
 .../selectShouldInitializeLaunchDarkly.tsx               | 9 ++++++++-
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/libs/shared-web/src/hooks/useAuthController.ts b/libs/shared-web/src/hooks/useAuthController.ts
index 2c006214d67..75ff5cc4125 100644
--- a/libs/shared-web/src/hooks/useAuthController.ts
+++ b/libs/shared-web/src/hooks/useAuthController.ts
@@ -121,7 +121,7 @@ export function useAuthController() {
     setTokenCallback(null);
     queryClient.clear();
     // avoid usage of react-router here to prevent needing AuthProvider to be wrapped in the BrowserRouter
-    window.location.assign('/auth/login');
+    window.location.replace('/auth/login');
     segment.reset();
   };
 
diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
index 522ab21c7b0..7cb6ddf3a21 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
@@ -1,3 +1,5 @@
+import * as Sentry from '@sentry/react';
+
 import { IOrganizationEntity } from '@novu/shared';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
@@ -66,7 +68,7 @@ export const LaunchDarklyProvider: React.FC<PropsWithChildren<ILaunchDarklyProvi
               },
         });
       } catch (err: unknown) {
-        // FIXME: what should we do here since we don't have logging?
+        Sentry.captureException(err);
       } finally {
         setIsLDReady(true);
       }
diff --git a/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx b/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
index 59d90e31a0a..8b692482c3e 100644
--- a/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
+++ b/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
@@ -15,7 +15,14 @@ export function selectShouldInitializeLaunchDarkly(userCtx: UserContext): boolea
     return true;
   }
 
-  // allow LD to load when the user is created but still in onboarding
+  /**
+   * Allow LD to load when the user is created but still in onboarding.
+   *
+   * After users provide their name, email, and password, we take them to an onboarding step where they provide details
+   * such as job title, use cases, company name, etc. When they reach this page, isLoggedIn is true, but they don't
+   * have an organizationId yet that we can use for org-based feature flags. To prevent from blocking this page
+   * from loading during this "limbo" state, we should initialize LD with the anonymous context.
+   */
   if (!selectHasUserCompletedSignUp(userCtx)) {
     return true;
   }

From 6ed5769b8ea839bac066438235ec57043e774c0c Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Sun, 12 May 2024 16:32:33 -0700
Subject: [PATCH 19/23] refactor: LD Provider in web with associated utils

---
 apps/web/src/Providers.tsx                                 | 3 ++-
 .../src/components/launch-darkly}/LaunchDarklyProvider.tsx | 7 +++----
 apps/web/src/components/launch-darkly/index.ts             | 1 +
 .../utils}/selectShouldInitializeLaunchDarkly.tsx          | 5 ++---
 .../utils}/selectShouldShowLaunchDarklyFallback.tsx        | 4 +---
 libs/shared-web/src/hooks/useFeatureFlags.ts               | 2 +-
 libs/shared-web/src/providers/index.ts                     | 1 -
 libs/shared-web/src/utils/auth-selectors/index.ts          | 2 --
 8 files changed, 10 insertions(+), 15 deletions(-)
 rename {libs/shared-web/src/providers => apps/web/src/components/launch-darkly}/LaunchDarklyProvider.tsx (91%)
 create mode 100644 apps/web/src/components/launch-darkly/index.ts
 rename {libs/shared-web/src/utils/auth-selectors => apps/web/src/components/launch-darkly/utils}/selectShouldInitializeLaunchDarkly.tsx (84%)
 rename {libs/shared-web/src/utils/auth-selectors => apps/web/src/components/launch-darkly/utils}/selectShouldShowLaunchDarklyFallback.tsx (82%)

diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index 6016e1ed9ad..faabcada630 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -1,12 +1,13 @@
 import { Loader } from '@mantine/core';
 import { colors } from '@novu/design-system';
-import { CONTEXT_PATH, LaunchDarklyProvider, SegmentProvider } from '@novu/shared-web';
+import { CONTEXT_PATH, SegmentProvider } from '@novu/shared-web';
 import * as Sentry from '@sentry/react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 import { PropsWithChildren } from 'react';
 import { HelmetProvider } from 'react-helmet-async';
 import { BrowserRouter } from 'react-router-dom';
 import { api } from './api/api.client';
+import { LaunchDarklyProvider } from './components/launch-darkly';
 import { AuthProvider } from './components/providers/AuthProvider';
 import { css } from './styled-system/css';
 
diff --git a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx b/apps/web/src/components/launch-darkly/LaunchDarklyProvider.tsx
similarity index 91%
rename from libs/shared-web/src/providers/LaunchDarklyProvider.tsx
rename to apps/web/src/components/launch-darkly/LaunchDarklyProvider.tsx
index 7cb6ddf3a21..191348a5369 100644
--- a/libs/shared-web/src/providers/LaunchDarklyProvider.tsx
+++ b/apps/web/src/components/launch-darkly/LaunchDarklyProvider.tsx
@@ -3,10 +3,9 @@ import * as Sentry from '@sentry/react';
 import { IOrganizationEntity } from '@novu/shared';
 import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
 import { PropsWithChildren, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
-import { LAUNCH_DARKLY_CLIENT_SIDE_ID } from '../config';
-import { useFeatureFlags } from '../hooks';
-import { useAuthContext } from './AuthProvider';
-import { selectShouldShowLaunchDarklyFallback, selectShouldInitializeLaunchDarkly } from '../utils/auth-selectors';
+import { useFeatureFlags, useAuthContext, LAUNCH_DARKLY_CLIENT_SIDE_ID } from '@novu/shared-web';
+import { selectShouldInitializeLaunchDarkly } from './utils/selectShouldInitializeLaunchDarkly';
+import { selectShouldShowLaunchDarklyFallback } from './utils/selectShouldShowLaunchDarklyFallback';
 
 /** A provider with children required */
 type GenericLDProvider = Awaited<ReturnType<typeof asyncWithLDProvider>>;
diff --git a/apps/web/src/components/launch-darkly/index.ts b/apps/web/src/components/launch-darkly/index.ts
new file mode 100644
index 00000000000..b1c5cfef1eb
--- /dev/null
+++ b/apps/web/src/components/launch-darkly/index.ts
@@ -0,0 +1 @@
+export * from './LaunchDarklyProvider';
diff --git a/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx b/apps/web/src/components/launch-darkly/utils/selectShouldInitializeLaunchDarkly.tsx
similarity index 84%
rename from libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
rename to apps/web/src/components/launch-darkly/utils/selectShouldInitializeLaunchDarkly.tsx
index 8b692482c3e..d8131950cfd 100644
--- a/libs/shared-web/src/utils/auth-selectors/selectShouldInitializeLaunchDarkly.tsx
+++ b/apps/web/src/components/launch-darkly/utils/selectShouldInitializeLaunchDarkly.tsx
@@ -1,6 +1,5 @@
-import { selectHasUserCompletedSignUp } from './selectHasUserCompletedSignUp';
-import { checkShouldUseLaunchDarkly } from '../checkShouldUseLaunchDarkly';
-import { UserContext } from '../../providers/AuthProvider';
+import { selectHasUserCompletedSignUp, UserContext } from '@novu/shared-web';
+import { checkShouldUseLaunchDarkly } from '@novu/shared-web';
 
 /** Determine if LaunchDarkly should be initialized based on the current auth context */
 export function selectShouldInitializeLaunchDarkly(userCtx: UserContext): boolean {
diff --git a/libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx b/apps/web/src/components/launch-darkly/utils/selectShouldShowLaunchDarklyFallback.tsx
similarity index 82%
rename from libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx
rename to apps/web/src/components/launch-darkly/utils/selectShouldShowLaunchDarklyFallback.tsx
index 96bb2f746d6..aa4e9839abe 100644
--- a/libs/shared-web/src/utils/auth-selectors/selectShouldShowLaunchDarklyFallback.tsx
+++ b/apps/web/src/components/launch-darkly/utils/selectShouldShowLaunchDarklyFallback.tsx
@@ -1,6 +1,4 @@
-import { selectHasUserCompletedSignUp } from '.';
-import { checkShouldUseLaunchDarkly } from '..';
-import { UserContext } from '../../providers/AuthProvider';
+import { selectHasUserCompletedSignUp, UserContext, checkShouldUseLaunchDarkly } from '@novu/shared-web';
 
 /** Determine if a fallback should be shown instead of the provider-wrapped application */
 export function selectShouldShowLaunchDarklyFallback(userCtx: UserContext, isLDReady: boolean): boolean {
diff --git a/libs/shared-web/src/hooks/useFeatureFlags.ts b/libs/shared-web/src/hooks/useFeatureFlags.ts
index e828e67f352..91a399480f4 100644
--- a/libs/shared-web/src/hooks/useFeatureFlags.ts
+++ b/libs/shared-web/src/hooks/useFeatureFlags.ts
@@ -1,7 +1,7 @@
 import { FeatureFlagsKeysEnum, IOrganizationEntity, prepareBooleanStringFeatureFlag } from '@novu/shared';
 import { useFlags, useLDClient } from 'launchdarkly-react-client-sdk';
 import { useEffect } from 'react';
-import { checkShouldUseLaunchDarkly } from '../utils';
+import { checkShouldUseLaunchDarkly } from '../utils/checkShouldUseLaunchDarkly';
 
 import { FEATURE_FLAGS } from '../config';
 
diff --git a/libs/shared-web/src/providers/index.ts b/libs/shared-web/src/providers/index.ts
index d844de645cf..38f270699df 100644
--- a/libs/shared-web/src/providers/index.ts
+++ b/libs/shared-web/src/providers/index.ts
@@ -1,3 +1,2 @@
 export * from './SegmentProvider';
 export * from './AuthProvider';
-export * from './LaunchDarklyProvider';
diff --git a/libs/shared-web/src/utils/auth-selectors/index.ts b/libs/shared-web/src/utils/auth-selectors/index.ts
index ca4767bd396..53240e5334f 100644
--- a/libs/shared-web/src/utils/auth-selectors/index.ts
+++ b/libs/shared-web/src/utils/auth-selectors/index.ts
@@ -1,3 +1 @@
 export * from './selectHasUserCompletedSignUp';
-export * from './selectShouldShowLaunchDarklyFallback';
-export * from './selectShouldInitializeLaunchDarkly';

From 9bf9e28d4b06cb37213e679c4d53be0c0d37e657 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 13 May 2024 14:11:10 -0700
Subject: [PATCH 20/23] fix: Fix tests, re-order providers

---
 apps/web/cypress/tests/invites.spec.ts         |  6 +++++-
 apps/web/src/Providers.tsx                     | 14 +++++++-------
 libs/shared-web/src/hooks/useAuthController.ts |  5 +++--
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/apps/web/cypress/tests/invites.spec.ts b/apps/web/cypress/tests/invites.spec.ts
index 54733a8c16c..3e84386a203 100644
--- a/apps/web/cypress/tests/invites.spec.ts
+++ b/apps/web/cypress/tests/invites.spec.ts
@@ -1,7 +1,9 @@
+import { FeatureFlagsKeysEnum } from '@novu/shared';
 import * as capitalize from 'lodash.capitalize';
 
 describe('Invites module', function () {
   beforeEach(function () {
+    cy.mockFeatureFlags({ [FeatureFlagsKeysEnum.IS_INFORMATION_ARCHITECTURE_ENABLED]: false });
     cy.task('clearDatabase');
   });
 
@@ -120,7 +122,9 @@ describe('Invites module', function () {
       cy.initializeSession().as('session');
 
       const invitationPath = `/auth/invitation/${this.token}`;
-      cy.visit(invitationPath);
+      cy.waitLoadFeatureFlags(() => {
+        cy.visit(invitationPath);
+      });
       cy.getByTestId('success-screen-reset').click();
 
       // checking if token is removed from local storage
diff --git a/apps/web/src/Providers.tsx b/apps/web/src/Providers.tsx
index faabcada630..bdc47fd51f7 100644
--- a/apps/web/src/Providers.tsx
+++ b/apps/web/src/Providers.tsx
@@ -50,13 +50,13 @@ const Providers: React.FC<PropsWithChildren<{}>> = ({ children }) => {
   return (
     <SegmentProvider>
       <QueryClientProvider client={queryClient}>
-        <AuthProvider>
-          <LaunchDarklyProvider fallbackDisplay={fallbackDisplay}>
-            <HelmetProvider>
-              <BrowserRouter basename={CONTEXT_PATH}>{children}</BrowserRouter>
-            </HelmetProvider>
-          </LaunchDarklyProvider>
-        </AuthProvider>
+        <BrowserRouter basename={CONTEXT_PATH}>
+          <AuthProvider>
+            <LaunchDarklyProvider fallbackDisplay={fallbackDisplay}>
+              <HelmetProvider>{children}</HelmetProvider>
+            </LaunchDarklyProvider>
+          </AuthProvider>
+        </BrowserRouter>
       </QueryClientProvider>
     </SegmentProvider>
   );
diff --git a/libs/shared-web/src/hooks/useAuthController.ts b/libs/shared-web/src/hooks/useAuthController.ts
index 75ff5cc4125..bb41e6384bc 100644
--- a/libs/shared-web/src/hooks/useAuthController.ts
+++ b/libs/shared-web/src/hooks/useAuthController.ts
@@ -7,6 +7,7 @@ import type { IJwtPayload, IOrganizationEntity, IUserEntity } from '@novu/shared
 
 import { useSegment } from '../providers';
 import { api } from '../api';
+import { useNavigate } from 'react-router-dom';
 
 function getUser() {
   return api.get('/v1/users/me');
@@ -40,6 +41,7 @@ export function getToken(): string {
 export function useAuthController() {
   const segment = useSegment();
   const queryClient = useQueryClient();
+  const navigate = useNavigate();
   const [token, setToken] = useState<string | null>(() => {
     const initialToken = getToken();
     applyToken(initialToken);
@@ -120,8 +122,7 @@ export function useAuthController() {
   const logout = () => {
     setTokenCallback(null);
     queryClient.clear();
-    // avoid usage of react-router here to prevent needing AuthProvider to be wrapped in the BrowserRouter
-    window.location.replace('/auth/login');
+    navigate('/auth/login');
     segment.reset();
   };
 

From be3c6b91df64cfcb6a2a38a2570687f5b0e4b1af Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 13 May 2024 14:22:06 -0700
Subject: [PATCH 21/23] refactor: Update useAuthController.ts

---
 libs/shared-web/src/hooks/useAuthController.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libs/shared-web/src/hooks/useAuthController.ts b/libs/shared-web/src/hooks/useAuthController.ts
index bb41e6384bc..a76f86c5b43 100644
--- a/libs/shared-web/src/hooks/useAuthController.ts
+++ b/libs/shared-web/src/hooks/useAuthController.ts
@@ -1,13 +1,13 @@
 import { useEffect, useCallback, useState } from 'react';
 import axios from 'axios';
 import jwtDecode from 'jwt-decode';
+import { useNavigate } from 'react-router-dom';
 import { useQuery, useQueryClient } from '@tanstack/react-query';
 import * as Sentry from '@sentry/react';
 import type { IJwtPayload, IOrganizationEntity, IUserEntity } from '@novu/shared';
 
 import { useSegment } from '../providers';
 import { api } from '../api';
-import { useNavigate } from 'react-router-dom';
 
 function getUser() {
   return api.get('/v1/users/me');

From c5118890f12cb540b8d2ad8c5e998f1332ea8c19 Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 13 May 2024 14:25:16 -0700
Subject: [PATCH 22/23] fix: Clarify comment

---
 apps/web/src/SettingsRoutes.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/apps/web/src/SettingsRoutes.tsx b/apps/web/src/SettingsRoutes.tsx
index 228fd5b3701..b469ccc6726 100644
--- a/apps/web/src/SettingsRoutes.tsx
+++ b/apps/web/src/SettingsRoutes.tsx
@@ -41,7 +41,7 @@ export const useSettingsRoutes = () => {
     );
   }
 
-  /* TODO: remove all routes above once information architecture is fully enabled */
+  /* TODO: remove all routes below once information architecture is fully enabled */
   return (
     <>
       <Route path={ROUTES.SETTINGS} element={<SettingsPageOld />}>

From f155b3d51ce45ca11a319b3c01d5fc2718f6b28f Mon Sep 17 00:00:00 2001
From: Joel Anton <joel@novu.co>
Date: Mon, 13 May 2024 17:43:39 -0700
Subject: [PATCH 23/23] refactor: Move waits

---
 apps/web/cypress/tests/auth.spec.ts | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/apps/web/cypress/tests/auth.spec.ts b/apps/web/cypress/tests/auth.spec.ts
index 829ba9b21c3..67e20fef42f 100644
--- a/apps/web/cypress/tests/auth.spec.ts
+++ b/apps/web/cypress/tests/auth.spec.ts
@@ -223,13 +223,13 @@ describe('User Sign-up and Login', function () {
 
     it('should logout user when auth token is expired', function () {
       // login the user
-      cy.waitLoadFeatureFlags(() => {
-        cy.visit('/auth/login');
-      });
+      cy.visit('/auth/login');
       cy.getByTestId('email').type('test-user-1@example.com');
       cy.getByTestId('password').type('123qwe!@#');
       cy.getByTestId('submit-btn').click();
 
+      cy.waitLoadFeatureFlags();
+
       cy.location('pathname').should('equal', '/workflows');
 
       // setting current time in future, to simulate expired token
@@ -238,9 +238,9 @@ describe('User Sign-up and Login', function () {
       const date = new Date(Date.now() + THIRTY_DAYS + ONE_MINUTE);
       cy.clock(date);
 
-      cy.waitLoadFeatureFlags(() => {
-        cy.visit('/subscribers');
-      });
+      cy.visit('/subscribers');
+
+      cy.waitLoadFeatureFlags();
 
       // checking if token is removed from local storage
       cy.getLocalStorage('auth_token').should('be.null');