@@ -10,8 +10,11 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
1010 constructor ( { id, state, manager } ) {
1111 super ( { id, state, manager } ) ;
1212
13- this . state . url = "https://github.com/issues/assigned" ;
1413 this . state . type = state . type ;
14+ this . state . url =
15+ this . state . type === "pull-requests"
16+ ? "https://github.com/pulls"
17+ : "https://github.com/issues/assigned" ;
1518
1619 this . state . options = state . options ?? { } ;
1720 this . state . repos = new Set ( state . repos ?? [ ] ) ;
@@ -29,22 +32,106 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
2932 return "zen-live-folder-github-no-filter" ;
3033 }
3134
32- const searchParams = this . #buildSearchOptions( ) ;
33- const url = `${ this . state . url } ?${ searchParams } ` ;
35+ const queries = this . #buildSearchOptions( ) ;
36+ const requests = await Promise . all (
37+ queries . map ( query => {
38+ const url = new URL ( this . state . url ) ;
39+ url . searchParams . set ( "q" , query ) ;
3440
35- const { text, status } = await this . fetch ( url ) ;
41+ if ( this . state . type === "pull-requests" ) {
42+ return this . parsePullRequests ( url . href ) ;
43+ }
44+
45+ return this . parseIssues ( url . href ) ;
46+ } )
47+ ) ;
48+
49+ const combinedItems = new Map ( ) ;
50+ const combinedActiveRepos = new Set ( ) ;
51+
52+ for ( const { status, items, activeRepos } of requests ) {
53+ // Assume no auth
54+ if ( status === 404 ) {
55+ return "zen-live-folder-github-no-auth" ;
56+ }
57+
58+ if ( items ) {
59+ items . forEach ( item => combinedItems . set ( item . id , item ) ) ;
60+ }
3661
37- // Assume no auth
38- if ( status === 404 ) {
39- return "zen-live-folder-github-no-auth" ;
62+ if ( activeRepos ) {
63+ activeRepos . forEach ( repo => combinedActiveRepos . add ( repo ) ) ;
64+ }
4065 }
4166
67+ this . state . repos = combinedActiveRepos ;
68+ return Array . from ( combinedItems . values ( ) ) ;
69+ } catch ( error ) {
70+ console . error ( "Error fetching or parsing GitHub issues:" , error ) ;
71+ return "zen-live-folder-failed-fetch" ;
72+ }
73+ }
74+
75+ async parsePullRequests ( url ) {
76+ const { text, status } = await this . fetch ( url ) ;
77+
78+ try {
79+ const document = new DOMParser ( ) . parseFromString ( text , "text/html" ) ;
80+ const issues = document . querySelectorAll ( "div[id^=issue_]" ) ;
81+ const items = [ ] ;
82+ const activeRepos = [ ] ;
83+
84+ if ( issues . length ) {
85+ const authors = document . querySelectorAll ( ".opened-by a" ) ;
86+ const titles = document . querySelectorAll ( "a[id^=issue_]" ) ;
87+
88+ for ( let i = 0 ; i < issues . length ; i ++ ) {
89+ const author = authors [ i ] . textContent ;
90+ const title = titles [ i ] . textContent ;
91+
92+ const repo = titles [ i ] . previousElementSibling . textContent . trim ( ) ;
93+ if ( repo ) {
94+ activeRepos . push ( repo ) ;
95+ }
96+
97+ const idMatch = authors [ i ] . parentElement . textContent
98+ . match ( / # [ 0 - 9 ] + / )
99+ . shift ( ) ;
100+
101+ items . push ( {
102+ title,
103+ subtitle : author ,
104+ icon : "chrome://browser/content/zen-images/favicons/github.svg" ,
105+ url : new URL ( titles [ i ] . href , this . state . url ) ,
106+ id : `${ repo } ${ idMatch } ` ,
107+ } ) ;
108+ }
109+ }
110+
111+ return {
112+ status,
113+
114+ items,
115+ activeRepos,
116+ } ;
117+ } catch ( err ) {
118+ console . error ( "Failed to parse Github pull requests" , err ) ;
119+ return {
120+ status,
121+ } ;
122+ }
123+ }
124+
125+ async parseIssues ( url ) {
126+ const { text, status } = await this . fetch ( url ) ;
127+
128+ try {
42129 const document = new DOMParser ( ) . parseFromString ( text , "text/html" ) ;
43130 const issues = document . querySelectorAll (
44131 "div[class^=IssueItem-module__defaultRepoContainer]"
45132 ) ;
46133 const items = [ ] ;
47- const activeRepos = new Set ( ) ;
134+ const activeRepos = [ ] ;
48135
49136 if ( issues . length ) {
50137 const authors = document . querySelectorAll (
@@ -65,7 +152,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
65152
66153 const repo = rawRepo . textContent ?. trim ( ) ;
67154 if ( repo ) {
68- activeRepos . add ( repo ) ;
155+ activeRepos . push ( repo ) ;
69156 }
70157
71158 const numberMatch = rawNumber ?. textContent ?. match ( / [ 0 - 9 ] + / ) ;
@@ -81,76 +168,67 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
81168 }
82169 }
83170
84- this . state . repos = activeRepos ;
85-
86- return items ;
87- } catch ( error ) {
88- console . error ( "Error fetching or parsing GitHub issues:" , error ) ;
89- return "zen-live-folder-failed-fetch" ;
171+ return {
172+ status,
173+
174+ items,
175+ activeRepos,
176+ } ;
177+ } catch ( err ) {
178+ console . error ( "Failed to parse Github Issues" , err ) ;
179+ return {
180+ status,
181+ } ;
90182 }
91183 }
92184
93185 #buildSearchOptions( ) {
94- let searchParams = new URLSearchParams ( ) ;
186+ const baseQuery = [
187+ this . state . type === "pull-requests" ? "is:pr" : "is:issue" ,
188+ "state:open" ,
189+ "sort:updated-desc" ,
190+ ] ;
191+
95192 const options = [
96193 {
97- value : "state:open " ,
98- enabled : true ,
194+ value : "author:@me " ,
195+ enabled : this . state . options . authorMe ?? false ,
99196 } ,
100197 {
101- value : "sort:updated-desc" ,
102- enabled : true ,
198+ value : "assignee:@me" ,
199+ enabled : this . state . options . assignedMe ?? true ,
200+ } ,
201+ {
202+ value : "review-requested:@me" ,
203+ enabled : this . state . options . reviewRequested ?? false ,
103204 } ,
104- [
105- {
106- value : "is:pr" ,
107- enabled : this . state . type === "pull-requests" ,
108- } ,
109- {
110- value : "is:issue" ,
111- enabled : this . state . type === "issues" ,
112- } ,
113- ] ,
114- [
115- {
116- value : "author:@me" ,
117- enabled : this . state . options . authorMe ?? false ,
118- } ,
119- {
120- value : "assignee:@me" ,
121- enabled : this . state . options . assignedMe ?? true ,
122- } ,
123- {
124- value : "review-requested:@me" ,
125- enabled : this . state . options . reviewRequested ?? false ,
126- } ,
127- ] ,
128205 ] ;
129206
130207 const excluded = this . state . options . repoExcludes ;
131208 for ( const repo of excluded ) {
132209 if ( repo && repo . trim ( ) ) {
133- options . push ( { value : `-repo:${ repo . trim ( ) } ` , enabled : true } ) ;
210+ baseQuery . push ( `-repo:${ repo . trim ( ) } ` ) ;
134211 }
135212 }
136213
137- let outputString = "" ;
214+ const queries = [ ] ;
138215 for ( const option of options ) {
139- if ( Array . isArray ( option ) ) {
140- const enabledOptions = option . filter ( x => x . enabled ) . map ( x => x . value ) ;
141- if ( enabledOptions . length ) {
142- outputString += ` (${ enabledOptions . join ( " OR " ) } ) ` ;
143- }
144- continue ;
216+ if ( option . enabled ) {
217+ queries . push ( option . value ) ;
145218 }
219+ }
146220
147- if ( option . enabled ) {
148- outputString += ` ${ option . value } ` ;
221+ const searchParams = [ ] ;
222+ if ( this . state . type === "pull-requests" ) {
223+ for ( const query of queries ) {
224+ searchParams . push ( `${ baseQuery . join ( " " ) } ${ query } ` ) ;
149225 }
226+
227+ return searchParams ;
150228 }
151229
152- searchParams . set ( "q" , outputString . trim ( ) . replace ( / + (? = ) / g , "" ) ) ;
153- return searchParams . toString ( ) ;
230+ // type: issues
231+ return [ ` ${ baseQuery . join ( " " ) } ${ queries . join ( " OR " ) } ` ] ;
154232 }
155233
156234 get options ( ) {
0 commit comments