@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
33import { useConfig } from './ConfigContext' ;
44import { SetupModal } from './SetupModal' ;
55import { startOpenRouterSetup } from '../utils/openRouterSetup' ;
6- import { startTetrateSetup } from '../utils/tetrateSetup' ;
6+ import { cancelTetrateSetup , startTetrateSetup } from '../utils/tetrateSetup' ;
77import WelcomeGooseLogo from './WelcomeGooseLogo' ;
88import { toastService } from '../toasts' ;
99import { OllamaSetup } from './OllamaSetup' ;
@@ -36,7 +36,9 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
3636 const [ userInActiveSetup , setUserInActiveSetup ] = useState ( false ) ;
3737 const [ showSwitchModelModal , setShowSwitchModelModal ] = useState ( false ) ;
3838 const [ switchModelProvider , setSwitchModelProvider ] = useState < string | null > ( null ) ;
39+ const [ isTetrateSetupInProgress , setIsTetrateSetupInProgress ] = useState ( false ) ;
3940 const onboardingTracked = useRef ( false ) ;
41+ const tetrateSetupRunId = useRef ( 0 ) ;
4042 const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
4143 const [ showScrollIndicator , setShowScrollIndicator ] = useState ( true ) ;
4244
@@ -65,15 +67,38 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
6567 show : boolean ;
6668 title : string ;
6769 message : string ;
70+ showProgress ?: boolean ;
6871 showRetry : boolean ;
72+ closeLabel ?: string ;
6973 autoClose ?: number ;
7074 } | null > ( null ) ;
7175
7276 const handleTetrateSetup = async ( ) => {
77+ if ( isTetrateSetupInProgress ) {
78+ return ;
79+ }
80+
81+ const runId = ++ tetrateSetupRunId . current ;
82+ setIsTetrateSetupInProgress ( true ) ;
83+ setTetrateSetupState ( {
84+ show : true ,
85+ title : 'Complete setup in your browser' ,
86+ message : 'After finishing sign-in in the browser, return to Goose. You can cancel setup anytime.' ,
87+ showProgress : true ,
88+ showRetry : false ,
89+ closeLabel : 'Cancel Setup' ,
90+ } ) ;
7391 trackOnboardingProviderSelected ( 'tetrate' ) ;
7492 try {
7593 const result = await startTetrateSetup ( ) ;
94+ if ( runId !== tetrateSetupRunId . current ) {
95+ return ;
96+ }
97+
98+ setIsTetrateSetupInProgress ( false ) ;
99+
76100 if ( result . success ) {
101+ setTetrateSetupState ( null ) ;
77102 setSwitchModelProvider ( 'tetrate' ) ;
78103 setShowSwitchModelModal ( true ) ;
79104 } else {
@@ -86,6 +111,11 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
86111 } ) ;
87112 }
88113 } catch ( error ) {
114+ if ( runId !== tetrateSetupRunId . current ) {
115+ return ;
116+ }
117+
118+ setIsTetrateSetupInProgress ( false ) ;
89119 console . error ( 'Tetrate setup error:' , error ) ;
90120 trackOnboardingSetupFailed ( 'tetrate' , 'unexpected_error' ) ;
91121 setTetrateSetupState ( {
@@ -97,6 +127,19 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
97127 }
98128 } ;
99129
130+ const handleCancelTetrateSetup = async ( ) => {
131+ if ( ! isTetrateSetupInProgress ) {
132+ setTetrateSetupState ( null ) ;
133+ return ;
134+ }
135+
136+ tetrateSetupRunId . current += 1 ;
137+ setIsTetrateSetupInProgress ( false ) ;
138+ setTetrateSetupState ( null ) ;
139+ trackOnboardingAbandoned ( 'tetrate_setup' ) ;
140+ await cancelTetrateSetup ( ) ;
141+ } ;
142+
100143 const handleApiKeySuccess = async ( provider : string , _model : string , apiKey : string ) => {
101144 trackOnboardingProviderSelected ( 'api_key' ) ;
102145 const keyName = `${ provider . toUpperCase ( ) } _API_KEY` ;
@@ -177,7 +220,7 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
177220 if ( setupType === 'openrouter' ) {
178221 setOpenRouterSetupState ( null ) ;
179222 } else {
180- setTetrateSetupState ( null ) ;
223+ void handleCancelTetrateSetup ( ) ;
181224 }
182225 } ;
183226
@@ -284,8 +327,12 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
284327 </ div >
285328
286329 < div
287- onClick = { handleTetrateSetup }
288- className = "w-full p-4 sm:p-6 bg-transparent border border-background-hover rounded-xl hover:border-text-muted transition-all duration-200 cursor-pointer group"
330+ onClick = { isTetrateSetupInProgress ? undefined : handleTetrateSetup }
331+ className = { `w-full p-4 sm:p-6 bg-transparent border border-background-hover rounded-xl transition-all duration-200 group ${
332+ isTetrateSetupInProgress
333+ ? 'cursor-not-allowed opacity-70'
334+ : 'cursor-pointer hover:border-text-muted'
335+ } `}
289336 >
290337 < div className = "flex items-start justify-between mb-3" >
291338 < div className = "flex items-center gap-2" >
@@ -408,9 +455,11 @@ export default function ProviderGuard({ didSelectProvider, children }: ProviderG
408455 < SetupModal
409456 title = { tetrateSetupState . title }
410457 message = { tetrateSetupState . message }
458+ showProgress = { tetrateSetupState . showProgress }
411459 showRetry = { tetrateSetupState . showRetry }
412460 onRetry = { ( ) => handleRetrySetup ( 'tetrate' ) }
413461 onClose = { ( ) => closeSetupModal ( 'tetrate' ) }
462+ closeLabel = { tetrateSetupState . closeLabel }
414463 autoClose = { tetrateSetupState . autoClose }
415464 />
416465 ) }
0 commit comments