@@ -8,7 +8,7 @@ import React, {
88} from 'react' ;
99import type { QueryObserverResult } from '@tanstack/react-query' ;
1010import { useRouter } from 'next/router' ;
11- import { useFeatureValue } from '@growthbook/growthbook-react' ;
11+ import { GrowthBookContext } from '@growthbook/growthbook-react' ;
1212import type { AnonymousUser , LoggedUser } from '../lib/user' ;
1313import { deleteAccount , logout as dispatchLogout } from '../lib/user' ;
1414import type { AccessToken , Boot , Visit } from '../lib/boot' ;
@@ -79,9 +79,12 @@ export interface AuthContextData {
7979 isGdprCovered ?: boolean ;
8080 isValidRegion ?: boolean ;
8181 isFunnel ?: boolean ;
82+ inlineLoginEnabled ?: boolean ;
8283}
8384
8485const isExtension = checkIsExtension ( ) ;
86+ const inlineLoginFeatureId = 'inline_login' ;
87+ const inlineLoginDefaultValue = false ;
8588const AuthContext = React . createContext < AuthContextData > ( null ) ;
8689export const useAuthContext = ( ) : AuthContextData => useContext ( AuthContext ) ;
8790export default AuthContext ;
@@ -159,20 +162,37 @@ export const AuthContextProvider = ({
159162 isAndroidApp,
160163} : AuthContextProviderProps ) : ReactElement => {
161164 const [ loginState , setLoginState ] = useState < LoginState | null > ( null ) ;
165+ const [ inlineLoginEnabled , setInlineLoginEnabled ] = useState < boolean > ( ) ;
166+ const inlineLoginEnabledRef = useRef < boolean > ( ) ;
162167 const endUser = user && 'providers' in user ? user : null ;
163168 const referral = user ?. referralId || user ?. referrer ;
164169 const referralOrigin = user ?. referralOrigin ;
165170 const router = useRouter ( ) ;
166171 const isFunnelRef = useRef ( ! ! router ?. pathname ?. startsWith ( webFunnelPrefix ) ) ;
172+ const growthbookContext = useContext ( GrowthBookContext ) ;
173+ const growthbook = growthbookContext ?. growthbook ;
167174 const isValidRegion = useMemo (
168175 ( ) => ! invalidPlusRegions . includes ( geo ?. region ) ,
169176 [ geo ?. region ] ,
170177 ) ;
171- // Inline-login experiment flag. Source of truth for the local default lives
172- // in `lib/featureManagement.ts` as `featureInlineLogin`. We can't import it
173- // here because `featureManagement` → `graphql/posts` → `AuthContext` would
174- // be a cycle, so the default is duplicated below; keep them in sync.
175- const isInlineLoginEnabled = useFeatureValue < boolean > ( 'inline_login' , true ) ;
178+ const evaluateInlineLogin = useCallback ( ( ) : boolean => {
179+ if ( ! isNullOrUndefined ( inlineLoginEnabledRef . current ) ) {
180+ return inlineLoginEnabledRef . current ;
181+ }
182+
183+ // Keep this id/default in sync with `featureInlineLogin`. Importing it here
184+ // would create a cycle through `graphql/posts` back to `AuthContext`.
185+ const isEnabled =
186+ growthbook ?. getFeatureValue (
187+ inlineLoginFeatureId ,
188+ inlineLoginDefaultValue ,
189+ ) === true ;
190+
191+ inlineLoginEnabledRef . current = isEnabled ;
192+ setInlineLoginEnabled ( isEnabled ) ;
193+
194+ return isEnabled ;
195+ } , [ growthbook ] ) ;
176196
177197 return (
178198 < AuthContext . Provider
@@ -186,6 +206,7 @@ export const AuthContextProvider = ({
186206 firstVisit : user ?. firstVisit ,
187207 trackingId : user ?. id ,
188208 shouldShowLogin : loginState !== null ,
209+ inlineLoginEnabled,
189210 showLogin : useCallback (
190211 ( { trigger, options = { } } ) => {
191212 const hasCompanion = ! ! isCompanionActivated ( ) ;
@@ -196,6 +217,7 @@ export const AuthContextProvider = ({
196217 }
197218
198219 const params = new URLSearchParams ( globalThis ?. location . search ) ;
220+ const shouldUseInlineLogin = ! isExtension && evaluateInlineLogin ( ) ;
199221
200222 setLoginState ( { ...options , trigger } ) ;
201223 if ( isExtension ) {
@@ -206,19 +228,20 @@ export const AuthContextProvider = ({
206228 params . set ( AFTER_AUTH_PARAM , window . location . pathname ) ;
207229 }
208230
231+ const onboardingPath = isExtension
232+ ? `${ onboardingUrl } ?${ params . toString ( ) } `
233+ : `/onboarding?${ params . toString ( ) } ` ;
234+
209235 // Inline login experiment: render the modal in-place instead of
210236 // redirecting to /onboarding. Extension keeps the redirect because
211237 // it has no host page to mount the modal on.
212- if ( isInlineLoginEnabled && ! isExtension ) {
238+ if ( shouldUseInlineLogin ) {
213239 return ;
214240 }
215241
216- const onboardingPath = `${ onboardingUrl } ?${ params . toString ( ) } ` ;
217- router . push (
218- isExtension ? onboardingPath : `/onboarding?${ params . toString ( ) } ` ,
219- ) ;
242+ router . push ( onboardingPath ) ;
220243 } ,
221- [ router , isInlineLoginEnabled ] ,
244+ [ evaluateInlineLogin , router ] ,
222245 ) ,
223246 closeLogin : useCallback ( ( ) => setLoginState ( null ) , [ ] ) ,
224247 loginState,
0 commit comments