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

Add Welcome message on installation #256

Merged
merged 1 commit into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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