From d8705eaf00e54c7f1acce2e9df09286294be9a6a Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Wed, 24 Aug 2022 16:58:56 +0200 Subject: [PATCH] feat(vscode): support insiders login (#6836) * feat(vscode): support insiders login * feat(vscode): display insiders notifications * chore: add myself to contributors * chore: update "protocol-handlers" to 0.1.3 * fix(vscode): add trailing slash to the vscode url * fix(vscode): use a button and onClick handler --- .all-contributorsrc | 9 ++ packages/app/package.json | 3 +- .../src/app/pages/VSCodeAuth/Prompt/index.tsx | 83 ++++++++++++++++--- yarn.lock | 7 +- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 3bef2b7a7b2..46df60a9716 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1672,6 +1672,15 @@ "contributions": [ "doc" ] + }, + { + "login": "kettanaito", + "name": "Artem", + "avatar_url": "https://github.com/kettanaito.png", + "profile": "https://github.com/kettanaito", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/packages/app/package.json b/packages/app/package.json index bd58f67839c..052af67a5be 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -182,6 +182,7 @@ "posthtml-parser": "^0.4.1", "posthtml-render": "^1.1.0", "prism-react-renderer": "^1.0.2", + "protocol-handlers": "^0.1.3", "punycode": "^2.1.1", "qrcode.react": "^0.8.0", "qs": "^6.5.0", @@ -193,8 +194,8 @@ "react-color": "^2.17.3", "react-content-loader": "^4.2.2", "react-day-picker": "^7.2.4", - "react-devtools-inline_legacy": "npm:react-devtools-inline@4.4.0", "react-devtools-inline": "^4.22.0", + "react-devtools-inline_legacy": "npm:react-devtools-inline@4.4.0", "react-dnd": "^9.4.0", "react-dnd-html5-backend": "^9.4.0", "react-dom": "^16.9.0", diff --git a/packages/app/src/app/pages/VSCodeAuth/Prompt/index.tsx b/packages/app/src/app/pages/VSCodeAuth/Prompt/index.tsx index 6f212114215..eabc47babdb 100644 --- a/packages/app/src/app/pages/VSCodeAuth/Prompt/index.tsx +++ b/packages/app/src/app/pages/VSCodeAuth/Prompt/index.tsx @@ -1,5 +1,14 @@ +import React, { + FunctionComponent, + useCallback, + useEffect, + useMemo, +} from 'react'; import { Button } from '@codesandbox/components'; -import React, { FunctionComponent, useEffect, useState } from 'react'; +import { notificationState } from '@codesandbox/common/lib/utils/notifications'; +import { NotificationStatus } from '@codesandbox/notifications'; +import { useLocation } from 'react-router-dom'; +import { openUrl, UnsupportedProtocolError } from 'protocol-handlers'; import { SubTitle } from 'app/components/SubTitle'; import { Title } from 'app/components/Title'; @@ -17,8 +26,12 @@ export const Prompt: FunctionComponent = () => { user, isLoggedIn, } = useAppState(); + const location = useLocation(); + const query = useMemo(() => new URLSearchParams(location.search), [ + location.search, + ]); + const isInsiders = query.get('insiders') === 'true'; - const [deepLink, setDeepLink] = useState(''); const actions = useActions(); useEffect(() => { if (isLoggedIn && !authToken && !isLoadingAuthToken) { @@ -26,14 +39,61 @@ export const Prompt: FunctionComponent = () => { } }, [isLoggedIn]); - useEffect(() => { - const deeplinkUrl = `vscode://CodeSandbox-io.codesandbox-projects/auth-completion?token=${authToken}`; + const vscodeUrl = useMemo(() => { + const url = new URL( + 'auth-completion', + 'vscode://CodeSandbox-io.codesandbox-projects/' + ); + url.searchParams.set('token', authToken); + + if (isInsiders) { + url.protocol = 'vscode-insiders://'; + } + + return url; + }, [authToken, isInsiders]); - if (authToken) { - setDeepLink(deeplinkUrl); - window.open(deeplinkUrl); + const openInVsCode = useCallback(() => { + if (!authToken) { + return; } - }, [authToken]); + + openUrl(vscodeUrl).catch(openVsCodeError => { + if (openVsCodeError instanceof UnsupportedProtocolError) { + notificationState.addNotification({ + status: NotificationStatus.WARNING, + message: 'Visual Studio Insiders is not installed', + actions: { + primary: { + label: 'Install', + run: () => { + window.open( + 'https://code.visualstudio.com/insiders/', + '_blank', + 'noopener,noreferrer' + ); + }, + }, + }, + }); + return; + } + + notificationState.addNotification({ + status: NotificationStatus.ERROR, + message: 'Failed to launch Visual Studio Code', + actions: { + primary: { + label: 'Try again', + run: () => openInVsCode(), + }, + }, + }); + }); + }, [vscodeUrl, authToken]); + + // Attempt to open VS Code when the page mounts. + useEffect(() => openInVsCode(), [openInVsCode]); if (error) { return ( @@ -101,12 +161,13 @@ export const Prompt: FunctionComponent = () => { diff --git a/yarn.lock b/yarn.lock index 56b103c2d86..36bf300496b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13680,7 +13680,7 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@1.0.1, esquery@^1.0.0, esquery@^1.0.1: +esquery@^1.0.0, esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== @@ -26852,6 +26852,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= +protocol-handlers@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/protocol-handlers/-/protocol-handlers-0.1.3.tgz#38a22831c4f9f06cec7035fb5f7eb7319a9f4786" + integrity sha512-m9nCcmseJMLSdfpKA8K8Glrp6IUFIDsiLoQhE66ClGwEmGE0GY0lR5XviHhXMGW8e8FvPn1IzTbeF+aOClcl1Q== + protocols@^1.1.0, protocols@^1.4.0: version "1.4.7" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32"