1- import { Card , Classes , Elevation , NonIdealState , Spinner , SpinnerSize } from '@blueprintjs/core' ;
1+ import {
2+ Button ,
3+ ButtonGroup ,
4+ Card ,
5+ Classes ,
6+ Elevation ,
7+ H4 ,
8+ Icon ,
9+ NonIdealState ,
10+ Spinner ,
11+ SpinnerSize
12+ } from '@blueprintjs/core' ;
13+ import { IconNames } from '@blueprintjs/icons' ;
214import classNames from 'classnames' ;
315import React , { useEffect } from 'react' ;
416import { useTranslation } from 'react-i18next' ;
517import { useDispatch } from 'react-redux' ;
618import { useLocation , useNavigate } from 'react-router' ;
719import SessionActions from 'src/commons/application/actions/SessionActions' ;
20+ import { useSession } from 'src/commons/utils/Hooks' ;
21+ import { useTypedSelector } from 'src/commons/utils/Hooks' ;
822import { parseQuery } from 'src/commons/utils/QueryHelper' ;
923import classes from 'src/styles/Login.module.scss' ;
1024
@@ -13,10 +27,31 @@ const LoginVscodeCallback: React.FC = () => {
1327 const dispatch = useDispatch ( ) ;
1428 const location = useLocation ( ) ;
1529 const { t } = useTranslation ( 'login' ) ;
16-
30+ const { isLoggedIn } = useSession ( ) ;
31+ const { code, ticket, provider : providerId } = parseQuery ( location . search ) ;
32+ const isVscode = useTypedSelector ( state => state . vscode . isVscode ) ;
1733 const { access_token : accessToken , refresh_token : refreshToken } = parseQuery ( location . search ) ;
1834
35+ // `code` parameter from OAuth2 redirect, `ticket` from CAS redirect (CAS untested for VS Code)
36+ const authCode = code || ticket ;
37+
38+ const launchVscode = ( ) => {
39+ window . location . href = `vscode://source-academy.source-academy/sso?code=${ authCode } &provider=${ providerId } ` ;
40+ } ;
41+
1942 useEffect ( ( ) => {
43+ if ( authCode ) {
44+ if ( ! isVscode ) {
45+ launchVscode ( ) ;
46+ } else {
47+ if ( isLoggedIn ) {
48+ return ;
49+ }
50+ // Fetch JWT tokens and user info from backend when auth provider code is present
51+ dispatch ( SessionActions . fetchAuth ( authCode , providerId ) ) ;
52+ }
53+ }
54+
2055 if ( accessToken && refreshToken ) {
2156 dispatch (
2257 SessionActions . setTokens ( {
@@ -27,10 +62,9 @@ const LoginVscodeCallback: React.FC = () => {
2762 dispatch ( SessionActions . fetchUserAndCourse ( ) ) ;
2863 navigate ( '/welcome' ) ;
2964 }
30- // eslint-disable-next-line react-hooks/exhaustive-deps
31- } , [ ] ) ;
65+ } , [ isVscode ] ) ;
3266
33- return (
67+ return isVscode ? (
3468 < div className = { classNames ( classes [ 'Login' ] , Classes . DARK ) } >
3569 < Card elevation = { Elevation . FOUR } >
3670 < div >
@@ -41,6 +75,30 @@ const LoginVscodeCallback: React.FC = () => {
4175 </ div >
4276 </ Card >
4377 </ div >
78+ ) : (
79+ < div className = { classNames ( classes [ 'Login' ] , Classes . DARK ) } >
80+ < Card elevation = { Elevation . FOUR } >
81+ < div >
82+ < div className = { classes [ 'login-header' ] } >
83+ < H4 >
84+ < Icon className = { classes [ 'login-icon' ] } icon = { IconNames . LOG_IN } />
85+ Sign in with SSO
86+ </ H4 >
87+ </ div >
88+ < p >
89+ Click < b > Open link</ b > on the dialog shown by your browser.
90+ </ p >
91+ < p > If you don't see a dialog, click the button below.</ p >
92+ < div >
93+ < ButtonGroup fill = { true } >
94+ < Button onClick = { launchVscode } className = { Classes . LARGE } >
95+ Launch VS Code extension
96+ </ Button >
97+ </ ButtonGroup >
98+ </ div >
99+ </ div >
100+ </ Card >
101+ </ div >
44102 ) ;
45103} ;
46104
0 commit comments