1
+ import { md5 } from '@env/crypto' ;
1
2
import slug from 'slug' ;
2
3
import type { QuickPick } from 'vscode' ;
3
4
import { Uri } from 'vscode' ;
@@ -52,11 +53,12 @@ interface Context {
52
53
53
54
interface State {
54
55
item ?: StartWorkItem ;
55
- action ?: StartWorkAction ;
56
+ type ?: StartWorkType ;
56
57
inWorktree ?: boolean ;
57
58
}
58
59
59
- export type StartWorkAction = 'start' ;
60
+ export type StartWorkType = 'branch' | 'branch-worktree' | 'issue' | 'issue-worktree' ;
61
+ type StartWorkTypeItem = { type : StartWorkType ; inWorktree ?: boolean } ;
60
62
61
63
export interface StartWorkCommandArgs {
62
64
readonly command : 'startWork' ;
@@ -99,30 +101,57 @@ export class StartWorkCommand extends QuickCommand<State> {
99
101
connectedIntegrations : await this . getConnectedIntegrations ( ) ,
100
102
} ;
101
103
102
- const opened = false ;
104
+ let opened = false ;
103
105
while ( this . canStepsContinue ( state ) ) {
106
+ const hasConnectedIntegrations = [ ...context . connectedIntegrations . values ( ) ] . some ( c => c ) ;
104
107
context . title = this . title ;
105
108
106
109
if ( state . counter < 1 ) {
107
- const result = yield * this . selectCommandStep ( state ) ;
110
+ if ( this . container . telemetry . enabled ) {
111
+ this . container . telemetry . sendEvent (
112
+ opened ? 'startWork/steps/type' : 'startWork/opened' ,
113
+ {
114
+ ...context . telemetryContext ! ,
115
+ connected : hasConnectedIntegrations ,
116
+ } ,
117
+ this . source ,
118
+ ) ;
119
+ }
120
+
121
+ opened = true ;
122
+ const result = yield * this . selectTypeStep ( state ) ;
108
123
if ( result === StepResultBreak ) continue ;
109
- state . action = result . action ;
124
+ state . type = result . type ;
110
125
state . inWorktree = result . inWorktree ;
126
+ if ( this . container . telemetry . enabled ) {
127
+ this . container . telemetry . sendEvent (
128
+ 'startWork/type/chosen' ,
129
+ {
130
+ ...context . telemetryContext ! ,
131
+ connected : hasConnectedIntegrations ,
132
+ type : state . type ,
133
+ } ,
134
+ this . source ,
135
+ ) ;
136
+ }
111
137
}
112
138
113
- if ( state . counter < 2 && ! state . action ) {
114
- const hasConnectedIntegrations = [ ...context . connectedIntegrations . values ( ) ] . some ( c => c ) ;
139
+ if ( ( state . counter < 2 && state . type === 'issue' ) || state . type === 'issue-worktree' ) {
115
140
if ( ! hasConnectedIntegrations ) {
116
141
if ( this . container . telemetry . enabled ) {
117
142
this . container . telemetry . sendEvent (
118
143
opened ? 'startWork/steps/connect' : 'startWork/opened' ,
119
144
{
120
145
...context . telemetryContext ! ,
121
146
connected : false ,
147
+ type : state . type ,
122
148
} ,
123
149
this . source ,
124
150
) ;
125
151
}
152
+
153
+ opened = true ;
154
+
126
155
const isUsingCloudIntegrations = configuration . get ( 'cloudIntegrations.enabled' , undefined , false ) ;
127
156
const result = isUsingCloudIntegrations
128
157
? yield * this . confirmCloudIntegrationsConnectStep ( state , context )
@@ -133,67 +162,99 @@ export class StartWorkCommand extends QuickCommand<State> {
133
162
}
134
163
135
164
await updateContextItems ( this . container , context ) ;
165
+ if ( this . container . telemetry . enabled ) {
166
+ this . container . telemetry . sendEvent (
167
+ opened ? 'startWork/steps/issue' : 'startWork/opened' ,
168
+ {
169
+ ...context . telemetryContext ! ,
170
+ connected : true ,
171
+ type : state . type ,
172
+ } ,
173
+ this . source ,
174
+ ) ;
175
+ }
176
+
177
+ opened = true ;
178
+
136
179
const result = yield * this . pickIssueStep ( state , context ) ;
137
180
if ( result === StepResultBreak ) continue ;
138
- if ( typeof result !== 'string' ) {
181
+ if ( ! isStartWorkTypeItem ( result ) ) {
139
182
state . item = result ;
140
- state . action = 'start' ;
183
+ if ( this . container . telemetry . enabled ) {
184
+ this . container . telemetry . sendEvent (
185
+ 'startWork/issue/chosen' ,
186
+ {
187
+ ...context . telemetryContext ! ,
188
+ connected : true ,
189
+ type : state . type ,
190
+ 'item.id' : getStartWorkItemIdHash ( result ) ,
191
+ 'item.type' : result . item . issue . type ,
192
+ 'item.provider' : result . item . issue . provider . id ,
193
+ 'item.assignees.count' : result . item . issue . assignees ?. length ?? undefined ,
194
+ 'item.createdDate' : result . item . issue . createdDate . getTime ( ) ,
195
+ 'item.updatedDate' : result . item . issue . updatedDate . getTime ( ) ,
196
+
197
+ 'item.comments.count' : result . item . issue . commentsCount ?? undefined ,
198
+ 'item.upvotes.count' : result . item . issue . thumbsUpCount ?? undefined ,
199
+
200
+ 'item.issue.state' : result . item . issue . state ,
201
+ } ,
202
+ this . source ,
203
+ ) ;
204
+ }
141
205
} else {
142
- state . action = result ;
206
+ state . type = result . type ;
207
+ state . inWorktree = result . inWorktree ;
143
208
}
144
209
}
145
210
146
211
const issue = state . item ?. item ?. issue ;
147
212
const repo = issue && ( await this . getIssueRepositoryIfExists ( issue ) ) ;
148
213
149
- if ( typeof state . action === 'string' ) {
150
- switch ( state . action ) {
151
- case 'start' : {
152
- const result = yield * getSteps (
153
- this . container ,
154
- {
155
- command : 'branch' ,
156
- state : {
157
- subcommand : 'create' ,
158
- repo : repo ,
159
- name : issue
160
- ? `${ slug ( issue . id , { lower : false } ) } -${ slug ( issue . title ) } `
161
- : undefined ,
162
- suggestNameOnly : true ,
163
- suggestRepoOnly : true ,
164
- flags : state . inWorktree ? [ '--worktree' ] : [ '--switch' ] ,
165
- } ,
166
- confirm : false ,
167
- } ,
168
- this . pickedVia ,
169
- ) ;
170
- if ( result === StepResultBreak ) {
171
- endSteps ( state ) ;
172
- } else {
173
- state . counter -- ;
174
- state . action = undefined ;
175
- }
176
- break ;
177
- }
178
- }
214
+ const result = yield * getSteps (
215
+ this . container ,
216
+ {
217
+ command : 'branch' ,
218
+ state : {
219
+ subcommand : 'create' ,
220
+ repo : repo ,
221
+ name : issue ? `${ slug ( issue . id , { lower : false } ) } -${ slug ( issue . title ) } ` : undefined ,
222
+ suggestNameOnly : true ,
223
+ suggestRepoOnly : true ,
224
+ flags : state . inWorktree ? [ '--worktree' ] : [ '--switch' ] ,
225
+ } ,
226
+ confirm : false ,
227
+ } ,
228
+ this . pickedVia ,
229
+ ) ;
230
+ if ( result === StepResultBreak ) {
231
+ endSteps ( state ) ;
232
+ } else {
233
+ state . counter -- ;
179
234
}
180
235
}
181
236
182
237
return state . counter < 0 ? StepResultBreak : undefined ;
183
238
}
184
239
185
- private * selectCommandStep (
240
+ private * selectTypeStep (
186
241
state : StepState < State > ,
187
- ) : StepResultGenerator < { action ?: StartWorkAction ; inWorktree ?: boolean } > {
242
+ ) : StepResultGenerator < { type : StartWorkType ; inWorktree ?: boolean } > {
188
243
const step = createPickStep ( {
189
244
placeholder : 'Start work by creating a new branch' ,
190
245
items : [
191
- createQuickPickItemOfT ( 'Create a Branch' , {
192
- action : 'start' ,
246
+ createQuickPickItemOfT < StartWorkTypeItem > ( 'Create a Branch' , {
247
+ type : 'branch' ,
248
+ } ) ,
249
+ createQuickPickItemOfT < StartWorkTypeItem > ( 'Create a Branch in a Worktree' , {
250
+ type : 'branch-worktree' ,
251
+ inWorktree : true ,
252
+ } ) ,
253
+ createQuickPickItemOfT < StartWorkTypeItem > ( 'Create a Branch from an Issue' , { type : 'issue' } ) ,
254
+ createQuickPickItemOfT < StartWorkTypeItem > ( 'Create a Branch from an Issue in a Worktree' , {
255
+ type : 'issue-worktree' ,
256
+ inWorktree : true ,
193
257
} ) ,
194
- createQuickPickItemOfT ( 'Create a Branch in a Worktree' , { action : 'start' , inWorktree : true } ) ,
195
- createQuickPickItemOfT ( 'Create a Branch from an Issue' , { } ) ,
196
- createQuickPickItemOfT ( 'Create a Branch from an Issue in a Worktree' , { inWorktree : true } ) ,
197
258
] ,
198
259
} ) ;
199
260
const selection : StepSelection < typeof step > = yield step ;
@@ -336,7 +397,7 @@ export class StartWorkCommand extends QuickCommand<State> {
336
397
private * pickIssueStep (
337
398
state : StepState < State > ,
338
399
context : Context ,
339
- ) : StepResultGenerator < StartWorkItem | StartWorkAction > {
400
+ ) : StepResultGenerator < StartWorkItem | StartWorkTypeItem > {
340
401
const buildIssueItem = ( i : StartWorkItem ) => {
341
402
const buttons = i . item . issue . url ? [ OpenOnGitHubQuickInputButton ] : [ ] ;
342
403
return {
@@ -366,15 +427,15 @@ export class StartWorkCommand extends QuickCommand<State> {
366
427
367
428
function getItemsAndPlaceholder ( ) : {
368
429
placeholder : string ;
369
- items : QuickPickItemOfT < StartWorkItem | StartWorkAction > [ ] ;
430
+ items : QuickPickItemOfT < StartWorkItem | StartWorkTypeItem > [ ] ;
370
431
} {
371
432
if ( ! context . result . items . length ) {
372
433
return {
373
434
placeholder : 'No issues found. Start work anyway.' ,
374
435
items : [
375
- createQuickPickItemOfT (
436
+ createQuickPickItemOfT < StartWorkTypeItem > (
376
437
state . inWorktree ? 'Create a branch on a worktree' : 'Create a branch' ,
377
- 'start' ,
438
+ { type : state . inWorktree ? 'branch-worktree' : 'branch' , inWorktree : state . inWorktree } ,
378
439
) ,
379
440
] ,
380
441
} ;
@@ -395,7 +456,7 @@ export class StartWorkCommand extends QuickCommand<State> {
395
456
matchOnDetail : true ,
396
457
items : items ,
397
458
onDidClickItemButton : ( _quickpick , button , { item } ) => {
398
- if ( button === OpenOnGitHubQuickInputButton && typeof item !== 'string' ) {
459
+ if ( button === OpenOnGitHubQuickInputButton && ! isStartWorkTypeItem ( item ) ) {
399
460
this . open ( item ) ;
400
461
return true ;
401
462
}
@@ -411,13 +472,6 @@ export class StartWorkCommand extends QuickCommand<State> {
411
472
return typeof element . item === 'string' ? element . item : { ...element . item } ;
412
473
}
413
474
414
- private startWork ( state : PartialStepState < State > , item ?: StartWorkItem ) {
415
- state . action = 'start' ;
416
- if ( item != null ) {
417
- state . item = item ;
418
- }
419
- }
420
-
421
475
private open ( item : StartWorkItem ) : void {
422
476
if ( item . item . issue . url == null ) return ;
423
477
void openUrl ( item . item . issue . url ) ;
@@ -451,4 +505,22 @@ async function updateContextItems(container: Container, context: Context) {
451
505
} ) ,
452
506
) ?? [ ] ,
453
507
} ;
508
+ if ( container . telemetry . enabled ) {
509
+ updateTelemetryContext ( context ) ;
510
+ }
511
+ }
512
+
513
+ function updateTelemetryContext ( context : Context ) {
514
+ context . telemetryContext = {
515
+ ...context . telemetryContext ! ,
516
+ 'items.count' : context . result . items . length ,
517
+ } ;
518
+ }
519
+
520
+ function isStartWorkTypeItem ( item : unknown ) : item is StartWorkTypeItem {
521
+ return item != null && typeof item === 'object' && 'type' in item ;
522
+ }
523
+
524
+ export function getStartWorkItemIdHash ( item : StartWorkItem ) {
525
+ return md5 ( item . item . issue . id ) ;
454
526
}
0 commit comments