From 37881c20f3f49a39e335fdb42ea7b0e0c212cf4d Mon Sep 17 00:00:00 2001 From: Chivorotkiv Date: Mon, 18 Nov 2024 19:07:17 +0100 Subject: [PATCH 1/4] Shows different "globe" tooltips on GitHub and Jira issues in StartWork (#3774, #3775) --- src/commands/quickCommand.buttons.ts | 5 +++++ src/plus/startWork/startWork.ts | 33 ++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/commands/quickCommand.buttons.ts b/src/commands/quickCommand.buttons.ts index a17c71cfb0933..88015bb4d0b98 100644 --- a/src/commands/quickCommand.buttons.ts +++ b/src/commands/quickCommand.buttons.ts @@ -132,6 +132,11 @@ export const MergeQuickInputButton: QuickInputButton = { tooltip: 'Merge...', }; +export const OpenOnJiraQuickInputButton: QuickInputButton = { + iconPath: new ThemeIcon('globe'), + tooltip: 'Open on Jira', +}; + export const OpenOnGitHubQuickInputButton: QuickInputButton = { iconPath: new ThemeIcon('globe'), tooltip: 'Open on GitHub', diff --git a/src/plus/startWork/startWork.ts b/src/plus/startWork/startWork.ts index 1b799b74975c2..935cd477a9b9f 100644 --- a/src/plus/startWork/startWork.ts +++ b/src/plus/startWork/startWork.ts @@ -1,6 +1,6 @@ import { md5 } from '@env/crypto'; import slug from 'slug'; -import type { QuickPick } from 'vscode'; +import type { QuickInputButton, QuickPick } from 'vscode'; import { Uri } from 'vscode'; import type { AsyncStepResultGenerator, @@ -18,7 +18,7 @@ import { QuickCommand, StepResultBreak, } from '../../commands/quickCommand'; -import { OpenOnGitHubQuickInputButton } from '../../commands/quickCommand.buttons'; +import { OpenOnGitHubQuickInputButton, OpenOnJiraQuickInputButton } from '../../commands/quickCommand.buttons'; import { getSteps } from '../../commands/quickWizard.utils'; import { proBadge } from '../../constants'; import type { IntegrationId } from '../../constants.integrations'; @@ -414,7 +414,8 @@ export class StartWorkCommand extends QuickCommand { opened: boolean, ): StepResultGenerator { const buildIssueItem = (i: StartWorkItem) => { - const buttons = i.item.issue.url ? [OpenOnGitHubQuickInputButton] : []; + const onWebbButton = i.item.issue.url ? getOpenOnWebQuickInputButton(i.item.issue.provider.id) : undefined; + const buttons = onWebbButton ? [onWebbButton] : []; const hoverContent = i.item.issue.body ? `${repeatSpaces(200)}\n\n${i.item.issue.body}` : ''; return { label: @@ -499,11 +500,18 @@ export class StartWorkCommand extends QuickCommand { items: [], onDidActivate: updateItems, onDidClickItemButton: (_quickpick, button, { item }) => { - if (button === OpenOnGitHubQuickInputButton && !isStartWorkTypeItem(item)) { - this.open(item); - return undefined; + if (isStartWorkTypeItem(item)) { + return false; + } + + switch (button) { + case OpenOnGitHubQuickInputButton: + case OpenOnJiraQuickInputButton: + this.open(item); + return undefined; + default: + return false; } - return false; }, onDidChangeValue: () => true, }); @@ -576,3 +584,14 @@ function repeatSpaces(count: number) { export function getStartWorkItemIdHash(item: StartWorkItem) { return md5(item.item.issue.id); } + +function getOpenOnWebQuickInputButton(integrationId: string): QuickInputButton | undefined { + switch (integrationId) { + case HostingIntegrationId.GitHub: + return OpenOnGitHubQuickInputButton; + case IssueIntegrationId.Jira: + return OpenOnJiraQuickInputButton; + default: + return undefined; + } +} From 8d5edb504cf4b10048546e6a27c82418d96f0d89 Mon Sep 17 00:00:00 2001 From: Sergei Shmakov Date: Wed, 20 Nov 2024 14:20:21 +0100 Subject: [PATCH 2/4] Sends telemetry event when an issue is opened in web browser (#3774, #3775) --- docs/telemetry-events.md | 13 +++++++++++++ src/constants.telemetry.ts | 5 +++++ src/plus/startWork/startWork.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/docs/telemetry-events.md b/docs/telemetry-events.md index eba3108f66768..f14871d4dd654 100644 --- a/docs/telemetry-events.md +++ b/docs/telemetry-events.md @@ -1358,6 +1358,19 @@ void } ``` +### startWork/issue/action + +> Sent when the user takes an action on a StartWork issue + +```typescript +{ + 'instance': number, + 'type': 'branch' | 'branch-worktree' | 'issue' | 'issue-worktree', + 'items.count': number, + 'action': 'soft-open' +} +``` + ### startWork/issue/chosen > Sent when the user chooses an issue to start work in the second step diff --git a/src/constants.telemetry.ts b/src/constants.telemetry.ts index da5031b4594d5..d88955ab20618 100644 --- a/src/constants.telemetry.ts +++ b/src/constants.telemetry.ts @@ -324,6 +324,11 @@ export type TelemetryEvents = { connected: boolean; type: StartWorkType; }; + /** Sent when the user takes an action on a StartWork issue */ + 'startWork/issue/action': StartWorkEventData & { + action: 'soft-open'; + connected: boolean; + } & Partial>; /** Sent when the user chooses an issue to start work in the second step */ 'startWork/issue/chosen': StartWorkEventData & { connected: boolean; diff --git a/src/plus/startWork/startWork.ts b/src/plus/startWork/startWork.ts index 935cd477a9b9f..5941a6f2cfefa 100644 --- a/src/plus/startWork/startWork.ts +++ b/src/plus/startWork/startWork.ts @@ -507,6 +507,7 @@ export class StartWorkCommand extends QuickCommand { switch (button) { case OpenOnGitHubQuickInputButton: case OpenOnJiraQuickInputButton: + this.sendItemActionTelemetry('soft-open', item, state, context); this.open(item); return undefined; default: @@ -529,6 +530,35 @@ export class StartWorkCommand extends QuickCommand { void openUrl(item.item.issue.url); } + private sendItemActionTelemetry( + action: 'soft-open', + item: StartWorkItem, + state: StepState, + context: Context, + ) { + this.container.telemetry.sendEvent( + 'startWork/issue/action', + { + ...context.telemetryContext!, + action: action, + connected: true, + type: state.type, + 'item.id': getStartWorkItemIdHash(item), + 'item.type': item.item.issue.type, + 'item.provider': item.item.issue.provider.id, + 'item.assignees.count': item.item.issue.assignees?.length ?? undefined, + 'item.createdDate': item.item.issue.createdDate.getTime(), + 'item.updatedDate': item.item.issue.updatedDate.getTime(), + + 'item.comments.count': item.item.issue.commentsCount ?? undefined, + 'item.upvotes.count': item.item.issue.thumbsUpCount ?? undefined, + + 'item.issue.state': item.item.issue.state, + }, + this.source, + ); + } + private async getConnectedIntegrations(): Promise> { const connected = new Map(); await Promise.allSettled( From ea3d53af78a3b74cb46fc37039b78243ef22d63f Mon Sep 17 00:00:00 2001 From: Sergei Shmakov Date: Wed, 20 Nov 2024 15:02:19 +0100 Subject: [PATCH 3/4] Reuses a function that prepares issue data for telemetry (#3774, #3775) --- src/plus/startWork/startWork.ts | 56 ++++++++++++++------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/plus/startWork/startWork.ts b/src/plus/startWork/startWork.ts index 5941a6f2cfefa..f36652b464846 100644 --- a/src/plus/startWork/startWork.ts +++ b/src/plus/startWork/startWork.ts @@ -176,19 +176,9 @@ export class StartWorkCommand extends QuickCommand { 'startWork/issue/chosen', { ...context.telemetryContext!, + ...buildItemTelemetryData(result), connected: true, type: state.type, - 'item.id': getStartWorkItemIdHash(result), - 'item.type': result.item.issue.type, - 'item.provider': result.item.issue.provider.id, - 'item.assignees.count': result.item.issue.assignees?.length ?? undefined, - 'item.createdDate': result.item.issue.createdDate.getTime(), - 'item.updatedDate': result.item.issue.updatedDate.getTime(), - - 'item.comments.count': result.item.issue.commentsCount ?? undefined, - 'item.upvotes.count': result.item.issue.thumbsUpCount ?? undefined, - - 'item.issue.state': result.item.issue.state, }, this.source, ); @@ -536,27 +526,13 @@ export class StartWorkCommand extends QuickCommand { state: StepState, context: Context, ) { - this.container.telemetry.sendEvent( - 'startWork/issue/action', - { - ...context.telemetryContext!, - action: action, - connected: true, - type: state.type, - 'item.id': getStartWorkItemIdHash(item), - 'item.type': item.item.issue.type, - 'item.provider': item.item.issue.provider.id, - 'item.assignees.count': item.item.issue.assignees?.length ?? undefined, - 'item.createdDate': item.item.issue.createdDate.getTime(), - 'item.updatedDate': item.item.issue.updatedDate.getTime(), - - 'item.comments.count': item.item.issue.commentsCount ?? undefined, - 'item.upvotes.count': item.item.issue.thumbsUpCount ?? undefined, - - 'item.issue.state': item.item.issue.state, - }, - this.source, - ); + this.container.telemetry.sendEvent('startWork/issue/action', { + ...context.telemetryContext!, + ...buildItemTelemetryData(item), + action: action, + connected: true, + type: state.type, + }); } private async getConnectedIntegrations(): Promise> { @@ -615,6 +591,22 @@ export function getStartWorkItemIdHash(item: StartWorkItem) { return md5(item.item.issue.id); } +function buildItemTelemetryData(item: StartWorkItem) { + return { + 'item.id': getStartWorkItemIdHash(item), + 'item.type': item.item.issue.type, + 'item.provider': item.item.issue.provider.id, + 'item.assignees.count': item.item.issue.assignees?.length ?? undefined, + 'item.createdDate': item.item.issue.createdDate.getTime(), + 'item.updatedDate': item.item.issue.updatedDate.getTime(), + + 'item.comments.count': item.item.issue.commentsCount ?? undefined, + 'item.upvotes.count': item.item.issue.thumbsUpCount ?? undefined, + + 'item.issue.state': item.item.issue.state, + }; +} + function getOpenOnWebQuickInputButton(integrationId: string): QuickInputButton | undefined { switch (integrationId) { case HostingIntegrationId.GitHub: From 5724a60df583daef2283681415349ec08683223f Mon Sep 17 00:00:00 2001 From: Sergei Shmakov Date: Wed, 20 Nov 2024 15:21:14 +0100 Subject: [PATCH 4/4] Adds required `type` to 'startWork/issue/action' telemetry event (#3774, #3775) --- src/constants.telemetry.ts | 1 + src/plus/startWork/startWork.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/constants.telemetry.ts b/src/constants.telemetry.ts index d88955ab20618..1bfdfcc3b9659 100644 --- a/src/constants.telemetry.ts +++ b/src/constants.telemetry.ts @@ -328,6 +328,7 @@ export type TelemetryEvents = { 'startWork/issue/action': StartWorkEventData & { action: 'soft-open'; connected: boolean; + type: StartWorkType; } & Partial>; /** Sent when the user chooses an issue to start work in the second step */ 'startWork/issue/chosen': StartWorkEventData & { diff --git a/src/plus/startWork/startWork.ts b/src/plus/startWork/startWork.ts index f36652b464846..5e9481d9c9bd2 100644 --- a/src/plus/startWork/startWork.ts +++ b/src/plus/startWork/startWork.ts @@ -56,6 +56,9 @@ interface State { type?: StartWorkType; inWorktree?: boolean; } +interface StateWithType extends State { + type: StartWorkType; +} export type StartWorkType = 'branch' | 'branch-worktree' | 'issue' | 'issue-worktree'; type StartWorkTypeItem = { type: StartWorkType; inWorktree?: boolean }; @@ -166,6 +169,7 @@ export class StartWorkCommand extends QuickCommand { } } + assertsTypeStepState(state); const result = yield* this.pickIssueStep(state, context, opened); opened = true; if (result === StepResultBreak) continue; @@ -399,7 +403,7 @@ export class StartWorkCommand extends QuickCommand { } private *pickIssueStep( - state: StepState, + state: StepState, context: Context, opened: boolean, ): StepResultGenerator { @@ -523,7 +527,7 @@ export class StartWorkCommand extends QuickCommand { private sendItemActionTelemetry( action: 'soft-open', item: StartWorkItem, - state: StepState, + state: StepState, context: Context, ) { this.container.telemetry.sendEvent('startWork/issue/action', { @@ -617,3 +621,12 @@ function getOpenOnWebQuickInputButton(integrationId: string): QuickInputButton | return undefined; } } + +function assertsTypeStepState(state: StepState): asserts state is StepState { + if (state.type != null) { + return; + } + + debugger; + throw new Error('Missing `item` field in state of StartWork'); +}