Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Add Welcome message on installation (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Young authored Jun 16, 2017
1 parent 4480a85 commit 295ff64
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 18 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@
"default": 0,
"description": "[Not for TFVC] Specify the team project's build definition Id to monitor when your source code repository is not hosted with Microsoft. Requires both team.remoteUrl and team.teamProject."
},
"team.showWelcomeMessage": {
"type": "boolean",
"default": true,
"description": "Tracks whether the extension should display the Welcome message after the initial installation."
},
"tfvc.location": {
"type": "string",
"default": "",
Expand Down
24 changes: 23 additions & 1 deletion src/extensionmanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ export class ExtensionManager implements Disposable {
}

private async initializeExtension(): Promise<void> {
//Users could install without having a folder (workspace) open
this._settings = new Settings(); //We need settings before showing the Welcome message
await this.showWelcomeMessage(); //Ensure we show the message before hooking workspace.onDidChangeConfiguration

//Don't initialize if we don't have a workspace
if (!workspace || !workspace.rootPath) {
return;
Expand All @@ -250,7 +254,6 @@ export class ExtensionManager implements Disposable {

//If Logging is enabled, the user must have used the extension before so we can enable
//it here. This will allow us to log errors when we begin processing TFVC commands.
this._settings = new Settings();
Telemetry.Initialize(this._settings); //We don't have the serverContext just yet
Telemetry.SendEvent(TelemetryEvents.Installed); //Send event that the extension is installed (even if not used)
this.logStart(this._settings.LoggingLevel, workspace.rootPath);
Expand Down Expand Up @@ -433,6 +436,25 @@ export class ExtensionManager implements Disposable {
return false;
}

//Ensure this is async (and is awaited on) so that the extension doesn't continue until user deals with message
private async showWelcomeMessage(): Promise<void> {
if (this._settings.ShowWelcomeMessage) {
const welcomeMessage: string = `Welcome to version ${Constants.ExtensionVersion} of the Team Services extension!`;
const messageItems: IButtonMessageItem[] = [];
messageItems.push({ title : Strings.LearnMore,
url : Constants.ReadmeLearnMoreUrl,
telemetryId : TelemetryEvents.WelcomeLearnMoreClick });
messageItems.push({ title : Strings.SetupTfvcSupport,
url : Constants.TfvcLearnMoreUrl,
telemetryId : TfvcTelemetryEvents.SetupTfvcSupportClick });
messageItems.push({ title : Strings.DontShowAgain });
const chosenItem: IButtonMessageItem = await VsCodeUtils.ShowInfoMessage(welcomeMessage, ...messageItems);
if (chosenItem && chosenItem.title === Strings.DontShowAgain) {
this._settings.ShowWelcomeMessage = false;
}
}
}

//Set up the initial status bars
private initializeStatusBars(): void {
if (this.ensureMinimalInitialization()) {
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class SettingNames {
static RemoteUrl: string = SettingNames.SettingsPrefix + "remoteUrl";
static TeamProject: string = SettingNames.SettingsPrefix + "teamProject";
static BuildDefinitionId: string = SettingNames.SettingsPrefix + "buildDefinitionId";
static ShowWelcomeMessage: string = SettingNames.SettingsPrefix + "showWelcomeMessage";
}

export class TelemetryEvents {
Expand Down Expand Up @@ -109,6 +110,7 @@ export class TelemetryEvents {
static ViewWorkItem: string = TelemetryEvents.TelemetryPrefix + "viewworkitem";
static ViewWorkItems: string = TelemetryEvents.TelemetryPrefix + "viewworkitems";
static VS2015U3CSR: string = TelemetryEvents.TelemetryPrefix + "vs2015u3csr";
static WelcomeLearnMoreClick: string = TelemetryEvents.TelemetryPrefix + "welcomelearnmoreclick";
}

//Don't export this class. TfvcTelemetryEvents is the only one which should be used when sending telemetry
Expand Down Expand Up @@ -148,6 +150,7 @@ export class TfvcTelemetryEvents {
static RenameConflict: string = TfvcBaseTelemetryEvents.TelemetryPrefix + TfvcBaseTelemetryEvents.RenameConflict;
static RestrictWorkspace: string = TfvcBaseTelemetryEvents.TelemetryPrefix + TfvcBaseTelemetryEvents.RestrictWorkspace;
static StartUp: string = TfvcBaseTelemetryEvents.TelemetryPrefix + TfvcBaseTelemetryEvents.StartUp;
static SetupTfvcSupportClick: string = TfvcBaseTelemetryEvents.TelemetryPrefix + "setuptfvcsupportclick";
//Begin tooling-specific telemetry (tf.exe or CLC)
static ClcConfigured: string = TfvcTelemetryEvents.UsingClc + "-" + TfvcBaseTelemetryEvents.Configured;
static ExeConfigured: string = TfvcTelemetryEvents.UsingExe + "-" + TfvcBaseTelemetryEvents.Configured;
Expand Down Expand Up @@ -189,4 +192,10 @@ export class WitTypes {
static Bug: string = "Bug";
static Task: string = "Task";
}

export enum MessageTypes {
Error = 0,
Warn = 1,
Info = 2
}
/* tslint:enable:variable-name */
17 changes: 16 additions & 1 deletion src/helpers/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ export abstract class BaseSettings {
const value = configuration.get<T>(name, undefined);

// If user specified a value, use it
if (value) {
if (value !== undefined) {
return value;
}
return defaultValue;
}

protected writeSetting(name: string, value: any, global?: boolean): void {
const configuration = workspace.getConfiguration();
configuration.update(name, value, global);
}
}

export interface IPinnedQuery {
Expand Down Expand Up @@ -73,6 +78,7 @@ export interface ISettings {
RemoteUrl: string;
TeamProject: string;
BuildDefinitionId: number;
ShowWelcomeMessage: boolean;
}

export class Settings extends BaseSettings implements ISettings {
Expand All @@ -83,6 +89,7 @@ export class Settings extends BaseSettings implements ISettings {
private _remoteUrl: string;
private _teamProject: string;
private _buildDefinitionId: number;
private _showWelcomeMessage: boolean;

constructor() {
super();
Expand All @@ -104,6 +111,7 @@ export class Settings extends BaseSettings implements ISettings {
this._remoteUrl = this.readSetting<string>(SettingNames.RemoteUrl, undefined);
this._teamProject = this.readSetting<string>(SettingNames.TeamProject, undefined);
this._buildDefinitionId = this.readSetting<number>(SettingNames.BuildDefinitionId, 0);
this._showWelcomeMessage = this.readSetting<boolean>(SettingNames.ShowWelcomeMessage, true);
}

public get AppInsightsEnabled(): boolean {
Expand Down Expand Up @@ -133,4 +141,11 @@ export class Settings extends BaseSettings implements ISettings {
public get BuildDefinitionId(): number {
return this._buildDefinitionId;
}

public get ShowWelcomeMessage(): boolean {
return this._showWelcomeMessage;
}
public set ShowWelcomeMessage(value: boolean) {
this.writeSetting(SettingNames.ShowWelcomeMessage, value, true /*global*/);
}
}
8 changes: 5 additions & 3 deletions src/helpers/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export class Strings {
static SendFeedbackPrompt: string = "Enter your feedback here (1000 char limit)";
static NoFeedbackSent: string = "No feedback was sent.";
static ThanksForFeedback: string = "Thanks for sending feedback!";
static LearnMore: string = "Learn more...";
static LearnMoreAboutTfvc: string = "TFVC support...";
static MoreDetails: string = "More details...";
static LearnMore: string = "Learn More...";
static LearnMoreAboutTfvc: string = "TFVC Support...";
static MoreDetails: string = "More Details...";
static SetupTfvcSupport: string = "Set Up TFVC Support...";
static ShowMe: string = "Show Me!";
static VS2015Update3CSR: string = "Get Latest VS 2015 Update";
static DontShowAgain: string = "Don't Show Again";

static ChoosePullRequest: string = "Choose a pull request";
static ChooseWorkItem: string = "Choose a work item";
Expand Down
38 changes: 30 additions & 8 deletions src/helpers/vscodeutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"use strict";

import { commands, MessageItem, QuickPickItem, Range, window } from "vscode";
import { Constants } from "./constants";
import { Constants, MessageTypes } from "./constants";
import { IButtonMessageItem } from "./vscodeutils.interfaces";
import { Utils } from "./utils";
import { Telemetry } from "../services/telemetry";
Expand Down Expand Up @@ -48,15 +48,41 @@ export class VsCodeUtils {
return value;
}

public static async ShowErrorMessage(message: string, ...urlMessageItem: IButtonMessageItem[]): Promise<IButtonMessageItem> {
return this.showMessage(message, MessageTypes.Error, ...urlMessageItem);
}

public static async ShowInfoMessage(message: string, ...urlMessageItem: IButtonMessageItem[]): Promise<IButtonMessageItem> {
return this.showMessage(message, MessageTypes.Info, ...urlMessageItem);
}

public static async ShowWarningMessage(message: string): Promise<IButtonMessageItem> {
return this.showMessage(message, MessageTypes.Warn);
}

//We have a single method to display either simple messages (with no options) or messages
//that have multiple buttons that can run commands, open URLs, send telemetry, etc.
public static async ShowErrorMessage(message: string, ...urlMessageItem: IButtonMessageItem[]): Promise<void> {
private static async showMessage(message: string, type: MessageTypes, ...urlMessageItem: IButtonMessageItem[]): Promise<IButtonMessageItem> {
//The following "cast" allows us to pass our own type around (and not reference "vscode" via an import)
const messageItems: ButtonMessageItem[] = <ButtonMessageItem[]>urlMessageItem;
const messageToDisplay: string = `(${Constants.ExtensionName}) ${Utils.FormatMessage(message)}`;

//Use the typescript spread operator to pass the rest parameter to showErrorMessage
const chosenItem: IButtonMessageItem = await window.showErrorMessage(messageToDisplay, ...messageItems);
let chosenItem: IButtonMessageItem;
switch (type) {
case MessageTypes.Error:
chosenItem = await window.showErrorMessage(messageToDisplay, ...messageItems);
break;
case MessageTypes.Info:
chosenItem = await window.showInformationMessage(messageToDisplay, ...messageItems);
break;
case MessageTypes.Warn:
chosenItem = await window.showWarningMessage(messageToDisplay, ...messageItems);
break;
default:
break;
}

if (chosenItem) {
if (chosenItem.url) {
Utils.OpenUrl(chosenItem.url);
Expand All @@ -68,10 +94,6 @@ export class VsCodeUtils {
commands.executeCommand<void>(chosenItem.command);
}
}
return;
}

public static ShowWarningMessage(message: string) {
window.showWarningMessage("(" + Constants.ExtensionName + ") " + Utils.FormatMessage(message));
return chosenItem;
}
}
3 changes: 2 additions & 1 deletion test/contexts/contexthelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { ISettings } from "../../src/helpers/settings";
export class SettingsMock implements ISettings {
/* tslint:disable:variable-name */
constructor(public AppInsightsEnabled: boolean, public AppInsightsKey: string, public LoggingLevel: string,
public PollingInterval: number, public RemoteUrl: string, public TeamProject: string, public BuildDefinitionId: number) {
public PollingInterval: number, public RemoteUrl: string, public TeamProject: string, public BuildDefinitionId: number,
public ShowWelcomeMessage: boolean) {
//nothing to do
}
/* tslint:enable:variable-name */
Expand Down
8 changes: 4 additions & 4 deletions test/contexts/externalcontext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe("ExternalContext", function() {
const repoPath: string = path.join(__dirname, TEST_REPOS_FOLDER, repoName, DOT_GIT_FOLDER);
const ctx: ExternalContext = new ExternalContext(repoPath);

const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, "https://xplatalm.visualstudio.com", "L2.VSCodeExtension.RC", undefined);
const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, "https://xplatalm.visualstudio.com", "L2.VSCodeExtension.RC", undefined, true);
const initialized: Boolean = await ctx.Initialize(mock);

assert.isTrue(initialized);
Expand All @@ -83,7 +83,7 @@ describe("ExternalContext", function() {
const repoPath: string = path.join(__dirname, TEST_REPOS_FOLDER, repoName, DOT_GIT_FOLDER);
const ctx: ExternalContext = new ExternalContext(repoPath);

const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, undefined, "L2.VSCodeExtension.RC", undefined);
const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, undefined, "L2.VSCodeExtension.RC", undefined, true);
const initialized: Boolean = await ctx.Initialize(mock);

assert.isFalse(initialized);
Expand All @@ -100,7 +100,7 @@ describe("ExternalContext", function() {
const repoPath: string = path.join(__dirname, TEST_REPOS_FOLDER, repoName, DOT_GIT_FOLDER);
const ctx: ExternalContext = new ExternalContext(repoPath);

const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, "https://xplatalm.visualstudio.com", undefined, undefined);
const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, "https://xplatalm.visualstudio.com", undefined, undefined, true);
const initialized: Boolean = await ctx.Initialize(mock);

assert.isFalse(initialized);
Expand All @@ -117,7 +117,7 @@ describe("ExternalContext", function() {
const repoPath: string = path.join(__dirname, TEST_REPOS_FOLDER, repoName, DOT_GIT_FOLDER);
const ctx: ExternalContext = new ExternalContext(repoPath);

const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, undefined, undefined, undefined);
const mock: SettingsMock = new SettingsMock(false, undefined, undefined, 1, undefined, undefined, undefined, true);
const initialized: Boolean = await ctx.Initialize(mock);

assert.isFalse(initialized);
Expand Down

0 comments on commit 295ff64

Please sign in to comment.