Repository: stateful/vscode-runme
Pull request #658 (2023-07-06 20:54:47)

Provide "Send Feedback" button in Notebook UX
Repurpose what previously was meant to be an exit survey.


Patch:
diff --git a/package.json b/package.json
index d0dbfd17..7d7b72bc 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,12 @@
         "category": "Runme",
         "title": "Prompt to participate in Feedback survey"
       },
+      {
+        "command": "runme.surveyFeedbackButton",
+        "category": "Runme",
+        "title": "Send Feedback",
+        "icon": "$(feedback)"
+      },
       {
         "command": "runme.surveyShebangComingSoon",
         "category": "Runme",
@@ -223,6 +229,10 @@
         {
           "command": "runme.runCellCategory",
           "when": "false"
+        },
+        {
+          "command": "runme.surveyFeedbackButton",
+          "when": "false"
         }
       ],
       "editor/title": [
@@ -263,6 +273,11 @@
         }
       ],
       "notebook/toolbar": [
+        {
+          "command": "runme.surveyFeedbackButton",
+          "when": "notebookType == runme",
+          "group": "navigation"
+        },
         {
           "command": "runme.runCategory",
           "when": "notebookType == runme && notebookHasCategories",
diff --git a/src/extension/extension.ts b/src/extension/extension.ts
index 69607631..202e83be 100644
--- a/src/extension/extension.ts
+++ b/src/extension/extension.ts
@@ -1,4 +1,4 @@
-import { workspace, notebooks, commands, ExtensionContext, tasks, window } from 'vscode'
+import { Disposable, workspace, notebooks, commands, ExtensionContext, tasks, window } from 'vscode'
 import { TelemetryReporter } from 'vscode-telemetry'
 
 import { Serializer } from '../types'
@@ -87,8 +87,12 @@ export class RunmeExtension {
 
     const treeViewer = new RunmeLauncherProvider(getDefaultWorkspace())
     const uriHandler = new RunmeUriHandler(context)
-    const activeUsersSurvey = new survey.SurveyActiveUserFeedback(context)
     const winCodeLensRunSurvey = new survey.SurveyWinCodeLensRun(context)
+    const surveys: Disposable[] = [
+      winCodeLensRunSurvey,
+      new survey.SurveyActiveUserFeedback(context),
+      new survey.SurveyFeedbackButton(context),
+    ]
     const stopBackgroundTaskProvider = new StopBackgroundTaskProvider()
 
     const runCLI = runCLICommand(context.extensionUri, !!grpcRunner, server, kernel)
@@ -115,8 +119,7 @@ export class RunmeExtension {
       serializer,
       server,
       treeViewer,
-      activeUsersSurvey,
-      winCodeLensRunSurvey,
+      ...surveys,
       workspace.registerNotebookSerializer(Kernel.type, serializer, {
         transientOutputs: true,
         transientCellMetadata,
diff --git a/src/extension/survey.ts b/src/extension/survey.ts
index 85699053..13d76ebe 100644
--- a/src/extension/survey.ts
+++ b/src/extension/survey.ts
@@ -38,6 +38,10 @@ abstract class Survey implements Disposable {
     this.context = context
   }
 
+  open() {
+    commands.executeCommand(this.id)
+  }
+
   dispose() {
     this.disposables.forEach((d) => d.dispose())
   }
@@ -295,10 +299,6 @@ export class SurveyShebangComingSoon extends Survey {
     commands.registerCommand(SurveyShebangComingSoon.#id, this.prompt.bind(this))
   }
 
-  open() {
-    commands.executeCommand(SurveyShebangComingSoon.#id)
-  }
-
   async prompt(): Promise<void> {
     const option = await window.showWarningMessage(
       'Not every language is executable... yet! Coming soon: Mix and match languages in Runme.',
@@ -316,3 +316,23 @@ export class SurveyShebangComingSoon extends Survey {
     )
   }
 }
+
+export class SurveyFeedbackButton extends Survey {
+  static readonly #id: string = 'runme.surveyFeedbackButton'
+  readonly #mid: string
+
+  constructor(protected context: ExtensionContext) {
+    super(context, SurveyFeedbackButton.#id)
+    this.#mid = getNamespacedMid(SurveyFeedbackButton.#id)
+
+    commands.registerCommand(SurveyFeedbackButton.#id, this.prompt.bind(this))
+  }
+
+  async prompt(): Promise<void> {
+    TelemetryReporter.sendTelemetryEvent('survey.FeedbackButton', { never: 'false' })
+    return commands.executeCommand(
+      'vscode.open',
+      Uri.parse(`https://wfoq097ak2p.typeform.com/feedback#mid=${this.#mid}`)
+    )
+  }
+}


Test patch:
diff --git a/tests/extension/extension.test.ts b/tests/extension/extension.test.ts
index 14086e83..7dad08e6 100644
--- a/tests/extension/extension.test.ts
+++ b/tests/extension/extension.test.ts
@@ -73,7 +73,7 @@ test('initializes all providers', async () => {
   await ext.initialize(context)
   expect(notebooks.registerNotebookCellStatusBarItemProvider).toBeCalledTimes(6)
   expect(workspace.registerNotebookSerializer).toBeCalledTimes(1)
-  expect(commands.registerCommand).toBeCalledTimes(25)
+  expect(commands.registerCommand).toBeCalledTimes(26)
   expect(window.registerTreeDataProvider).toBeCalledTimes(1)
   expect(window.registerUriHandler).toBeCalledTimes(1)
   expect(bootFile).toBeCalledTimes(1)