11import type { ConfigurationChangeEvent , TreeViewVisibilityChangeEvent } from 'vscode' ;
2- import { Disposable , ThemeIcon , TreeItem , TreeItemCollapsibleState , Uri , window } from 'vscode' ;
2+ import { Disposable , ThemeColor , ThemeIcon , TreeItem , TreeItemCollapsibleState , Uri , window } from 'vscode' ;
33import type { OpenWalkthroughCommandArgs } from '../commands/walkthroughs' ;
44import type { LaunchpadViewConfig , ViewFilesLayout } from '../config' ;
55import { proBadge } from '../constants' ;
66import type { Container } from '../container' ;
7- import { AuthenticationRequiredError } from '../errors' ;
7+ import { AuthenticationError , AuthenticationRequiredError } from '../errors' ;
88import { GitUri , unknownGitUri } from '../git/gitUri' ;
99import type { PullRequest } from '../git/models/pullRequest' ;
1010import type { SubscriptionChangeEvent } from '../plus/gk/subscriptionService' ;
@@ -16,9 +16,11 @@ import type { LaunchpadGroup } from '../plus/launchpad/models/launchpad';
1616import { launchpadGroupIconMap , launchpadGroupLabelMap } from '../plus/launchpad/models/launchpad' ;
1717import { createCommand , executeCommand } from '../system/-webview/command' ;
1818import { configuration } from '../system/-webview/configuration' ;
19+ import { AggregateError } from '../system/promise' ;
1920import { CacheableChildrenViewNode } from './nodes/abstract/cacheableChildrenViewNode' ;
2021import type { ClipboardType , ViewNode } from './nodes/abstract/viewNode' ;
2122import { ContextValues , getViewNodeId } from './nodes/abstract/viewNode' ;
23+ import { MessageNode } from './nodes/common' ;
2224import type { GroupingNode } from './nodes/groupingNode' ;
2325import { LaunchpadViewGroupingNode } from './nodes/launchpadViewGroupingNode' ;
2426import { getPullRequestChildren , getPullRequestTooltip } from './nodes/pullRequestNode' ;
@@ -121,7 +123,7 @@ export class LaunchpadItemNode extends CacheableChildrenViewNode<'launchpad-item
121123export class LaunchpadViewNode extends CacheableChildrenViewNode <
122124 'launchpad' ,
123125 LaunchpadView ,
124- GroupingNode | LaunchpadItemNode
126+ GroupingNode | LaunchpadItemNode | MessageNode
125127> {
126128 private disposable : Disposable ;
127129
@@ -151,7 +153,31 @@ export class LaunchpadViewNode extends CacheableChildrenViewNode<
151153 this . children = undefined ;
152154 }
153155
154- async getChildren ( ) : Promise < ( GroupingNode | LaunchpadItemNode ) [ ] > {
156+ private createErrorNode ( error : Error ) : MessageNode {
157+ // Extract AuthenticationError from AggregateError if present
158+ let actualError = error ;
159+ if ( error instanceof AggregateError ) {
160+ const firstAuthError = error . errors . find ( e => e instanceof AuthenticationError ) ;
161+ actualError = firstAuthError ?? error . errors [ 0 ] ?? error ;
162+ }
163+
164+ const isAuthError = actualError instanceof AuthenticationError ;
165+ const contextValue = isAuthError ? ContextValues . LaunchpadErrorAuth : ContextValues . LaunchpadError ;
166+
167+ return new MessageNode (
168+ this . view ,
169+ this ,
170+ isAuthError ? 'Authentication Required' : 'Unable to fully load items' ,
171+ actualError . name === 'HttpError' && 'status' in actualError && typeof actualError . status === 'number'
172+ ? `${ actualError . status } : ${ String ( actualError ) } `
173+ : String ( actualError ) ,
174+ String ( actualError ) ,
175+ new ThemeIcon ( 'warning' , new ThemeColor ( 'list.warningForeground' ) ) ,
176+ contextValue ,
177+ ) ;
178+ }
179+
180+ async getChildren ( ) : Promise < ( GroupingNode | LaunchpadItemNode | MessageNode ) [ ] > {
155181 this . view . description = this . view . grouped
156182 ? `${ this . view . name . toLocaleLowerCase ( ) } \u00a0\u2022\u00a0 ${ proBadge } `
157183 : proBadge ;
@@ -161,13 +187,25 @@ export class LaunchpadViewNode extends CacheableChildrenViewNode<
161187 const access = await this . view . container . git . access ( 'launchpad' ) ;
162188 if ( ! access . allowed ) return [ ] ;
163189
164- const children : ( GroupingNode | LaunchpadItemNode ) [ ] = [ ] ;
190+ const children : ( GroupingNode | LaunchpadItemNode | MessageNode ) [ ] = [ ] ;
165191
166192 const hasIntegrations = await this . view . container . launchpad . hasConnectedIntegration ( ) ;
167193 if ( ! hasIntegrations ) return [ ] ;
168194
169195 try {
170196 const result = await this . view . container . launchpad . getCategorizedItems ( ) ;
197+
198+ // Handle error - always show error node
199+ if ( result . error != null ) {
200+ this . view . message = undefined ;
201+ children . push ( this . createErrorNode ( result . error ) ) ;
202+
203+ // If there are no items, just return the error node
204+ if ( ! result . items ?. length ) {
205+ return children ;
206+ }
207+ }
208+
171209 if ( ! result . items ?. length ) {
172210 this . view . message = 'All done! Take a vacation.' ;
173211 return [ ] ;
0 commit comments