Skip to content

Commit 75d7275

Browse files
committed
Improves error handling for authentication in launchpad
Enhances the launchpad error display by detecting authentication errors and providing a clearer, actionable message with options to reconnect integrations directly from the UI. Aggregates multiple errors to surface the most relevant cause and streamlines error item creation for consistency. Improves user experience by making it easier to resolve integration authentication issues. (#4492, #4748)
1 parent 1e57f7d commit 75d7275

File tree

1 file changed

+29
-14
lines changed

1 file changed

+29
-14
lines changed

src/plus/launchpad/launchpad.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import type { IntegrationIds } from '../../constants.integrations';
4141
import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../../constants.integrations';
4242
import type { LaunchpadTelemetryContext, Source, Sources, TelemetryEvents } from '../../constants.telemetry';
4343
import type { Container } from '../../container';
44+
import { AuthenticationError } from '../../errors';
4445
import type { QuickPickItemOfT } from '../../quickpicks/items/common';
4546
import { createQuickPickItemOfT, createQuickPickSeparator } from '../../quickpicks/items/common';
4647
import type { DirectiveQuickPickItem } from '../../quickpicks/items/directive';
@@ -53,6 +54,7 @@ import { getScopedCounter } from '../../system/counter';
5354
import { fromNow } from '../../system/date';
5455
import { some } from '../../system/iterable';
5556
import { Logger } from '../../system/logger';
57+
import { AggregateError } from '../../system/promise';
5658
import { interpolate, pluralize } from '../../system/string';
5759
import { ProviderBuildStatusState, ProviderPullRequestReviewState } from '../integrations/providers/models';
5860
import type { LaunchpadCategorizedResult, LaunchpadItem } from './launchpadProvider';
@@ -571,7 +573,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
571573
return groupedAndSorted;
572574
};
573575

574-
function getItemsAndQuickpickProps(isFiltering?: boolean) {
576+
const getItemsAndQuickpickProps = (isFiltering?: boolean) => {
575577
const result = context.inSearch ? context.searchResult : context.result;
576578

577579
if (result?.error != null && !result?.items?.length) {
@@ -625,18 +627,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
625627

626628
// Add error information item if there's an error but items were still loaded
627629
const errorItem: DirectiveQuickPickItem | undefined =
628-
result?.error != null
629-
? createDirectiveQuickPickItem(Directive.Noop, false, {
630-
label: '$(warning) Unable to fully load items',
631-
detail:
632-
result.error.name === 'HttpError' &&
633-
'status' in result.error &&
634-
typeof result.error.status === 'number'
635-
? `${result.error.status}: ${String(result.error)}`
636-
: String(result.error),
637-
buttons: [OpenLogsQuickInputButton],
638-
})
639-
: undefined;
630+
result?.error != null ? this.createErrorQuickPickItem(result.error) : undefined;
640631

641632
const hasPicked = items.some(i => i.picked);
642633
if (context.inSearch === 'mode') {
@@ -677,7 +668,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
677668
...getLaunchpadQuickPickItems(result.items, isFiltering),
678669
],
679670
};
680-
}
671+
};
681672

682673
const updateItems = async (
683674
quickpick: QuickPick<
@@ -863,6 +854,11 @@ export class LaunchpadCommand extends QuickCommand<State> {
863854
return;
864855
}
865856

857+
if (button === ConnectIntegrationButton) {
858+
await this.container.integrations.manageCloudIntegrations({ source: 'launchpad' });
859+
return;
860+
}
861+
866862
if (!item) return;
867863

868864
switch (button) {
@@ -1436,6 +1432,25 @@ export class LaunchpadCommand extends QuickCommand<State> {
14361432
this.source,
14371433
);
14381434
}
1435+
1436+
private createErrorQuickPickItem(error: Error): DirectiveQuickPickItem {
1437+
if (error instanceof AggregateError) {
1438+
const firstAuthError = error.errors.find(e => e instanceof AuthenticationError);
1439+
error = firstAuthError ?? error.errors[0] ?? error;
1440+
}
1441+
1442+
const isAuthError = error instanceof AuthenticationError;
1443+
1444+
return createDirectiveQuickPickItem(Directive.Noop, false, {
1445+
label: isAuthError ? '$(warning) Authentication Required' : '$(warning) Unable to fully load items',
1446+
detail: isAuthError
1447+
? `${String(error)} — Click to reconnect your integration`
1448+
: error.name === 'HttpError' && 'status' in error && typeof error.status === 'number'
1449+
? `${error.status}: ${String(error)}`
1450+
: String(error),
1451+
buttons: isAuthError ? [ConnectIntegrationButton, OpenLogsQuickInputButton] : [OpenLogsQuickInputButton],
1452+
});
1453+
}
14391454
}
14401455

14411456
function getLaunchpadItemInformationRows(

0 commit comments

Comments
 (0)