1- import type { QuickInputButton } from 'vscode' ;
1+ import type { QuickInputButton , QuickPick } from 'vscode' ;
22import { ThemeIcon , Uri } from 'vscode' ;
33import type {
4+ AsyncStepResultGenerator ,
45 PartialStepState ,
56 StepGenerator ,
67 StepResultGenerator ,
@@ -11,12 +12,16 @@ import {
1112 canPickStepContinue ,
1213 createPickStep ,
1314 endSteps ,
15+ freezeStep ,
1416 QuickCommand ,
1517 StepResultBreak ,
1618} from '../../commands/quickCommand' ;
1719import { ensureAccessStep } from '../../commands/quickCommand.steps' ;
1820import { getSteps } from '../../commands/quickWizard.utils' ;
21+ import type { OpenWalkthroughCommandArgs } from '../../commands/walkthroughs' ;
1922import { proBadge } from '../../constants' ;
23+ import { Commands } from '../../constants.commands' ;
24+ import type { IntegrationId } from '../../constants.integrations' ;
2025import { HostingIntegrationId } from '../../constants.integrations' ;
2126import type { Container } from '../../container' ;
2227import { PlusFeatures } from '../../features' ;
@@ -26,6 +31,8 @@ import { createQuickPickItemOfT, createQuickPickSeparator } from '../../quickpic
2631import type { DirectiveQuickPickItem } from '../../quickpicks/items/directive' ;
2732import { createDirectiveQuickPickItem , Directive , isDirectiveQuickPickItem } from '../../quickpicks/items/directive' ;
2833import { fromNow } from '../../system/date' ;
34+ import { some } from '../../system/iterable' ;
35+ import { executeCommand } from '../../system/vscode/command' ;
2936
3037export type StartWorkItem = {
3138 item : SearchedIssue ;
@@ -41,6 +48,7 @@ export type StartWorkResult = { items: StartWorkItem[] };
4148interface Context {
4249 result : StartWorkResult ;
4350 title : string ;
51+ connectedIntegrations : Map < IntegrationId , boolean > ;
4452}
4553
4654interface State {
@@ -63,6 +71,8 @@ function assertsStartWorkStepState(state: StepState<State>): asserts state is St
6371 throw new Error ( 'Missing item' ) ;
6472}
6573
74+ export const supportedStartWorkIntegrations = [ HostingIntegrationId . GitHub ] ;
75+
6676export class StartWorkCommand extends QuickCommand < State > {
6777 constructor ( container : Container ) {
6878 super ( container , 'startWork' , 'startWork' , `Start Work\u00a0\u00a0${ proBadge } ` , {
@@ -82,10 +92,19 @@ export class StartWorkCommand extends QuickCommand<State> {
8292 const context : Context = {
8393 result : { items : [ ] } ,
8494 title : this . title ,
95+ connectedIntegrations : await this . getConnectedIntegrations ( ) ,
8596 } ;
8697
8798 while ( this . canStepsContinue ( state ) ) {
8899 context . title = this . title ;
100+
101+ const hasConnectedIntegrations = [ ...context . connectedIntegrations . values ( ) ] . some ( c => c ) ;
102+ if ( ! hasConnectedIntegrations ) {
103+ const result = yield * this . confirmCloudIntegrationsConnectStep ( state , context ) ;
104+ if ( result === StepResultBreak ) {
105+ return result ;
106+ }
107+ }
89108 const result = yield * ensureAccessStep ( state , context , PlusFeatures . Launchpad ) ;
90109 if ( result === StepResultBreak ) continue ;
91110
@@ -135,6 +154,78 @@ export class StartWorkCommand extends QuickCommand<State> {
135154 return state . counter < 0 ? StepResultBreak : undefined ;
136155 }
137156
157+ private async * confirmCloudIntegrationsConnectStep (
158+ state : StepState < State > ,
159+ context : Context ,
160+ ) : AsyncStepResultGenerator < { connected : boolean | IntegrationId ; resume : ( ) => void } > {
161+ // TODO: This step is almost an exact copy of the similar one from launchpad.ts. Do we want to do anything about it? Maybe to move it to an util function with ability to parameterize labels?
162+ const hasConnectedIntegration = some ( context . connectedIntegrations . values ( ) , c => c ) ;
163+ const step = this . createConfirmStep (
164+ `${ this . title } \u00a0\u2022\u00a0 Connect an ${ hasConnectedIntegration ? 'Additional ' : '' } Integration` ,
165+ [
166+ createDirectiveQuickPickItem ( Directive . Cancel , undefined , {
167+ label : 'Start Work lets you start work on an issue' ,
168+ detail : 'Click to learn more about Start Work' ,
169+ iconPath : new ThemeIcon ( 'rocket' ) ,
170+ onDidSelect : ( ) =>
171+ // TODO: navigate to "start-work" related place
172+ void executeCommand < OpenWalkthroughCommandArgs > ( Commands . OpenWalkthrough , {
173+ step : 'launchpad' ,
174+ source : 'launchpad' ,
175+ detail : 'info' ,
176+ } ) ,
177+ } ) ,
178+ createQuickPickSeparator ( ) ,
179+ createQuickPickItemOfT (
180+ {
181+ label : `Connect an ${ hasConnectedIntegration ? 'Additional ' : '' } Integration...` ,
182+ detail : hasConnectedIntegration
183+ ? 'Connect additional integrations to view their issues in Start Work'
184+ : 'Connect an integration to accelerate your work' ,
185+ picked : true ,
186+ } ,
187+ true ,
188+ ) ,
189+ ] ,
190+ createDirectiveQuickPickItem ( Directive . Cancel , false , { label : 'Cancel' } ) ,
191+ {
192+ placeholder : hasConnectedIntegration
193+ ? 'Connect additional integrations to Start Work'
194+ : 'Connect an integration to get started with Start Work' ,
195+ buttons : [ ] ,
196+ ignoreFocusOut : true ,
197+ } ,
198+ ) ;
199+
200+ // Note: This is a hack to allow the quickpick to stay alive after the user finishes connecting the integration.
201+ // Otherwise it disappears.
202+ let freeze ! : ( ) => Disposable ;
203+ let quickpick ! : QuickPick < any > ;
204+ step . onDidActivate = qp => {
205+ quickpick = qp ;
206+ freeze = ( ) => freezeStep ( step , qp ) ;
207+ } ;
208+
209+ const selection : StepSelection < typeof step > = yield step ;
210+
211+ if ( canPickStepContinue ( step , state , selection ) ) {
212+ const previousPlaceholder = quickpick . placeholder ;
213+ quickpick . placeholder = 'Connecting integrations...' ;
214+ quickpick . ignoreFocusOut = true ;
215+ const resume = freeze ( ) ;
216+ const connected = await this . container . integrations . connectCloudIntegrations (
217+ { integrationIds : supportedStartWorkIntegrations } ,
218+ {
219+ source : 'startWork' ,
220+ } ,
221+ ) ;
222+ quickpick . placeholder = previousPlaceholder ;
223+ return { connected : connected , resume : ( ) => resume [ Symbol . dispose ] ( ) } ;
224+ }
225+
226+ return StepResultBreak ;
227+ }
228+
138229 private * pickIssueStep ( state : StepState < State > , context : Context ) : StepResultGenerator < StartWorkItem > {
139230 const buildIssueItem = ( i : StartWorkItem ) => {
140231 const buttons = [ StartWorkQuickInputButton ] ;
@@ -261,6 +352,18 @@ export class StartWorkCommand extends QuickCommand<State> {
261352 state . item = item ;
262353 }
263354 }
355+
356+ private async getConnectedIntegrations ( ) : Promise < Map < IntegrationId , boolean > > {
357+ const connected = new Map < IntegrationId , boolean > ( ) ;
358+ await Promise . allSettled (
359+ supportedStartWorkIntegrations . map ( async integrationId => {
360+ const integration = await this . container . integrations . get ( integrationId ) ;
361+ connected . set ( integrationId , integration . maybeConnected ?? ( await integration . isConnected ( ) ) ) ;
362+ } ) ,
363+ ) ;
364+
365+ return connected ;
366+ }
264367}
265368
266369async function updateContextItems ( container : Container , context : Context ) {
0 commit comments