From 15841e5a7ec38c5e3f7f0366c303326c1ff1dd8d Mon Sep 17 00:00:00 2001 From: Beatrix <68532117+abeatrix@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:55:19 -0700 Subject: [PATCH] cody: fix sign in issues with App (#54106) --- client/cody/CHANGELOG.md | 3 ++ client/cody/src/chat/ChatViewProvider.ts | 43 ++++++++++-------- client/cody/src/chat/protocol.ts | 6 +-- client/cody/src/main.ts | 10 +++-- client/cody/src/services/AuthProvider.ts | 38 +++++++++++++--- client/cody/src/services/LocalAppDetector.ts | 8 +--- client/cody/src/services/LocalAppFsPaths.ts | 4 -- client/cody/test/e2e/auth.test.ts | 6 +-- client/cody/test/e2e/common.ts | 2 +- client/cody/webviews/App.story.tsx | 2 +- client/cody/webviews/App.tsx | 31 ++++++------- client/cody/webviews/ConnectApp.tsx | 9 ++-- client/cody/webviews/Error.tsx | 5 ++- client/cody/webviews/LoadingPage.module.css | 44 ++++++++++++++++++ client/cody/webviews/LoadingPage.tsx | 16 ++++--- client/cody/webviews/Login.story.tsx | 47 +++++++++++++++++++- client/cody/webviews/Login.tsx | 40 ++--------------- client/cody/webviews/Settings.tsx | 2 +- 18 files changed, 202 insertions(+), 114 deletions(-) create mode 100644 client/cody/webviews/LoadingPage.module.css diff --git a/client/cody/CHANGELOG.md b/client/cody/CHANGELOG.md index d2c75ed7996c..cea4df29196d 100644 --- a/client/cody/CHANGELOG.md +++ b/client/cody/CHANGELOG.md @@ -12,6 +12,7 @@ Starting from `0.2.0`, Cody is using `major.EVEN_NUMBER.patch` for release versi - Support switching between multiple instances with `Switch Account`. [pull/53434](https://github.com/sourcegraph/sourcegraph/pull/53434) - Automate sign-in flow with Cody App. [pull/53908](https://github.com/sourcegraph/sourcegraph/pull/53908) - Add a warning message to recipes when the selection gets truncated. [pull/54025](https://github.com/sourcegraph/sourcegraph/pull/54025) +- Start up loading screen. [pull/54106](https://github.com/sourcegraph/sourcegraph/pull/54106) ### Fixed @@ -22,6 +23,8 @@ Starting from `0.2.0`, Cody is using `major.EVEN_NUMBER.patch` for release versi - Autocomplete: Fix network issues when using remote VS Code setups. [pull/53956](https://github.com/sourcegraph/sourcegraph/pull/53956) - Autocomplete: Fix an issue where the loading indicator would not reset when a network error ocurred. [pull/53956](https://github.com/sourcegraph/sourcegraph/pull/53956) - Autocomplete: Improve local context performance. [pull/54124](https://github.com/sourcegraph/sourcegraph/pull/54124) +- Codebase index status does not get updated on workspace change. [pull/54106](https://github.com/sourcegraph/sourcegraph/pull/54106) +- Button for connect to App after user is signed out. [pull/54106](https://github.com/sourcegraph/sourcegraph/pull/54106) - Fixes an issue with link formatting. [pull/54200](https://github.com/sourcegraph/sourcegraph/pull/54200) ### Changed diff --git a/client/cody/src/chat/ChatViewProvider.ts b/client/cody/src/chat/ChatViewProvider.ts index 1dd50e2c402e..fa26e66d6566 100644 --- a/client/cody/src/chat/ChatViewProvider.ts +++ b/client/cody/src/chat/ChatViewProvider.ts @@ -40,7 +40,6 @@ import { TestSupport } from '../test-support' import { fastFilesExist } from './fastFileFinder' import { - AuthStatus, ConfigurationSubsetForWebview, DOTCOM_URL, ExtensionMessage, @@ -117,6 +116,9 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp this.disposables.push( vscode.window.onDidChangeActiveTextEditor(async () => { await this.updateCodebaseContext() + }), + vscode.workspace.onDidChangeWorkspaceFolders(async () => { + await this.updateCodebaseContext() }) ) @@ -221,10 +223,10 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp debug('ChatViewProvider:onDidReceiveMessage:initialized', '') this.loadChatHistory() this.publishContextStatus() - this.publishConfig() this.sendTranscript() this.sendChatHistory() await this.loadRecentChat() + this.publishConfig() break case 'submit': await this.onHumanMessageSubmitted(message.text, message.submitType) @@ -242,6 +244,10 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp await this.executeRecipe(message.recipe) break case 'auth': + if (message.type === 'app' && message.endpoint) { + await this.authProvider.appAuth(message.endpoint) + break + } // cody.auth.signin or cody.auth.signout await vscode.commands.executeCommand(`cody.auth.${message.type}`) break @@ -623,16 +629,28 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp * Save, verify, and sync authStatus between extension host and webview * activate extension when user has valid login */ - public async syncAuthStatus(authStatus: AuthStatus): Promise { - if (authStatus.isLoggedIn) { - void this.publishConfig() - } + public async syncAuthStatus(): Promise { + const authStatus = this.authProvider.getAuthStatus() await vscode.commands.executeCommand('setContext', 'cody.activated', authStatus.isLoggedIn) this.setWebviewView(authStatus.isLoggedIn ? 'chat' : 'login') await this.webview?.postMessage({ type: 'login', authStatus }) this.sendEvent('auth', authStatus.isLoggedIn ? 'successfully' : 'failed') } + /** + * Display app state in webview view that is used during Signin flow + */ + private async sendLocalAppState(token: string | null): Promise { + // Notify webview that app is installed + debug('ChatViewProvider:sendLocalAppState', 'isInstalled') + void this.webview?.postMessage({ type: 'app-state', isInstalled: true }) + // Log user in if token is present and user is not logged in + if (token) { + debug('ChatViewProvider:sendLocalAppState', 'auth') + await this.authProvider.auth(LOCAL_APP_URL.href, token, this.config.customHeaders) + } + } + /** * Delete history from current chat history and local storage */ @@ -774,19 +792,6 @@ export class ChatViewProvider implements vscode.WebviewViewProvider, vscode.Disp } } - /** - * Display app state in webview view that is used during Signin flow - */ - private async sendLocalAppState(token: string | null): Promise { - // Notify webview that app is installed - debug('ChatViewProvider:sendLocalAppState', 'isInstalled') - void this.webview?.postMessage({ type: 'app-state', isInstalled: true }) - // Log user in if token is present and user is not logged in - if (token) { - debug('ChatViewProvider:sendLocalAppState', 'auth') - await this.authProvider.auth(LOCAL_APP_URL.href, token, this.config.customHeaders) - } - } /** * Display error message in webview view as banner in chat view * It does not display error message as assistant response diff --git a/client/cody/src/chat/protocol.ts b/client/cody/src/chat/protocol.ts index 1d557f15a325..5c4dfb64b581 100644 --- a/client/cody/src/chat/protocol.ts +++ b/client/cody/src/chat/protocol.ts @@ -21,7 +21,7 @@ export type WebviewMessage = | { command: 'openFile'; filePath: string } | { command: 'edit'; text: string } | { command: 'insert'; text: string } - | { command: 'auth'; type: 'signin' | 'signout' | 'support' } + | { command: 'auth'; type: 'signin' | 'signout' | 'support' | 'app'; endpoint?: string } | { command: 'abort' } /** @@ -85,7 +85,7 @@ export interface AuthStatus { } export const defaultAuthStatus = { - endpoint: DOTCOM_URL.href, + endpoint: '', isLoggedIn: false, showInvalidAccessTokenError: false, authenticated: false, @@ -122,8 +122,6 @@ export interface LocalEnv { hasAppJson: boolean isAppInstalled: boolean isAppRunning: boolean - // TODO: remove this once the experimental period for connect app is over - isAppConnectEnabled: boolean } export function isLoggedIn(authStatus: AuthStatus): boolean { diff --git a/client/cody/src/main.ts b/client/cody/src/main.ts index 42d6cb01ca7c..f63866eed613 100644 --- a/client/cody/src/main.ts +++ b/client/cody/src/main.ts @@ -6,7 +6,7 @@ import { Configuration, ConfigurationWithAccessToken } from '@sourcegraph/cody-s import { SourcegraphNodeCompletionsClient } from '@sourcegraph/cody-shared/src/sourcegraph-api/completions/nodeClient' import { ChatViewProvider } from './chat/ChatViewProvider' -import { AuthStatus, CODY_FEEDBACK_URL } from './chat/protocol' +import { CODY_FEEDBACK_URL } from './chat/protocol' import { CodyCompletionItemProvider } from './completions' import { CompletionsDocumentProvider } from './completions/docprovider' import { History } from './completions/history' @@ -105,7 +105,6 @@ const register = async ( // Could we use the `initialConfig` instead? const workspaceConfig = vscode.workspace.getConfiguration() const config = getConfiguration(workspaceConfig) - const authProvider = new AuthProvider(initialConfig, secretStorage, localStorage) const { intentDetector, @@ -116,6 +115,8 @@ const register = async ( onConfigurationChange: externalServicesOnDidConfigurationChange, } = await configureExternalServices(initialConfig, rgPath, editor) + const authProvider = new AuthProvider(initialConfig, secretStorage, localStorage) + // Create chat webview const chatProvider = new ChatViewProvider( context.extensionPath, @@ -187,7 +188,7 @@ const register = async ( } }), // Auth - vscode.commands.registerCommand('cody.auth.sync', (state: AuthStatus) => chatProvider.syncAuthStatus(state)), + vscode.commands.registerCommand('cody.auth.sync', () => chatProvider.syncAuthStatus()), vscode.commands.registerCommand('cody.auth.signin', () => authProvider.signinMenu()), vscode.commands.registerCommand('cody.auth.signout', () => authProvider.signoutMenu()), vscode.commands.registerCommand('cody.auth.support', () => showFeedbackSupportQuickPick()), @@ -268,6 +269,9 @@ const register = async ( }) ) + // Make sure all commands are registered before initializing the authProvider which sends info to webview. + await authProvider.init() + let completionsProvider: vscode.Disposable | null = null if (initialConfig.autocomplete) { completionsProvider = createCompletionsProvider( diff --git a/client/cody/src/services/AuthProvider.ts b/client/cody/src/services/AuthProvider.ts index 7ad9e5d99ddd..094f2908c54e 100644 --- a/client/cody/src/services/AuthProvider.ts +++ b/client/cody/src/services/AuthProvider.ts @@ -36,12 +36,12 @@ export class AuthProvider { private localStorage: LocalStorage ) { this.loadEndpointHistory() - this.init(localStorage).catch(() => null) } // Sign into the last endpoint the user was signed into if any - private async init(localStorage: LocalStorage): Promise { - const lastEndpoint = localStorage?.getEndpoint() + public async init(): Promise { + const lastEndpoint = this.localStorage?.getEndpoint() + this.authStatus.endpoint = lastEndpoint if (!lastEndpoint) { return } @@ -50,12 +50,12 @@ export class AuthProvider { if (!token) { return } - await this.auth(lastEndpoint, token || null) + await this.auth(lastEndpoint, token) debug('AuthProvider:init:tokenFound', lastEndpoint) } // Display quickpick to select endpoint to sign in to - public async signinMenu(type?: 'enterprise' | 'dotcom' | 'token', uri?: string): Promise { + public async signinMenu(type?: 'enterprise' | 'dotcom' | 'token' | 'app', uri?: string): Promise { const mode = this.authStatus.isLoggedIn ? 'switch' : 'signin' debug('AuthProvider:signinMenu', mode) logEvent('CodyVSCodeExtension:login:clicked') @@ -86,9 +86,19 @@ export class AuthProvider { await this.auth(input.endpoint, input.token) break } + case 'app': { + if (uri) { + await this.appAuth(uri) + } + break + } default: { // Auto log user if token for the selected instance was found in secret const selectedEndpoint = item.uri + if (isLocalApp(selectedEndpoint)) { + await this.appAuth(selectedEndpoint) + return + } const token = await this.secretStorage.get(selectedEndpoint) const authState = await this.auth(selectedEndpoint, token || null) if (!authState) { @@ -108,6 +118,20 @@ export class AuthProvider { } } + public async appAuth(uri?: string): Promise { + const token = await this.secretStorage.get('SOURCEGRAPH_CODY_APP') + if (token) { + const authStatus = await this.auth(LOCAL_APP_URL.href, token) + const isLoggedIn = authStatus?.isLoggedIn + if (isLoggedIn) { + return + } + } + if (uri) { + await vscode.env.openExternal(vscode.Uri.parse(uri)) + } + } + // Display quickpick to select endpoint to sign out of public async signoutMenu(): Promise { logEvent('CodyVSCodeExtension:logout:clicked') @@ -248,10 +272,12 @@ export class AuthProvider { return } await this.localStorage.saveEndpoint(endpoint) - await updateConfiguration('serverEndpoint', endpoint) if (token) { await this.secretStorage.storeToken(endpoint, token) } + if (token && isLocalApp(endpoint)) { + await this.secretStorage.storeToken('SOURCEGRAPH_CODY_APP', token) + } this.loadEndpointHistory() debug('AuthProvider:storeAuthInfo:stored', endpoint || '') } diff --git a/client/cody/src/services/LocalAppDetector.ts b/client/cody/src/services/LocalAppDetector.ts index b4981a4a8b71..7144288abba2 100644 --- a/client/cody/src/services/LocalAppDetector.ts +++ b/client/cody/src/services/LocalAppDetector.ts @@ -30,9 +30,6 @@ export class LocalAppDetector implements vscode.Disposable { this.localAppMarkers = LOCAL_APP_LOCATIONS[this.localEnv.os] // Only Mac is supported for now this.isSupported = this.localEnv.os === 'darwin' && this.localEnv.homeDir !== undefined - const codyConfiguration = vscode.workspace.getConfiguration('cody') - // TODO: remove this once the experimental period for connect app is over - this.localEnv.isAppConnectEnabled = codyConfiguration.get('experimental.app.connect') ?? false void this.init() } @@ -43,7 +40,9 @@ export class LocalAppDetector implements vscode.Disposable { await this.fetchServer() return this.localEnv } + private async init(): Promise { + this.dispose() debug('LocalAppDetector:init:', 'initializing') const homeDir = this.localEnv.homeDir // if conditions are not met, this will be a noop @@ -185,7 +184,4 @@ const envInit = { isAppInstalled: false, isAppRunning: false, hasAppJson: false, - - // TODO: remove this once the experimental period for connect app is over - isAppConnectEnabled: false, } diff --git a/client/cody/src/services/LocalAppFsPaths.ts b/client/cody/src/services/LocalAppFsPaths.ts index 44a6e9ea4fda..4adb35daac67 100644 --- a/client/cody/src/services/LocalAppFsPaths.ts +++ b/client/cody/src/services/LocalAppFsPaths.ts @@ -1,9 +1,5 @@ export const LOCAL_APP_LOCATIONS: LocalAppPaths = { darwin: [ - { - dir: '/Applications/', - file: 'Sourcegraph.app', - }, { dir: '/Applications/', file: 'Cody.app', diff --git a/client/cody/test/e2e/auth.test.ts b/client/cody/test/e2e/auth.test.ts index f4b6c92411cc..911d234cc926 100644 --- a/client/cody/test/e2e/auth.test.ts +++ b/client/cody/test/e2e/auth.test.ts @@ -6,7 +6,7 @@ import { test } from './helpers' test('requires a valid auth token and allows logouts', async ({ page, sidebar }) => { await expect(sidebar.getByText('Invalid credentials')).not.toBeVisible() - await sidebar.getByRole('button', { name: 'Continue with Access Token' }).click() + await sidebar.getByRole('button', { name: 'Other Sign In Options…' }).click() await page.getByRole('option', { name: 'Sign in with URL and Access Token' }).click() await page.getByRole('combobox', { name: 'input' }).fill(SERVER_URL) await page.getByRole('combobox', { name: 'input' }).press('Enter') @@ -15,7 +15,7 @@ test('requires a valid auth token and allows logouts', async ({ page, sidebar }) await expect(sidebar.getByText('Invalid credentials')).toBeVisible() - await sidebar.getByRole('button', { name: 'Continue with Access Token' }).click() + await sidebar.getByRole('button', { name: 'Other Sign In Options…' }).click() await page.getByRole('option', { name: 'Sign in with URL and Access Token' }).click() await page.getByRole('combobox', { name: 'input' }).fill(SERVER_URL) await page.getByRole('combobox', { name: 'input' }).press('Enter') @@ -35,6 +35,6 @@ test('requires a valid auth token and allows logouts', async ({ page, sidebar }) await sidebar.getByRole('button', { name: 'Sign Out' }).click() await page.getByRole('combobox', { name: 'input' }).press('Enter') - await expect(sidebar.getByRole('button', { name: 'Continue with Access Token' })).toBeVisible() + await expect(sidebar.getByRole('button', { name: 'Other Sign In Options…' })).toBeVisible() await expect(sidebar.getByText('Invalid credentials')).not.toBeVisible() }) diff --git a/client/cody/test/e2e/common.ts b/client/cody/test/e2e/common.ts index 033ea5a02408..0212f222e648 100644 --- a/client/cody/test/e2e/common.ts +++ b/client/cody/test/e2e/common.ts @@ -4,7 +4,7 @@ import { SERVER_URL, VALID_TOKEN } from '../fixtures/mock-server' // Sign into Cody with valid auth from the sidebar export const sidebarSignin = async (page: Page, sidebar: Frame): Promise => { - await sidebar.getByRole('button', { name: 'Continue with Access Token' }).click() + await sidebar.getByRole('button', { name: 'Other Sign In Options…' }).click() await page.getByRole('option', { name: 'Sign in with URL and Access Token' }).click() await page.getByRole('combobox', { name: 'input' }).fill(SERVER_URL) await page.getByRole('combobox', { name: 'input' }).press('Enter') diff --git a/client/cody/webviews/App.story.tsx b/client/cody/webviews/App.story.tsx index 0ea165011d23..20c2d7626572 100644 --- a/client/cody/webviews/App.story.tsx +++ b/client/cody/webviews/App.story.tsx @@ -53,13 +53,13 @@ const dummyVSCodeAPI: VSCodeWrapper = { arch: 'x64', homeDir: '/home/user', isAppInstalled: false, - isAppConnectEnabled: false, isAppRunning: false, hasAppJson: false, extensionVersion: '0.0.0', }, authStatus: { ...defaultAuthStatus, + isLoggedIn: true, authenticated: true, hasVerifiedEmail: true, requiresVerifiedEmail: false, diff --git a/client/cody/webviews/App.tsx b/client/cody/webviews/App.tsx index a575e7d6f903..a4d6894cdb33 100644 --- a/client/cody/webviews/App.tsx +++ b/client/cody/webviews/App.tsx @@ -6,7 +6,7 @@ import { ChatContextStatus } from '@sourcegraph/cody-shared/src/chat/context' import { ChatHistory, ChatMessage } from '@sourcegraph/cody-shared/src/chat/transcript/messages' import { Configuration } from '@sourcegraph/cody-shared/src/configuration' -import { AuthStatus, LocalEnv, isLoggedIn } from '../src/chat/protocol' +import { AuthStatus, LocalEnv } from '../src/chat/protocol' import { Chat } from './Chat' import { Debug } from './Debug' @@ -23,13 +23,13 @@ export const App: React.FunctionComponent<{ vscodeAPI: VSCodeWrapper }> = ({ vsc const [config, setConfig] = useState<(Pick & LocalEnv) | null>( null ) - const [endpoint, setEndpoint] = useState() + const [endpoint, setEndpoint] = useState(null) const [debugLog, setDebugLog] = useState([]) const [view, setView] = useState() const [messageInProgress, setMessageInProgress] = useState(null) const [messageBeingEdited, setMessageBeingEdited] = useState(false) const [transcript, setTranscript] = useState([]) - const [authStatus, setAuthStatus] = useState() + const [authStatus, setAuthStatus] = useState(null) const [formInput, setFormInput] = useState('') const [inputHistory, setInputHistory] = useState([]) const [userHistory, setUserHistory] = useState(null) @@ -54,17 +54,12 @@ export const App: React.FunctionComponent<{ vscodeAPI: VSCodeWrapper }> = ({ vsc } case 'config': setConfig(message.config) - setAuthStatus(message.authStatus) - setView(isLoggedIn(message.authStatus) ? 'chat' : 'login') setIsAppInstalled(message.config.isAppInstalled) - setEndpoint(message.authStatus.endpoint || config?.serverEndpoint) + setEndpoint(message.authStatus.endpoint) + setView(message.authStatus.isLoggedIn ? 'chat' : 'login') + setAuthStatus(message.authStatus) break case 'login': - setAuthStatus(message.authStatus) - setView(isLoggedIn(message.authStatus) ? 'chat' : 'login') - if (message.authStatus.endpoint) { - setEndpoint(message.authStatus.endpoint) - } break case 'showTab': if (message.tab === 'chat') { @@ -100,21 +95,24 @@ export const App: React.FunctionComponent<{ vscodeAPI: VSCodeWrapper }> = ({ vsc vscodeAPI.postMessage({ command: 'initialized' }) } // The dependencies array is empty to execute the callback only on component mount. - }, [config?.serverEndpoint, debugLog, endpoint, errorMessages, transcript, view, vscodeAPI]) + }, [debugLog, errorMessages, view, vscodeAPI]) const onLogout = useCallback(() => { - setAuthStatus(undefined) + setConfig(null) + setEndpoint(null) + setAuthStatus(null) + setView('login') vscodeAPI.postMessage({ command: 'auth', type: 'signout' }) }, [vscodeAPI]) - if (!view) { + if (!view || !authStatus || !config) { return } return (
- {view === 'login' || !authStatus?.authenticated ? ( + {view === 'login' || !authStatus.isLoggedIn ? ( = ({ vsc appOS={config?.os} appArch={config?.arch} callbackScheme={config?.uriScheme} - isAppConnectEnabled={config?.isAppConnectEnabled} setEndpoint={setEndpoint} /> ) : ( @@ -142,7 +139,7 @@ export const App: React.FunctionComponent<{ vscodeAPI: VSCodeWrapper }> = ({ vsc /> )} {view === 'recipes' && } - {view === 'settings' && ( + {view === 'settings' && endpoint && ( )} {view === 'chat' && ( diff --git a/client/cody/webviews/ConnectApp.tsx b/client/cody/webviews/ConnectApp.tsx index a5772ab34e30..0f469c1c1aa7 100644 --- a/client/cody/webviews/ConnectApp.tsx +++ b/client/cody/webviews/ConnectApp.tsx @@ -33,10 +33,11 @@ export const ConnectApp: React.FunctionComponent = ({ callbackUri.searchParams.append('requestFrom', callbackScheme === 'vscode-insiders' ? 'CODY_INSIDERS' : 'CODY') // Use postMessage to open because it won't open otherwise due to the sourcegraph:// scheme. - const openLink = (url: string): void => + const authApp = (url: string): void => vscodeAPI.postMessage({ - command: 'links', - value: url, + command: 'auth', + type: 'app', + endpoint: url, }) return ( @@ -44,7 +45,7 @@ export const ConnectApp: React.FunctionComponent = ({ openLink(isAppInstalled ? callbackUri.href : DOWNLOAD_URL)} + onClick={() => authApp(isAppInstalled ? callbackUri.href : DOWNLOAD_URL)} > {buttonText} diff --git a/client/cody/webviews/Error.tsx b/client/cody/webviews/Error.tsx index 55a1a7af424f..472e932feda7 100644 --- a/client/cody/webviews/Error.tsx +++ b/client/cody/webviews/Error.tsx @@ -37,13 +37,14 @@ export const ErrorContainer: React.FunctionComponent<{ if (isApp.isInstalled && !isApp.isRunning) { return null } + // new users will not have an endpoint if (!endpoint) { - return

{AUTH_ERRORS.INVALID_URL}

+ return null } const isInsiderBuild = siteVersion.length > 12 || siteVersion.includes('dev') const isVersionCompatible = isInsiderBuild || siteVersion >= '5.1.0' const isVersionBeforeCody = !isVersionCompatible && siteVersion < '5.0.0' - const prefix = `Failed: ${isApp ? 'Cody App' : endpoint}` + const prefix = `Failed: ${isApp.isRunning ? 'Cody App' : endpoint}` // When doesn't have a valid token if (showInvalidAccessTokenError) { return ( diff --git a/client/cody/webviews/LoadingPage.module.css b/client/cody/webviews/LoadingPage.module.css new file mode 100644 index 000000000000..f94cc2895f9b --- /dev/null +++ b/client/cody/webviews/LoadingPage.module.css @@ -0,0 +1,44 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +.dots-holder { + display: flex; + gap: 0.2rem; +} + +.dot { + animation: 1s flash 0.5s infinite; + width: calc(var(--vscode-editor-font-size) / 2) !important; + height: calc(var(--vscode-editor-font-size) / 2) !important; + border-radius: 100%; + background-color: #b200f8; + opacity: 0.25; +} + +.dot:nth-child(2) { + animation-delay: 0.6s; + background-color: #ff5543; +} + +.dot:nth-child(3) { + animation-delay: 0.7s; + background-color: #00cbec; +} + +@keyframes flash { + from { + opacity: 1; + } + + 50% { + opacity: 1; + } + + to { + opacity: 0.25; + } +} \ No newline at end of file diff --git a/client/cody/webviews/LoadingPage.tsx b/client/cody/webviews/LoadingPage.tsx index f5a9d15c4600..4c09b6aac0ee 100644 --- a/client/cody/webviews/LoadingPage.tsx +++ b/client/cody/webviews/LoadingPage.tsx @@ -1,11 +1,17 @@ -import { VSCodeProgressRing } from '@vscode/webview-ui-toolkit/react' +import styles from './LoadingPage.module.css' export const LoadingPage: React.FunctionComponent<{}> = () => (
-
-
- -
+
+
) + +const LoadingDots: React.FunctionComponent = () => ( +
+
+
+
+
+) diff --git a/client/cody/webviews/Login.story.tsx b/client/cody/webviews/Login.story.tsx index b85a3d7a9f89..b704e2ce10d1 100644 --- a/client/cody/webviews/Login.story.tsx +++ b/client/cody/webviews/Login.story.tsx @@ -43,12 +43,25 @@ const requiresVerifiedEmailAuthStatus: AuthStatus = { endpoint, } +const supportedAppOS = 'darwin' +const supportedAppArch = 'arm64' +const unsupportedAppOS = 'other-os' +const unsupportedAppArch = 'other-arch' + export default meta export const Simple: ComponentStoryObj = { render: () => (
- {}} /> + {}} + endpoint={endpoint} + appOS={supportedAppOS} + appArch={supportedAppArch} + />
), } @@ -61,6 +74,9 @@ export const InvalidLogin: ComponentStoryObj = { isAppInstalled={false} vscodeAPI={vscodeAPI} setEndpoint={() => {}} + endpoint={endpoint} + appOS={supportedAppOS} + appArch={supportedAppArch} />
), @@ -74,6 +90,9 @@ export const UnverifiedEmailLogin: ComponentStoryObj = { isAppInstalled={false} vscodeAPI={vscodeAPI} setEndpoint={() => {}} + endpoint={endpoint} + appOS={supportedAppOS} + appArch={supportedAppArch} />
), @@ -82,7 +101,31 @@ export const UnverifiedEmailLogin: ComponentStoryObj = { export const LoginWithAppInstalled: ComponentStoryObj = { render: () => (
- {}} /> + {}} + endpoint={endpoint} + appOS={supportedAppOS} + appArch={supportedAppArch} + /> +
+ ), +} + +export const UnsupportedAppOS: ComponentStoryObj = { + render: () => ( +
+ {}} + endpoint={endpoint} + appOS={unsupportedAppOS} + appArch={unsupportedAppArch} + />
), } diff --git a/client/cody/webviews/Login.tsx b/client/cody/webviews/Login.tsx index fa1328e128fc..6b7942068feb 100644 --- a/client/cody/webviews/Login.tsx +++ b/client/cody/webviews/Login.tsx @@ -13,21 +13,20 @@ import styles from './Login.module.css' interface LoginProps { authStatus?: AuthStatus - endpoint?: string + endpoint: string | null isAppInstalled: boolean isAppRunning?: boolean vscodeAPI: VSCodeWrapper callbackScheme?: string appOS?: string appArch?: string - isAppConnectEnabled?: boolean setEndpoint: (endpoint: string) => void } const APP_DESC = { getStarted: 'Cody for VS Code requires the Cody desktop app to enable context fetching for your private code.', download: 'Download and run the Cody desktop app to configure your local code graph.', - connectApp: 'Cody App detected. All that’s left is to do is connect VS Code with Cody App.', + connectApp: 'Cody App detected. All that’s left to do is connect VS Code with Cody App.', notRunning: 'Cody for VS Code requires the Cody desktop app to enable context fetching for your private code.', comingSoon: 'We’re working on bringing Cody App to your platform. In the meantime, you can try Cody with open source repositories by signing in to Sourcegraph.com.', @@ -42,7 +41,6 @@ export const Login: React.FunctionComponent> appArch, isAppInstalled = false, isAppRunning = false, - isAppConnectEnabled = false, setEndpoint, }) => { const isOSSupported = appOS === 'darwin' && appArch === 'arm64' @@ -96,34 +94,6 @@ export const Login: React.FunctionComponent> ) - const EnterpriseSignin: React.FunctionComponent = () => ( -
-

Sourcegraph Enterprise

-

- Sign in by entering an access token created through your user settings on Sourcegraph. -

- onFooterButtonClick('signin')}> - Continue with Access Token - -
- ) - - const DotComSignin: React.FunctionComponent = () => ( -
-

Sourcegraph.com

-

- Cody for open source code is available to all users with a Sourcegraph.com account. -

- loginWithDotCom()}> - Continue with Sourcegraph.com - -
- ) - const isApp = { isInstalled: endpoint === LOCAL_APP_URL.href && isAppInstalled, isRunning: isAppRunning, @@ -133,10 +103,8 @@ export const Login: React.FunctionComponent> {authStatus && } {/* Signin Sections */}
- - - {isAppConnectEnabled && } - {isAppConnectEnabled && !isOSSupported && } + + {!isOSSupported && }
{/* Footer */}