Skip to content

Commit 3dc37d4

Browse files
Improves Start Work flows - closes #3832, #3833, and #3836
- Adds title button to connect additional integrations. - Adds telemetry event startWork/title/action - Separates out "create branch" logic from Start Work and moves into its own telemetry even home/createBranch - Removes all references to "start work type" including in telemetry - Updates placeholder messaging - Adds option to connect additional integrations or manage integrations when no issues are found. - Adds telemetry event startWork/action - Fixes back button and prevents refetching issues when clicked - Updates freeze/resume logic into shared place and applies to all areas that use it - Removes launchpad description item when at least one integration is connected in the connect step # Conflicts: # src/constants.telemetry.ts Removes todo on comment and sets up similar event for startWork
1 parent 02d0a25 commit 3dc37d4

File tree

12 files changed

+350
-343
lines changed

12 files changed

+350
-343
lines changed

src/commands/git/search.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,7 @@ import type {
2828
StepSelection,
2929
StepState,
3030
} from '../quickCommand';
31-
import {
32-
canPickStepContinue,
33-
createPickStep,
34-
endSteps,
35-
freezeStep,
36-
QuickCommand,
37-
StepResultBreak,
38-
} from '../quickCommand';
31+
import { canPickStepContinue, createPickStep, endSteps, QuickCommand, StepResultBreak } from '../quickCommand';
3932
import {
4033
MatchAllToggleQuickInputButton,
4134
MatchCaseToggleQuickInputButton,
@@ -470,7 +463,7 @@ async function updateSearchQuery(
470463
let append = false;
471464

472465
if (usePickers?.author && item.item === 'author:') {
473-
using frozen = freezeStep(step, quickpick);
466+
using _frozen = step.freeze?.();
474467

475468
const authors = ops.get('author:');
476469

@@ -492,8 +485,6 @@ async function updateSearchQuery(
492485
},
493486
);
494487

495-
frozen[Symbol.dispose]();
496-
497488
if (contributors != null) {
498489
const authors = contributors
499490
.map(c => c.email ?? c.name ?? c.username)
@@ -507,7 +498,7 @@ async function updateSearchQuery(
507498
append = true;
508499
}
509500
} else if (usePickers?.file && item.item === 'file:') {
510-
using frozen = freezeStep(step, quickpick);
501+
using _frozen = step.freeze?.();
511502

512503
let files = ops.get('file:');
513504

@@ -520,8 +511,6 @@ async function updateSearchQuery(
520511
defaultUri: state.repo.folder?.uri,
521512
});
522513

523-
frozen[Symbol.dispose]();
524-
525514
if (uris?.length) {
526515
if (files == null) {
527516
files = new Set();

src/commands/quickCommand.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export interface QuickPickStep<T extends QuickPickItem = QuickPickItem> {
6666
value?: string;
6767
selectValueWhenShown?: boolean;
6868

69+
quickpick?: QuickPick<DirectiveQuickPickItem | T>;
70+
freeze?: () => Disposable;
6971
frozen?: boolean;
7072

7173
onDidActivate?(quickpick: QuickPick<DirectiveQuickPickItem | T>): void;
@@ -361,7 +363,28 @@ export function createInputStep<T extends string>(step: Optional<QuickInputStep<
361363
}
362364

363365
export function createPickStep<T extends QuickPickItem>(step: Optional<QuickPickStep<T>, 'type'>): QuickPickStep<T> {
364-
return { type: 'pick', ...step };
366+
const original = step.onDidActivate;
367+
step = { type: 'pick' as const, ...step };
368+
step.onDidActivate = qp => {
369+
step.quickpick = qp;
370+
step.freeze = () => {
371+
qp.enabled = false;
372+
const originalFocusOut = qp.ignoreFocusOut;
373+
qp.ignoreFocusOut = true;
374+
step.frozen = true;
375+
return {
376+
[Symbol.dispose]: () => {
377+
step.frozen = false;
378+
qp.enabled = true;
379+
qp.ignoreFocusOut = originalFocusOut;
380+
qp.show();
381+
},
382+
};
383+
};
384+
original?.(qp);
385+
};
386+
387+
return step as QuickPickStep<T>;
365388
}
366389

367390
export function createCustomStep<T>(step: Optional<CustomStep<T>, 'type'>): CustomStep<T> {
@@ -372,18 +395,6 @@ export function endSteps(state: PartialStepState) {
372395
state.counter = -1;
373396
}
374397

375-
export function freezeStep(step: QuickPickStep, quickpick: QuickPick<any>): Disposable {
376-
quickpick.enabled = false;
377-
step.frozen = true;
378-
return {
379-
[Symbol.dispose]: () => {
380-
step.frozen = false;
381-
quickpick.enabled = true;
382-
quickpick.show();
383-
},
384-
};
385-
}
386-
387398
export interface CrossCommandReference<T = unknown> {
388399
command: Commands;
389400
args?: T;

src/constants.commands.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,17 @@ export type CoreGitCommands =
329329
| 'git.pushForce'
330330
| 'git.undoCommit';
331331

332+
export type CustomViewCommands =
333+
| 'gitlens.home.openInGraph'
334+
| 'gitlens.home.fetch'
335+
| 'gitlens.home.openPullRequestChanges'
336+
| 'gitlens.home.openPullRequestOnRemote'
337+
| 'gitlens.home.createPullRequest'
338+
| 'gitlens.home.openWorktree'
339+
| 'gitlens.home.switchToBranch'
340+
| 'gitlens.home.createBranch'
341+
| 'gitlens.home.startWork';
342+
332343
export type TreeViewCommands = `gitlens.views.${
333344
| `branches.${
334345
| 'copy'

src/constants.telemetry.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type { CustomEditorTypes, TreeViewTypes, WebviewTypes, WebviewViewTypes }
88
import type { FeaturePreviews, FeaturePreviewStatus } from './features';
99
import type { GitContributionTiers } from './git/models/contributor';
1010
import type { Subscription, SubscriptionAccount } from './plus/gk/account/subscription';
11-
import type { StartWorkType } from './plus/startWork/startWork';
1211
import type { GraphColumnConfig } from './plus/webviews/graph/protocol';
1312
import type { Period } from './plus/webviews/timeline/protocol';
1413
import type { Flatten } from './system/object';
@@ -227,6 +226,10 @@ export type TelemetryEvents = {
227226
enabled: boolean;
228227
version: string;
229228
};
229+
/** Sent when the user chooses to create a branch from the home view */
230+
'home/createBranch': void;
231+
/** Sent when the user chooses to start work on an issue from the home view */
232+
'home/startWork': void;
230233

231234
/** Sent when the user takes an action on the Launchpad title bar */
232235
'launchpad/title/action': {
@@ -360,25 +363,26 @@ export type TelemetryEvents = {
360363
'startWork/open': StartWorkEventDataBase;
361364
/** Sent when the launchpad is opened; use `instance` to correlate a StartWork "session" */
362365
'startWork/opened': StartWorkConnectedEventData;
363-
/** Sent when the user chooses an option to start work in the first step */
364-
'startWork/type/chosen': {
365-
type: StartWorkType;
366-
} & StartWorkConnectedEventData;
367366
/** Sent when the user takes an action on a StartWork issue */
368367
'startWork/issue/action': {
369368
action: 'soft-open';
370-
type: StartWorkType;
371369
} & StartWorkConnectedEventData &
372370
Partial<Record<`item.${string}`, string | number | boolean>>;
373371
/** Sent when the user chooses an issue to start work in the second step */
374-
'startWork/issue/chosen': {
375-
type: StartWorkType;
376-
} & StartWorkConnectedEventData &
372+
'startWork/issue/chosen': StartWorkConnectedEventData &
377373
Partial<Record<`item.${string}`, string | number | boolean>>;
378-
/** Sent when the Start Work has "reloaded" (while open, e.g. user refreshed or back button) and is disconnected; use `instance` to correlate a Start Work "session" */
379-
'startWork/steps/type': StartWorkConnectedEventData;
374+
/** Sent when the user reaches the "connect an integration" step of Start Work */
380375
'startWork/steps/connect': StartWorkConnectedEventData;
376+
/** Sent when the user reaches the "choose an issue" step of Start Work */
381377
'startWork/steps/issue': StartWorkConnectedEventData;
378+
/** Sent when the user chooses to connect an integration */
379+
'startWork/title/action': StartWorkConnectedEventData & {
380+
action: 'connect';
381+
};
382+
/** Sent when the user chooses to manage integrations */
383+
'startWork/action': StartWorkConnectedEventData & {
384+
action: 'manage' | 'connect';
385+
};
382386

383387
/** Sent when the subscription is loaded */
384388
subscription: SubscriptionEventData;
@@ -602,7 +606,6 @@ interface RepositoryOpenedEventData extends RepositoryEventData, RepositoryContr
602606

603607
type StartWorkEventDataBase = {
604608
instance: number;
605-
type?: StartWorkType;
606609
};
607610

608611
type StartWorkEventData = {

src/plus/launchpad/launchpad.ts

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
canPickStepContinue,
1414
createPickStep,
1515
endSteps,
16-
freezeStep,
1716
QuickCommand,
1817
StepResultBreak,
1918
} from '../../commands/quickCommand';
@@ -978,20 +977,23 @@ export class LaunchpadCommand extends QuickCommand<State> {
978977
state: StepState<State>,
979978
context: Context,
980979
): AsyncStepResultGenerator<{ connected: boolean | IntegrationId; resume: () => void }> {
981-
const confirmations: (QuickPickItemOfT<IntegrationId> | DirectiveQuickPickItem)[] = [
982-
createDirectiveQuickPickItem(Directive.Cancel, undefined, {
983-
label: 'Launchpad prioritizes your pull requests to keep you focused and your team unblocked',
984-
detail: 'Click to learn more about Launchpad',
985-
iconPath: new ThemeIcon('rocket'),
986-
onDidSelect: () =>
987-
void executeCommand<OpenWalkthroughCommandArgs>(Commands.OpenWalkthrough, {
988-
step: 'accelerate-pr-reviews',
989-
source: 'launchpad',
990-
detail: 'info',
980+
const hasConnectedIntegration = some(context.connectedIntegrations.values(), c => c);
981+
const confirmations: (QuickPickItemOfT<IntegrationId> | DirectiveQuickPickItem)[] = !hasConnectedIntegration
982+
? [
983+
createDirectiveQuickPickItem(Directive.Cancel, undefined, {
984+
label: 'Launchpad prioritizes your pull requests to keep you focused and your team unblocked',
985+
detail: 'Click to learn more about Launchpad',
986+
iconPath: new ThemeIcon('rocket'),
987+
onDidSelect: () =>
988+
void executeCommand<OpenWalkthroughCommandArgs>(Commands.OpenWalkthrough, {
989+
step: 'accelerate-pr-reviews',
990+
source: 'launchpad',
991+
detail: 'info',
992+
}),
991993
}),
992-
}),
993-
createQuickPickSeparator(),
994-
];
994+
createQuickPickSeparator(),
995+
]
996+
: [];
995997

996998
for (const integration of supportedLaunchpadIntegrations) {
997999
if (context.connectedIntegrations.get(integration)) {
@@ -1032,19 +1034,12 @@ export class LaunchpadCommand extends QuickCommand<State> {
10321034
{ placeholder: 'Connect an integration to get started with Launchpad', buttons: [], ignoreFocusOut: false },
10331035
);
10341036

1035-
// Note: This is a hack to allow the quickpick to stay alive after the user finishes connecting the integration.
1036-
// Otherwise it disappears.
1037-
let freeze!: () => Disposable;
1038-
step.onDidActivate = qp => {
1039-
freeze = () => freezeStep(step, qp);
1040-
};
1041-
10421037
const selection: StepSelection<typeof step> = yield step;
10431038
if (canPickStepContinue(step, state, selection)) {
1044-
const resume = freeze();
1039+
const resume = step.freeze?.();
10451040
const chosenIntegrationId = selection[0].item;
10461041
const connected = await this.ensureIntegrationConnected(chosenIntegrationId);
1047-
return { connected: connected ? chosenIntegrationId : false, resume: () => resume[Symbol.dispose]() };
1042+
return { connected: connected ? chosenIntegrationId : false, resume: () => resume?.[Symbol.dispose]() };
10481043
}
10491044

10501045
return StepResultBreak;
@@ -1058,18 +1053,22 @@ export class LaunchpadCommand extends QuickCommand<State> {
10581053
const step = this.createConfirmStep(
10591054
`${this.title} \u00a0\u2022\u00a0 Connect an ${hasConnectedIntegration ? 'Additional ' : ''}Integration`,
10601055
[
1061-
createDirectiveQuickPickItem(Directive.Cancel, undefined, {
1062-
label: 'Launchpad prioritizes your pull requests to keep you focused and your team unblocked',
1063-
detail: 'Click to learn more about Launchpad',
1064-
iconPath: new ThemeIcon('rocket'),
1065-
onDidSelect: () =>
1066-
void executeCommand<OpenWalkthroughCommandArgs>(Commands.OpenWalkthrough, {
1067-
step: 'accelerate-pr-reviews',
1068-
source: 'launchpad',
1069-
detail: 'info',
1070-
}),
1071-
}),
1072-
createQuickPickSeparator(),
1056+
...(hasConnectedIntegration
1057+
? []
1058+
: [
1059+
createDirectiveQuickPickItem(Directive.Cancel, undefined, {
1060+
label: 'Launchpad prioritizes your pull requests to keep you focused and your team unblocked',
1061+
detail: 'Click to learn more about Launchpad',
1062+
iconPath: new ThemeIcon('rocket'),
1063+
onDidSelect: () =>
1064+
void executeCommand<OpenWalkthroughCommandArgs>(Commands.OpenWalkthrough, {
1065+
step: 'accelerate-pr-reviews',
1066+
source: 'launchpad',
1067+
detail: 'info',
1068+
}),
1069+
}),
1070+
createQuickPickSeparator(),
1071+
]),
10731072
createQuickPickItemOfT(
10741073
{
10751074
label: `Connect an ${hasConnectedIntegration ? 'Additional ' : ''}Integration...`,
@@ -1091,30 +1090,25 @@ export class LaunchpadCommand extends QuickCommand<State> {
10911090
},
10921091
);
10931092

1094-
// Note: This is a hack to allow the quickpick to stay alive after the user finishes connecting the integration.
1095-
// Otherwise it disappears.
1096-
let freeze!: () => Disposable;
1097-
let quickpick!: QuickPick<any>;
1098-
step.onDidActivate = qp => {
1099-
quickpick = qp;
1100-
freeze = () => freezeStep(step, qp);
1101-
};
1102-
11031093
const selection: StepSelection<typeof step> = yield step;
11041094

11051095
if (canPickStepContinue(step, state, selection)) {
1106-
const previousPlaceholder = quickpick.placeholder;
1107-
quickpick.placeholder = 'Connecting integrations...';
1108-
quickpick.ignoreFocusOut = true;
1109-
const resume = freeze();
1096+
let previousPlaceholder: string | undefined;
1097+
if (step.quickpick) {
1098+
previousPlaceholder = step.quickpick.placeholder;
1099+
step.quickpick.placeholder = 'Connecting integrations...';
1100+
}
1101+
const resume = step.freeze?.();
11101102
const connected = await this.container.integrations.connectCloudIntegrations(
11111103
{ integrationIds: supportedLaunchpadIntegrations },
11121104
{
11131105
source: 'launchpad',
11141106
},
11151107
);
1116-
quickpick.placeholder = previousPlaceholder;
1117-
return { connected: connected, resume: () => resume[Symbol.dispose]() };
1108+
if (step.quickpick) {
1109+
step.quickpick.placeholder = previousPlaceholder;
1110+
}
1111+
return { connected: connected, resume: () => resume?.[Symbol.dispose]() };
11181112
}
11191113

11201114
return StepResultBreak;

src/plus/launchpad/launchpadProvider.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,9 @@ export class LaunchpadProvider implements Disposable {
921921
await Promise.allSettled(
922922
supportedLaunchpadIntegrations.map(async integrationId => {
923923
const integration = await this.container.integrations.get(integrationId);
924-
connected.set(integrationId, integration.maybeConnected ?? (await integration.isConnected()));
924+
const isConnected = integration.maybeConnected ?? (await integration.isConnected());
925+
const hasAccess = isConnected && (await integration.access());
926+
connected.set(integrationId, hasAccess);
925927
}),
926928
);
927929

0 commit comments

Comments
 (0)