@@ -28,10 +28,12 @@ function allIncluded(outputTarget = 'email') {
28
28
let showClosedLabel = true ;
29
29
let userReason = '' ;
30
30
31
- let pr_merged_button =
32
- '<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #6f42c1;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--purple">closed</div>' ;
33
- let pr_unmerged_button =
31
+ let pr_open_button =
34
32
'<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #2cbe4e;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--green">open</div>' ;
33
+ let pr_closed_button =
34
+ '<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #d73a49;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--red">closed</div>' ;
35
+ let pr_merged_button =
36
+ '<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #6f42c1;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--purple">merged</div>' ;
35
37
36
38
let issue_closed_button =
37
39
'<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #6f42c1;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--purple">closed</div>' ;
@@ -119,13 +121,13 @@ function allIncluded(outputTarget = 'email') {
119
121
}
120
122
if ( ! items . showOpenLabel ) {
121
123
showOpenLabel = false ;
122
- pr_unmerged_button = '' ;
124
+ pr_open_button = '' ;
123
125
issue_opened_button = '' ;
124
126
}
125
127
if ( ! items . showClosedLabel ) {
126
128
showClosedLabel = false ;
127
- pr_merged_button = '' ;
128
-
129
+ pr_closed_button = '' ;
130
+
129
131
}
130
132
if ( items . userReason ) {
131
133
userReason = items . userReason ;
@@ -708,20 +710,29 @@ ${userReason}`;
708
710
return Math . ceil ( ( d2 - d1 ) / ( 1000 * 60 * 60 * 24 ) ) ;
709
711
}
710
712
711
- // Helper to fetch PR details for merged status
712
- async function fetchPrMergedStatus ( owner , repo , number , headers ) {
713
+ // Session cache for merged status
714
+ let sessionMergedStatusCache = { } ;
715
+
716
+ // Helper to fetch PR details for merged status (REST, single PR)
717
+ async function fetchPrMergedStatusREST ( owner , repo , number , headers ) {
718
+ const cacheKey = `${ owner } /${ repo } #${ number } ` ;
719
+ if ( sessionMergedStatusCache [ cacheKey ] !== undefined ) {
720
+ return sessionMergedStatusCache [ cacheKey ] ;
721
+ }
713
722
const url = `https://api.github.com/repos/${ owner } /${ repo } /pulls/${ number } ` ;
714
723
try {
715
724
const res = await fetch ( url , { headers } ) ;
716
725
if ( ! res . ok ) return null ;
717
726
const data = await res . json ( ) ;
718
- return data . merged_at ? true : false ;
727
+ const merged = ! ! data . merged_at ;
728
+ sessionMergedStatusCache [ cacheKey ] = merged ;
729
+ return merged ;
719
730
} catch ( e ) {
720
731
return null ;
721
732
}
722
733
}
723
734
724
- // Update: make this async to allow merged status fetch and fallback
735
+ // Refactor writeGithubIssuesPrs to implement the new logic
725
736
async function writeGithubIssuesPrs ( ) {
726
737
let items = githubIssuesData . items ;
727
738
lastWeekArray = [ ] ;
@@ -735,7 +746,42 @@ ${userReason}`;
735
746
let useMergedStatus = false ;
736
747
let fallbackToSimple = false ;
737
748
let daysRange = getDaysBetween ( startingDate , endingDate ) ;
738
- if ( daysRange <= 14 ) useMergedStatus = true ;
749
+ if ( daysRange <= 7 ) useMergedStatus = true ;
750
+
751
+ // Collect PRs to batch fetch merged status
752
+ let prsToCheck = [ ] ;
753
+ for ( let i = 0 ; i < items . length ; i ++ ) {
754
+ let item = items [ i ] ;
755
+ if ( item . pull_request && item . state === 'closed' && useMergedStatus && ! fallbackToSimple ) {
756
+ let repository_url = item . repository_url ;
757
+ let repoParts = repository_url . split ( '/' ) ;
758
+ let owner = repoParts [ repoParts . length - 2 ] ;
759
+ let repo = repoParts [ repoParts . length - 1 ] ;
760
+ prsToCheck . push ( { owner, repo, number : item . number , idx : i } ) ;
761
+ }
762
+ }
763
+
764
+ let mergedStatusResults = { } ;
765
+ if ( githubToken ) {
766
+ // Use GraphQL batching for all cases
767
+ if ( prsToCheck . length > 0 ) {
768
+ mergedStatusResults = await fetchPrsMergedStatusBatch ( prsToCheck , headers ) ;
769
+ }
770
+ } else if ( useMergedStatus ) {
771
+ if ( prsToCheck . length > 30 ) {
772
+ fallbackToSimple = true ;
773
+ if ( typeof Materialize !== 'undefined' && Materialize . toast ) {
774
+ Materialize . toast ( 'API limit exceeded. Please use a GitHub token for full status. Showing only open/closed PRs.' , 5000 ) ;
775
+ }
776
+ } else {
777
+ // Use REST API for each PR, cache results
778
+ for ( let pr of prsToCheck ) {
779
+ let merged = await fetchPrMergedStatusREST ( pr . owner , pr . repo , pr . number , headers ) ;
780
+ mergedStatusResults [ `${ pr . owner } /${ pr . repo } #${ pr . number } ` ] = merged ;
781
+ }
782
+ }
783
+ }
784
+
739
785
for ( let i = 0 ; i < items . length ; i ++ ) {
740
786
let item = items [ i ] ;
741
787
let html_url = item . html_url ;
@@ -746,29 +792,23 @@ ${userReason}`;
746
792
let li = '' ;
747
793
if ( item . pull_request ) {
748
794
if ( item . state === 'open' ) {
749
- li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_unmerged_button } </li>` ;
795
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_open_button } </li>` ;
750
796
} else if ( item . state === 'closed' ) {
751
- if ( useMergedStatus && ! fallbackToSimple ) {
752
- let owner = repository_url . split ( '/' ) [ repository_url . split ( '/' ) . length - 2 ] ;
753
- let merged = null ;
754
- try {
755
- merged = await fetchPrMergedStatus ( owner , project , number , headers ) ;
756
- if ( merged === true ) {
757
- li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_true_button } </li>` ;
758
- } else if ( merged === false ) {
759
- li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_false_button } </li>` ;
760
- } else {
761
- li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_false_button } </li>` ;
762
- }
763
- } catch ( e ) {
764
- fallbackToSimple = true ;
797
+ if ( ( githubToken || ( useMergedStatus && ! fallbackToSimple ) ) && mergedStatusResults ) {
798
+ let repository_url = item . repository_url ;
799
+ let repoParts = repository_url . split ( '/' ) ;
800
+ let owner = repoParts [ repoParts . length - 2 ] ;
801
+ let repo = repoParts [ repoParts . length - 1 ] ;
802
+ let merged = mergedStatusResults [ `${ owner } /${ repo } #${ number } ` ] ;
803
+ if ( merged === true ) {
765
804
li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_button } </li>` ;
766
- if ( typeof Materialize !== 'undefined' && Materialize . toast ) {
767
- Materialize . toast ( 'API limit exceeded or error occurred. Please use a GitHub token for higher limits.' , 5000 ) ;
768
- }
805
+ } else if ( merged === false ) {
806
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
807
+ } else {
808
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
769
809
}
770
810
} else {
771
- li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_button } </li>` ;
811
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
772
812
}
773
813
}
774
814
} else {
@@ -845,16 +885,20 @@ ${userReason}`;
845
885
846
886
//check for github safe writing
847
887
let intervalWriteGithubIssues = setInterval ( async ( ) => {
848
- if ( outputTarget === 'popup' ) {
849
- if ( githubUsername && githubIssuesData ) {
850
- clearInterval ( intervalWriteGithubIssues ) ;
851
- await writeGithubIssuesPrs ( ) ;
852
- }
853
- } else {
854
- if ( scrumBody && githubUsername && githubIssuesData ) {
855
- clearInterval ( intervalWriteGithubIssues ) ;
856
- await writeGithubIssuesPrs ( ) ;
888
+ try {
889
+ if ( outputTarget === 'popup' ) {
890
+ if ( githubUsername && githubIssuesData ) {
891
+ clearInterval ( intervalWriteGithubIssues ) ;
892
+ await writeGithubIssuesPrs ( ) ;
893
+ }
894
+ } else {
895
+ if ( scrumBody && githubUsername && githubIssuesData ) {
896
+ clearInterval ( intervalWriteGithubIssues ) ;
897
+ await writeGithubIssuesPrs ( ) ;
898
+ }
857
899
}
900
+ } catch ( err ) {
901
+ logError ( 'Interval writeGithubIssuesPrs error:' , err ) ;
858
902
}
859
903
} , 500 ) ;
860
904
let intervalWriteGithubPrs = setInterval ( ( ) => {
@@ -914,4 +958,161 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
914
958
} ) ;
915
959
return true ;
916
960
}
917
- } ) ;
961
+ } ) ;
962
+
963
+ // Replace all references to pr_unmerged_button, pr_merged_true_button, pr_merged_false_button with unified names
964
+ // ... existing code ...
965
+ // Refactor fetchPrMergedStatus to batch requests
966
+ async function fetchPrsMergedStatusBatch ( prs , headers ) {
967
+ // prs: Array of {owner, repo, number}
968
+ const results = { } ;
969
+ if ( prs . length === 0 ) return results ;
970
+ // Use GitHub GraphQL API for batching
971
+ const query = `query {
972
+ ${ prs . map ( ( pr , i ) => ` repo${ i } : repository(owner: \"${ pr . owner } \", name: \"${ pr . repo } \") {
973
+ pr${ i } : pullRequest(number: ${ pr . number } ) { merged }
974
+ }` ) . join ( '\n' ) }
975
+ }` ;
976
+ try {
977
+ const res = await fetch ( 'https://api.github.com/graphql' , {
978
+ method : 'POST' ,
979
+ headers : {
980
+ ...headers ,
981
+ 'Content-Type' : 'application/json' ,
982
+ } ,
983
+ body : JSON . stringify ( { query } ) ,
984
+ } ) ;
985
+ if ( ! res . ok ) return results ;
986
+ const data = await res . json ( ) ;
987
+ prs . forEach ( ( pr , i ) => {
988
+ const merged = data . data [ `repo${ i } ` ] ?. [ `pr${ i } ` ] ?. merged ;
989
+ results [ `${ pr . owner } /${ pr . repo } #${ pr . number } ` ] = merged ;
990
+ } ) ;
991
+ return results ;
992
+ } catch ( e ) {
993
+ return results ;
994
+ }
995
+ }
996
+ // ... existing code ...
997
+ // Refactor writeGithubIssuesPrs to batch merged status requests
998
+ async function writeGithubIssuesPrs ( ) {
999
+ let items = githubIssuesData . items ;
1000
+ lastWeekArray = [ ] ;
1001
+ nextWeekArray = [ ] ;
1002
+ if ( ! items ) {
1003
+ logError ( 'No Github issues data available' ) ;
1004
+ return ;
1005
+ }
1006
+ const headers = { 'Accept' : 'application/vnd.github.v3+json' } ;
1007
+ if ( githubToken ) headers [ 'Authorization' ] = `token ${ githubToken } ` ;
1008
+ let useMergedStatus = false ;
1009
+ let fallbackToSimple = false ;
1010
+ let daysRange = getDaysBetween ( startingDate , endingDate ) ;
1011
+ if ( daysRange <= 14 ) useMergedStatus = true ;
1012
+
1013
+ // Collect PRs to batch fetch merged status
1014
+ let prsToCheck = [ ] ;
1015
+ for ( let i = 0 ; i < items . length ; i ++ ) {
1016
+ let item = items [ i ] ;
1017
+ if ( item . pull_request && item . state === 'closed' && useMergedStatus && ! fallbackToSimple ) {
1018
+ let repository_url = item . repository_url ;
1019
+ let repoParts = repository_url . split ( '/' ) ;
1020
+ let owner = repoParts [ repoParts . length - 2 ] ;
1021
+ let repo = repoParts [ repoParts . length - 1 ] ;
1022
+ prsToCheck . push ( { owner, repo, number : item . number , idx : i } ) ;
1023
+ }
1024
+ }
1025
+ let mergedStatusResults = { } ;
1026
+ if ( prsToCheck . length > 0 && githubToken ) {
1027
+ mergedStatusResults = await fetchPrsMergedStatusBatch ( prsToCheck , headers ) ;
1028
+ }
1029
+
1030
+ for ( let i = 0 ; i < items . length ; i ++ ) {
1031
+ let item = items [ i ] ;
1032
+ let html_url = item . html_url ;
1033
+ let repository_url = item . repository_url ;
1034
+ let project = repository_url . substr ( repository_url . lastIndexOf ( '/' ) + 1 ) ;
1035
+ let title = item . title ;
1036
+ let number = item . number ;
1037
+ let li = '' ;
1038
+ if ( item . pull_request ) {
1039
+ if ( item . state === 'open' ) {
1040
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_open_button } </li>` ;
1041
+ } else if ( item . state === 'closed' ) {
1042
+ if ( useMergedStatus && ! fallbackToSimple && githubToken ) {
1043
+ let repository_url = item . repository_url ;
1044
+ let repoParts = repository_url . split ( '/' ) ;
1045
+ let owner = repoParts [ repoParts . length - 2 ] ;
1046
+ let repo = repoParts [ repoParts . length - 1 ] ;
1047
+ let merged = mergedStatusResults [ `${ owner } /${ repo } #${ number } ` ] ;
1048
+ if ( merged === true ) {
1049
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_merged_button } </li>` ;
1050
+ } else if ( merged === false ) {
1051
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
1052
+ } else {
1053
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
1054
+ }
1055
+ } else {
1056
+ li = `<li><i>(${ project } )</i> - Made PR (#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ pr_closed_button } </li>` ;
1057
+ }
1058
+ }
1059
+ } else {
1060
+ // is a issue
1061
+ if ( item . state === 'open' && item . body ?. toUpperCase ( ) . indexOf ( 'YES' ) > 0 ) {
1062
+ let li2 =
1063
+ '<li><i>(' +
1064
+ project +
1065
+ ')</i> - Work on Issue(#' +
1066
+ number +
1067
+ ") - <a href='" +
1068
+ html_url +
1069
+ "' target='_blank'>" +
1070
+ title +
1071
+ '</a> ' +
1072
+ issue_opened_button +
1073
+ ' </li>' ;
1074
+ nextWeekArray . push ( li2 ) ;
1075
+ }
1076
+ if ( item . state === 'open' ) {
1077
+ li = `<li><i>(${ project } )</i> - Opened Issue(#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ issue_opened_button } </li>` ;
1078
+ } else if ( item . state === 'closed' ) {
1079
+ // Always show closed label for closed issues
1080
+ li = `<li><i>(${ project } )</i> - Opened Issue(#${ number } ) - <a href='${ html_url } '>${ title } </a> ${ issue_closed_button } </li>` ;
1081
+ } else {
1082
+ li =
1083
+ '<li><i>(' +
1084
+ project +
1085
+ ')</i> - Opened Issue(#' +
1086
+ number +
1087
+ ") - <a href='" +
1088
+ html_url +
1089
+ "' target='_blank'>" +
1090
+ title +
1091
+ '</a> </li>' ;
1092
+ }
1093
+ }
1094
+ lastWeekArray . push ( li ) ;
1095
+ }
1096
+ issuesDataProcessed = true ;
1097
+ triggerScrumGeneration ( ) ;
1098
+ }
1099
+ // ... existing code ...
1100
+ // Wrap setInterval async handlers in try/catch
1101
+ let intervalWriteGithubIssues = setInterval ( async ( ) => {
1102
+ try {
1103
+ if ( outputTarget === 'popup' ) {
1104
+ if ( githubUsername && githubIssuesData ) {
1105
+ clearInterval ( intervalWriteGithubIssues ) ;
1106
+ await writeGithubIssuesPrs ( ) ;
1107
+ }
1108
+ } else {
1109
+ if ( scrumBody && githubUsername && githubIssuesData ) {
1110
+ clearInterval ( intervalWriteGithubIssues ) ;
1111
+ await writeGithubIssuesPrs ( ) ;
1112
+ }
1113
+ }
1114
+ } catch ( err ) {
1115
+ logError ( 'Interval writeGithubIssuesPrs error:' , err ) ;
1116
+ }
1117
+ } , 500 ) ;
1118
+ // ... existing code ...
0 commit comments