@@ -2,7 +2,14 @@ import { parseISO } from 'date-fns';
2
2
import { formatDistanceToNow } from 'date-fns/formatDistanceToNow' ;
3
3
import * as vscode from 'vscode' ;
4
4
import { clientForSite } from '../../bitbucket/bbUtils' ;
5
- import { Commit , PaginatedComments , PaginatedPullRequests , PullRequest } from '../../bitbucket/model' ;
5
+ import {
6
+ Commit ,
7
+ PaginatedComments ,
8
+ PaginatedPullRequests ,
9
+ PullRequest ,
10
+ Task ,
11
+ type FileDiff ,
12
+ } from '../../bitbucket/model' ;
6
13
import { Commands } from '../../commands' ;
7
14
import { Logger } from '../../logger' ;
8
15
import { Resources } from '../../resources' ;
@@ -17,7 +24,8 @@ export const PullRequestContextValue = 'pullrequest';
17
24
export class PullRequestTitlesNode extends AbstractBaseNode {
18
25
private treeItem : vscode . TreeItem ;
19
26
public prHref : string ;
20
- private childrenPromises : Promise < AbstractBaseNode [ ] > ;
27
+ private loadedChildren : AbstractBaseNode [ ] = [ ] ;
28
+ private isLoading = false ;
21
29
22
30
constructor (
23
31
private pr : PullRequest ,
@@ -33,7 +41,7 @@ export class PullRequestTitlesNode extends AbstractBaseNode {
33
41
//grabbing all the PR data. Due to rate limits imposed by BBServer admins, mass preloading of all nodes is not feasible without
34
42
//caching.
35
43
if ( shouldPreload ) {
36
- this . childrenPromises = this . fetchDataAndProcessChildren ( ) ;
44
+ this . fetchDataAndProcessChildren ( ) ;
37
45
}
38
46
}
39
47
@@ -76,51 +84,111 @@ export class PullRequestTitlesNode extends AbstractBaseNode {
76
84
return this . pr ;
77
85
}
78
86
79
- async fetchDataAndProcessChildren ( ) : Promise < AbstractBaseNode [ ] | [ SimpleNode ] > {
80
- if ( ! this . pr ) {
81
- return [ ] ;
87
+ refresh ( ) : void {
88
+ vscode . commands . executeCommand ( Commands . RefreshPullRequestExplorerNode , this . treeItem . resourceUri ) ;
89
+ }
90
+
91
+ async criticalData (
92
+ criticalPromise : Promise < [ FileDiff [ ] , PaginatedComments ] > ,
93
+ ) : Promise < [ FileDiff [ ] , PaginatedComments , AbstractBaseNode [ ] ] > {
94
+ let fileChangedNodes : AbstractBaseNode [ ] = [ ] ;
95
+ let files : FileDiff [ ] = [ ] ;
96
+ let comments : PaginatedComments = { data : [ ] } ;
97
+ try {
98
+ [ files , comments ] = await criticalPromise ;
99
+ fileChangedNodes = await createFileChangesNodes ( this . pr , comments , files , [ ] , [ ] ) ;
100
+ // update loadedChildren with critical data without commits
101
+ this . loadedChildren = [
102
+ new DescriptionNode ( this . pr , this ) ,
103
+ ...( this . pr . site . details . isCloud ? [ new CommitSectionNode ( this . pr , [ ] , true ) ] : [ ] ) ,
104
+ ...fileChangedNodes ,
105
+ ] ;
106
+ } catch ( error ) {
107
+ Logger . debug ( 'error fetching pull request details' , error ) ;
108
+ this . loadedChildren = [ new SimpleNode ( '⚠️ Error: fetching pull request details failed' ) ] ;
109
+ this . isLoading = false ;
110
+ } finally {
111
+ this . refresh ( ) ;
112
+ return [ files , comments , fileChangedNodes ] ;
113
+ }
114
+ }
115
+
116
+ async nonCriticalData (
117
+ nonCriticalPromise : Promise < [ string [ ] , Task [ ] ] > ,
118
+ fileDiffs : FileDiff [ ] ,
119
+ allComments : PaginatedComments ,
120
+ commits : Commit [ ] ,
121
+ ) : Promise < void > {
122
+ try {
123
+ const [ conflictedFiles , tasks ] = await nonCriticalPromise ;
124
+ const [ jiraIssueNodes , bbIssueNodes , fileNodes ] = await Promise . all ( [
125
+ this . createRelatedJiraIssueNode ( commits , allComments ) ,
126
+ this . createRelatedBitbucketIssueNode ( commits , allComments ) ,
127
+ createFileChangesNodes ( this . pr , allComments , fileDiffs , conflictedFiles , tasks ) ,
128
+ ] ) ;
129
+ // update loadedChildren with additional data
130
+ this . loadedChildren = [
131
+ new DescriptionNode ( this . pr , this ) ,
132
+ ...( this . pr . site . details . isCloud ? [ new CommitSectionNode ( this . pr , commits ) ] : [ ] ) ,
133
+ ...jiraIssueNodes ,
134
+ ...bbIssueNodes ,
135
+ ...fileNodes ,
136
+ ] ;
137
+ } catch ( error ) {
138
+ Logger . debug ( 'error fetching additional pull request details' , error ) ;
139
+ // Keep existing nodes if additional data fetch fails
140
+ }
141
+ }
142
+
143
+ async fetchDataAndProcessChildren ( ) : Promise < void > {
144
+ // Return early if already loading or no PR
145
+ if ( this . isLoading || ! this . pr ) {
146
+ return ;
82
147
}
83
148
149
+ this . isLoading = true ;
150
+ this . loadedChildren = [ new DescriptionNode ( this . pr , this ) , new SimpleNode ( 'Loading...' ) ] ;
151
+ let fileDiffs : FileDiff [ ] = [ ] ;
152
+ let allComments : PaginatedComments = { data : [ ] } ;
153
+ let fileChangedNodes : AbstractBaseNode [ ] = [ ] ;
84
154
const bbApi = await clientForSite ( this . pr . site ) ;
85
- const promises = Promise . all ( [
155
+ const criticalPromise = Promise . all ( [
86
156
bbApi . pullrequests . getChangedFiles ( this . pr ) ,
87
- bbApi . pullrequests . getConflictedFiles ( this . pr ) ,
88
- bbApi . pullrequests . getCommits ( this . pr ) ,
89
157
bbApi . pullrequests . getComments ( this . pr ) ,
158
+ ] ) ;
159
+ const commitsPromise = bbApi . pullrequests . getCommits ( this . pr ) ;
160
+ const nonCriticalPromise = Promise . all ( [
161
+ bbApi . pullrequests . getConflictedFiles ( this . pr ) ,
90
162
bbApi . pullrequests . getTasks ( this . pr ) ,
91
163
] ) ;
92
-
93
- return promises . then (
94
- async ( result ) => {
95
- const [ fileDiffs , conflictedFiles , commits , allComments , tasks ] = result ;
96
-
97
- const children : AbstractBaseNode [ ] = [ new DescriptionNode ( this . pr , this ) ] ;
98
-
99
- //Only enable commit-level review nodes for BB Cloud (at least for now)
100
- if ( this . pr . site . details . isCloud ) {
101
- children . push ( new CommitSectionNode ( this . pr , commits ) ) ;
102
- }
103
-
104
- children . push ( ...( await this . createRelatedJiraIssueNode ( commits , allComments ) ) ) ;
105
- children . push ( ...( await this . createRelatedBitbucketIssueNode ( commits , allComments ) ) ) ;
106
- children . push (
107
- ...( await createFileChangesNodes ( this . pr , allComments , fileDiffs , conflictedFiles , tasks ) ) ,
108
- ) ;
109
- return children ;
110
- } ,
111
- ( reason ) => {
112
- Logger . debug ( 'error fetching pull request details' , reason ) ;
113
- return [ new SimpleNode ( '⚠️ Error: fetching pull request details failed' ) ] ;
114
- } ,
115
- ) ;
164
+ // Critical data - files, comments, and fileChangedNodes
165
+ [ fileDiffs , allComments , fileChangedNodes ] = await this . criticalData ( criticalPromise ) ;
166
+ // get commitsData
167
+ const commits = await commitsPromise ;
168
+ // update loadedChildren with commits data
169
+ this . loadedChildren = [
170
+ new DescriptionNode ( this . pr , this ) ,
171
+ ...( this . pr . site . details . isCloud ? [ new CommitSectionNode ( this . pr , commits ) ] : [ ] ) ,
172
+ ...fileChangedNodes ,
173
+ ] ;
174
+ // refresh TreeView
175
+ this . refresh ( ) ;
176
+ // Additional data - conflicts, commits, tasks
177
+ await this . nonCriticalData ( nonCriticalPromise , fileDiffs , allComments , commits ) ;
178
+ // update Loading to false
179
+ this . isLoading = false ;
180
+ // refresh TreeView
181
+ this . refresh ( ) ;
116
182
}
117
183
118
184
async getChildren ( element ?: AbstractBaseNode ) : Promise < AbstractBaseNode [ ] > {
119
- if ( ! element ) {
120
- //If the promise is undefined, we didn't begin preloading in the constructor, so we need to make the full call here
121
- return await ( this . childrenPromises ?? this . fetchDataAndProcessChildren ( ) ) ;
185
+ if ( element ) {
186
+ return element . getChildren ( ) ;
187
+ }
188
+ if ( ! this . loadedChildren . length && ! this . isLoading ) {
189
+ this . fetchDataAndProcessChildren ( ) ;
122
190
}
123
- return element . getChildren ( ) ;
191
+ return this . loadedChildren ;
124
192
}
125
193
126
194
private async createRelatedJiraIssueNode (
0 commit comments