Skip to content

Commit

Permalink
DEV2-1691 - add "Getting Started" functionality (#979)
Browse files Browse the repository at this point in the history
* - open getting-started webview after installation
- remove the hub welcome opening
- add "Getting Started guide" action to the side panel

* - open getting-started webview after installation
- remove the hub welcome opening
- add "Getting Started guide" action to the side panel

* code review
  • Loading branch information
yairco1990 authored Nov 27, 2022
1 parent b24346e commit 86201b7
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": ["${workspaceFolder}/out/test/**/*.js"]
"outFiles": ["${workspaceFolder}/out/test/**/*.js"],
"preLaunchTask": "npm: test:prepare"
}
]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@
"prettier:check": "prettier --check src/",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"test": "yarn clear-out && tsc && yarn test:copyassets && node ./out/test/runTest.js",
"test:prepare": "yarn clear-out && tsc && yarn test:copyassets",
"test": "yarn test:prepare && node ./out/test/runTest.js",
"vsce:package": "vsce package",
"vsce:publish": "vsce publish",
"ovsx:publish": "ovsx publish",
Expand Down
2 changes: 2 additions & 0 deletions src/binary/binaryFetcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import handleActiveFile from "./activeFileHandler";
import downloadAndExtractBundle from "./bundleDownloader";
import handleExistingVersion from "./existingVersionHandler";
import { onPluginInstalledEmitter } from "../../events/onPluginInstalledEmitter";

export default async function fetchBinaryPath(): Promise<string> {
const activeVersionPath = handleActiveFile();
Expand All @@ -22,6 +23,7 @@ export default async function fetchBinaryPath(): Promise<string> {
if (existingVersion) {
return existingVersion;
}
onPluginInstalledEmitter.fire();
return tryDownloadVersion();
}

Expand Down
6 changes: 6 additions & 0 deletions src/events/onPluginInstalledEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { EventEmitter } from "vscode";

const onPluginInstalledEmitter = new EventEmitter<void>();

// eslint-disable-next-line import/prefer-default-export
export { onPluginInstalledEmitter };
11 changes: 5 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ import notifyWorkspaceChanged from "./binary/requests/notifyWorkspaceChanged";
import registerTabnineTodayWidgetWebview from "./tabnineTodayWidget/tabnineTodayWidgetWebview";
import registerCodeReview from "./codeReview/codeReview";
import installAutocomplete from "./autocompleteInstaller";
import pollProcessState from "./binary/pollProcessState";
import handleOpenWelcomeInHub from "./openWelcomeInHub";
import handlePluginInstalled from "./handlePluginInstalled";

export async function activate(
context: vscode.ExtensionContext
Expand All @@ -60,7 +59,6 @@ export async function activate(
void initStartup(context);
handleSelection(context);
handleUninstall(() => uponUninstall(context));

registerCodeReview();

registerStatusBar(context);
Expand All @@ -69,6 +67,10 @@ export async function activate(
// before considering TabNine ready to operate.
void backgroundInit(context);

if (context.extensionMode !== vscode.ExtensionMode.Test) {
handlePluginInstalled(context);
}

return Promise.resolve();
}

Expand Down Expand Up @@ -132,9 +134,6 @@ async function backgroundInit(context: vscode.ExtensionContext) {
setDefaultStatus();
void registerCommands(context);
pollDownloadProgress();
pollProcessState(() => {
void handleOpenWelcomeInHub(context);
});
void executeStartupActions();
registerNotificationsWebview(context);
registerTabnineTodayWidgetWebview(context);
Expand Down
4 changes: 4 additions & 0 deletions src/globals/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,14 @@ export const PREV_INLINE_COMMAND = "tabnine.prev-inline-suggestion";
export const TABNINE_TREE_NAVIGATION_COMMAND = "tabnine:navigation";
export const SNIPPET_COMMAND = "tabnine.snippet-suggestion";
export const TABNINE_OPEN_APP_COMMAND = "tabnine:open-app";
export const TABNINE_OPEN_GETTING_STARTED_COMMAND =
"tabnine:open-getting-started";
export const TABNINE_NOTIFICATIONS_FOCUS_COMMAND =
"tabnine-notifications.focus";

export const TABNINE_APP_URL = "https://app.tabnine.com";
export const TABNINE_SITE_URL = "https://tabnine.com";
export const TABNINE_GETTING_STARTED_FOR_VSCODE_URL = `${TABNINE_SITE_URL}/getting-started/ide?client=vscode`;

export const URI_SCHEME_FILE = "file";
export const BINARY_RESTART_EVENT = "binary-restart-event";
Expand Down
14 changes: 14 additions & 0 deletions src/handlePluginInstalled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Disposable } from "vscode";
import { onPluginInstalledEmitter } from "./events/onPluginInstalledEmitter";
import { openGettingStartedWebview } from "./webview/openGettingStartedWebview";
import { isAlreadyOpenedGettingStarted } from "./state/gettingStartedOpenedState";
import { ExtensionContext } from "./preRelease/types";

export default function handlePluginInstalled(
context: ExtensionContext
): Disposable {
return onPluginInstalledEmitter.event(() => {
if (isAlreadyOpenedGettingStarted(context)) return;
openGettingStartedWebview(context);
});
}
19 changes: 19 additions & 0 deletions src/state/gettingStartedOpenedState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ExtensionContext } from "../preRelease/types";

export const ALREADY_OPENED_GETTING_STARTED_KEY =
"already-opened-getting-started";

export async function setAlreadyOpenedGettingStarted(
context: ExtensionContext
): Promise<void> {
await context.globalState.update(ALREADY_OPENED_GETTING_STARTED_KEY, true);
}

export function isAlreadyOpenedGettingStarted(
context: ExtensionContext
): boolean {
return context.globalState.get<boolean>(
ALREADY_OPENED_GETTING_STARTED_KEY,
false
);
}
42 changes: 42 additions & 0 deletions src/test/suite/gettingStarted.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as assert from "assert";
import { afterEach } from "mocha";
import * as sinon from "sinon";
import { onPluginInstalledEmitter } from "../../events/onPluginInstalledEmitter";
import { getContext } from "./utils/preReleaseInstaller.utils";
import handlePluginInstalled from "../../handlePluginInstalled";
import { ALREADY_OPENED_GETTING_STARTED_KEY } from "../../state/gettingStartedOpenedState";
import * as gettingStartedWebview from "../../webview/openGettingStartedWebview";

suite("Getting started tests", () => {
afterEach(() => {
sinon.verifyAndRestore();
});

test("Should open the webview if the user hasn't seen it yet", () => {
const openGettingStartedWebviewStub = sinon.spy(
gettingStartedWebview,
"openGettingStartedWebview"
);
const handler = handlePluginInstalled(getContext({}));
onPluginInstalledEmitter.fire();
handler.dispose();

assert(openGettingStartedWebviewStub.calledOnce);
});

test("Should not open the webview if the user has already seen it", () => {
const openGettingStartedWebviewStub = sinon.spy(
gettingStartedWebview,
"openGettingStartedWebview"
);
const handler = handlePluginInstalled(
getContext({
[ALREADY_OPENED_GETTING_STARTED_KEY]: true,
})
);
onPluginInstalledEmitter.fire();
handler.dispose();

assert(openGettingStartedWebviewStub.notCalled);
});
});
2 changes: 1 addition & 1 deletion src/test/suite/utils/preReleaseInstaller.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export async function runInstallation(
);
}

function getContext(contextGetMocks: ContextGetMocks): ExtensionContext {
export function getContext(contextGetMocks: ContextGetMocks): ExtensionContext {
return {
globalState: {
get: (key: string) => contextGetMocks[key],
Expand Down
6 changes: 6 additions & 0 deletions src/treeView/TabnineTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Event, ProviderResult, TreeDataProvider, TreeItem } from "vscode";
import {
TABNINE_OPEN_APP_COMMAND,
TABNINE_OPEN_GETTING_STARTED_COMMAND,
TABNINE_TREE_NAVIGATION_COMMAND,
} from "../globals/consts";
import TabnineTreeItem from "./TabnineTreeItem";
Expand All @@ -28,6 +29,11 @@ export default class TabnineTreeProvider
command: TABNINE_TREE_NAVIGATION_COMMAND,
arguments: [],
}),
new TabnineTreeItem("Getting Started guide", {
title: "Getting Started guide",
command: TABNINE_OPEN_GETTING_STARTED_COMMAND,
arguments: [],
}),
];
}
}
8 changes: 8 additions & 0 deletions src/treeView/registerTreeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { Capability, isCapabilityEnabled } from "../capabilities/capabilities";
import {
TABNINE_APP_URL,
TABNINE_OPEN_APP_COMMAND,
TABNINE_OPEN_GETTING_STARTED_COMMAND,
TABNINE_TREE_NAVIGATION_COMMAND,
} from "../globals/consts";
import { openGettingStartedWebview } from "../webview/openGettingStartedWebview";
import navigate from "./navigate";
import TabnineTreeProvider from "./TabnineTreeProvider";

Expand All @@ -33,6 +35,12 @@ export default function registerTreeView(context: ExtensionContext): void {
void fireEvent({
name: "tabnine-app-opened-from-sidebar",
});
}),
commands.registerCommand(TABNINE_OPEN_GETTING_STARTED_COMMAND, () => {
openGettingStartedWebview(context);
void fireEvent({
name: "getting-started-opened-from-sidebar",
});
})
);
void commands.executeCommand(
Expand Down
35 changes: 35 additions & 0 deletions src/webview/openGettingStartedWebview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ViewColumn, WebviewPanel, window } from "vscode";
import { fireEvent } from "../binary/requests/requests";
import { createIFrameTemplate } from "./webviewTemplates";
import { setAlreadyOpenedGettingStarted } from "../state/gettingStartedOpenedState";
import { TABNINE_GETTING_STARTED_FOR_VSCODE_URL } from "../globals/consts";
import { ExtensionContext } from "../preRelease/types";

let panel: WebviewPanel | undefined;

// eslint-disable-next-line import/prefer-default-export
export function openGettingStartedWebview(context: ExtensionContext): void {
if (!panel) {
panel = window.createWebviewPanel(
"tabnine.getting-started",
"Tabnine - Getting Started",
{ viewColumn: ViewColumn.Beside, preserveFocus: false },
{
retainContextWhenHidden: true,
enableFindWidget: true,
enableCommandUris: true,
enableScripts: true,
}
);
panel.onDidDispose(() => {
panel = undefined;
void fireEvent({
name: "getting-started-closed",
});
});
panel.webview.html = createIFrameTemplate(
TABNINE_GETTING_STARTED_FOR_VSCODE_URL
);
void setAlreadyOpenedGettingStarted(context);
}
}
13 changes: 13 additions & 0 deletions src/webview/webviewTemplates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// eslint-disable-next-line import/prefer-default-export
export const createIFrameTemplate = (url: string): string => `
<!DOCTYPE html>
<html lang="en" style="margin: 0; padding: 0; min-width: 100%; min-height: 100%">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tabnine Hub</title>
</head>
<body style="margin: 0; padding: 0; min-width: 100%; min-height: 100%">
<iframe src="${url}" id="config" frameborder="0" style="display: block; margin: 0; padding: 0; position: absolute; min-width: 100%; min-height: 100%; visibility: visible;"></iframe>
</body>
</html>`;

0 comments on commit 86201b7

Please sign in to comment.