From 4fce8d9ce46ed87fb7e73878a30c03fc0893c150 Mon Sep 17 00:00:00 2001 From: Lee Hansel Solevilla Date: Wed, 16 Apr 2025 19:18:36 +0800 Subject: [PATCH] fix: in-app redirect existing email --- .../src/features/onboarding/shared/utils.ts | 2 + .../onboarding/steps/FunnelRegistration.tsx | 59 +++++++++++++++---- packages/webapp/pages/callback.tsx | 24 ++++++++ 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/packages/shared/src/features/onboarding/shared/utils.ts b/packages/shared/src/features/onboarding/shared/utils.ts index 9ae73e3e537..464775ac65e 100644 --- a/packages/shared/src/features/onboarding/shared/utils.ts +++ b/packages/shared/src/features/onboarding/shared/utils.ts @@ -30,3 +30,5 @@ export function shouldRedirectAuth(): boolean { return brokenWebviewPatterns.some((pattern) => pattern.test(ua)); } + +export const AUTH_REDIRECT_KEY = 'auth_redirect'; diff --git a/packages/shared/src/features/onboarding/steps/FunnelRegistration.tsx b/packages/shared/src/features/onboarding/steps/FunnelRegistration.tsx index 4109eba8b2e..76041800d92 100644 --- a/packages/shared/src/features/onboarding/steps/FunnelRegistration.tsx +++ b/packages/shared/src/features/onboarding/steps/FunnelRegistration.tsx @@ -1,5 +1,5 @@ import type { ReactElement } from 'react'; -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { useRouter } from 'next/router'; import classNames from 'classnames'; import { @@ -29,7 +29,11 @@ import { broadcastChannel } from '../../../lib/constants'; import Logo, { LogoPosition } from '../../../components/Logo'; import type { LoggedUser } from '../../../lib/user'; import { FunnelStepTransitionType } from '../types/funnel'; -import { sanitizeMessage, shouldRedirectAuth } from '../shared'; +import { + AUTH_REDIRECT_KEY, + sanitizeMessage, + shouldRedirectAuth, +} from '../shared'; import type { FunnelStepSignup } from '../types/funnel'; import { useConsentCookie } from '../../../hooks/useCookieConsent'; import { GdprConsentKey } from '../../../hooks/useCookieBanner'; @@ -50,15 +54,9 @@ const useRegistrationListeners = ( const { logEvent } = useLogContext(); const router = useRouter(); - const onProviderMessage = async (e: MessageEvent) => { - const isEventSupported = supportedEvents.includes(e.data?.eventKey); - - if (!isEventSupported) { - return undefined; - } - - if (e.data?.flow) { - const connected = await getKratosFlow(AuthFlow.Registration, e.data.flow); + const handleExistingFlow = useCallback( + async (flow: string) => { + const connected = await getKratosFlow(AuthFlow.Registration, flow); logEvent({ event_name: AuthEventNames.RegistrationError, @@ -78,6 +76,19 @@ const useRegistrationListeners = ( } return displayToast(`${labels.auth.error.generic} Code: ${code}`); + }, + [displayToast, logEvent], + ); + + const onProviderMessage = async (e: MessageEvent) => { + const isEventSupported = supportedEvents.includes(e.data?.eventKey); + + if (!isEventSupported) { + return undefined; + } + + if (e.data?.flow) { + return handleExistingFlow(e.data.flow); } const bootResponse = await refetchBoot(); @@ -100,6 +111,23 @@ const useRegistrationListeners = ( useEventListener(broadcastChannel, 'message', onProviderMessage); useEventListener(globalThis, 'message', onProviderMessage); + + useEffect(() => { + if (!router?.isReady || !router?.query?.flow) { + return; + } + + const flowFn = async () => { + await handleExistingFlow(router.query.flow as string); + const url = new URL(window.location.href); + const fullPath = url.origin + url.pathname; + const { flow, ...rest } = router.query; + const params = new URLSearchParams(rest as Record); + router.replace(`${fullPath}?${params}`); + }; + + flowFn(); + }, [handleExistingFlow, router]); }; function InnerFunnelRegistration({ @@ -123,6 +151,7 @@ function InnerFunnelRegistration({ }, onRedirect: (redirect) => { if (shouldRedirect) { + window.sessionStorage.setItem(AUTH_REDIRECT_KEY, window.location.href); window.location.href = redirect; } else { windowPopup.current.location.href = redirect; @@ -142,6 +171,14 @@ function InnerFunnelRegistration({ const sanitizedHeading = useMemo(() => sanitizeMessage(headline), [headline]); + useEffect(() => { + if (typeof window === 'undefined' || !shouldRedirect) { + return; + } + + window.sessionStorage.removeItem(AUTH_REDIRECT_KEY); + }, [shouldRedirect]); + return (
diff --git a/packages/webapp/pages/callback.tsx b/packages/webapp/pages/callback.tsx index 27b0134b818..3aca9fdc8b8 100644 --- a/packages/webapp/pages/callback.tsx +++ b/packages/webapp/pages/callback.tsx @@ -6,6 +6,10 @@ import { AuthEvent } from '@dailydotdev/shared/src/lib/kratos'; import type { ReactElement } from 'react'; import { useContext, useEffect } from 'react'; import LogContext from '@dailydotdev/shared/src/contexts/LogContext'; +import { + AUTH_REDIRECT_KEY, + shouldRedirectAuth, +} from '@dailydotdev/shared/src/features/onboarding/shared'; const checkShouldSendBroadcast = () => { const ua = navigator.userAgent; @@ -17,6 +21,21 @@ const checkShouldSendBroadcast = () => { return conditions.some(Boolean); }; +const handleRedirectAuth = (params: URLSearchParams) => { + const href = window.sessionStorage.getItem(AUTH_REDIRECT_KEY); + + if (href) { + const [redirect, hrefParams] = href.split('?'); + const redirectParams = new URLSearchParams(hrefParams); + + Object.entries(redirectParams).forEach(([key, value]) => + params.set(key, value), + ); + + window.location.replace(`${redirect}?${params}`); + } +}; + function CallbackPage(): ReactElement { const { logEvent } = useContext(LogContext); useEffect(() => { @@ -36,6 +55,11 @@ function CallbackPage(): ReactElement { return; } + if (shouldRedirectAuth()) { + handleRedirectAuth(urlSearchParams); + return; + } + if (checkShouldSendBroadcast()) { broadcastMessage({ ...params, eventKey }); } else {