1+ import React , {
2+ FunctionComponent ,
3+ useCallback ,
4+ useEffect ,
5+ useMemo ,
6+ } from 'react' ;
17import { Button } from '@codesandbox/components' ;
2- import React , { FunctionComponent , useEffect , useState } from 'react' ;
8+ import { notificationState } from '@codesandbox/common/lib/utils/notifications' ;
9+ import { NotificationStatus } from '@codesandbox/notifications' ;
10+ import { useLocation } from 'react-router-dom' ;
11+ import { openUrl , UnsupportedProtocolError } from 'protocol-handlers' ;
312
413import { SubTitle } from 'app/components/SubTitle' ;
514import { Title } from 'app/components/Title' ;
@@ -17,23 +26,74 @@ export const Prompt: FunctionComponent = () => {
1726 user,
1827 isLoggedIn,
1928 } = useAppState ( ) ;
29+ const location = useLocation ( ) ;
30+ const query = useMemo ( ( ) => new URLSearchParams ( location . search ) , [
31+ location . search ,
32+ ] ) ;
33+ const isInsiders = query . get ( 'insiders' ) === 'true' ;
2034
21- const [ deepLink , setDeepLink ] = useState ( '' ) ;
2235 const actions = useActions ( ) ;
2336 useEffect ( ( ) => {
2437 if ( isLoggedIn && ! authToken && ! isLoadingAuthToken ) {
2538 actions . internal . authorize ( ) ;
2639 }
2740 } , [ isLoggedIn ] ) ;
2841
29- useEffect ( ( ) => {
30- const deeplinkUrl = `vscode://CodeSandbox-io.codesandbox-projects/auth-completion?token=${ authToken } ` ;
42+ const vscodeUrl = useMemo ( ( ) => {
43+ const url = new URL (
44+ 'auth-completion' ,
45+ 'vscode://CodeSandbox-io.codesandbox-projects/'
46+ ) ;
47+ url . searchParams . set ( 'token' , authToken ) ;
48+
49+ if ( isInsiders ) {
50+ url . protocol = 'vscode-insiders://' ;
51+ }
52+
53+ return url ;
54+ } , [ authToken , isInsiders ] ) ;
3155
32- if ( authToken ) {
33- setDeepLink ( deeplinkUrl ) ;
34- window . open ( deeplinkUrl ) ;
56+ const openInVsCode = useCallback ( ( ) => {
57+ if ( ! authToken ) {
58+ return ;
3559 }
36- } , [ authToken ] ) ;
60+
61+ openUrl ( vscodeUrl ) . catch ( openVsCodeError => {
62+ if ( openVsCodeError instanceof UnsupportedProtocolError ) {
63+ notificationState . addNotification ( {
64+ status : NotificationStatus . WARNING ,
65+ message : 'Visual Studio Insiders is not installed' ,
66+ actions : {
67+ primary : {
68+ label : 'Install' ,
69+ run : ( ) => {
70+ window . open (
71+ 'https://code.visualstudio.com/insiders/' ,
72+ '_blank' ,
73+ 'noopener,noreferrer'
74+ ) ;
75+ } ,
76+ } ,
77+ } ,
78+ } ) ;
79+ return ;
80+ }
81+
82+ notificationState . addNotification ( {
83+ status : NotificationStatus . ERROR ,
84+ message : 'Failed to launch Visual Studio Code' ,
85+ actions : {
86+ primary : {
87+ label : 'Try again' ,
88+ run : ( ) => openInVsCode ( ) ,
89+ } ,
90+ } ,
91+ } ) ;
92+ } ) ;
93+ } , [ vscodeUrl , authToken ] ) ;
94+
95+ // Attempt to open VS Code when the page mounts.
96+ useEffect ( ( ) => openInVsCode ( ) , [ openInVsCode ] ) ;
3797
3898 if ( error ) {
3999 return (
@@ -101,12 +161,13 @@ export const Prompt: FunctionComponent = () => {
101161
102162 < Buttons >
103163 < Button
104- as = "a"
105164 autoWidth
106- href = { deepLink }
107165 style = { { fontSize : 16 , height : 40 , width : '100%' , marginTop : '1rem' } }
166+ onClick = { openInVsCode }
108167 >
109- Open VSCode
168+ { isInsiders
169+ ? 'Open Visual Studio Code Insiders'
170+ : 'Open Visual Studio Code' }
110171 </ Button >
111172 </ Buttons >
112173 </ Container >
0 commit comments