diff --git a/src/manifest.json b/src/manifest.json
index 69eaa16..5565687 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -11,8 +11,11 @@
"96": "icons/icon.png",
"48": "icons/icon.png"
},
- "background": {
- "service_worker": "scripts/background.js"
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "scrum-helper@fossasia.org",
+ "strict_min_version": "109.0"
+ }
},
"content_scripts": [
{
@@ -25,7 +28,12 @@
"*://outlook.office.com/*",
"*://mail.yahoo.com/*"
],
- "js": ["scripts/jquery-3.2.1.min.js", "scripts/emailClientAdapter.js", "scripts/scrumHelper.js"]
+ "js": [
+ "scripts/browser-polyfill.js",
+ "scripts/jquery-3.2.1.min.js",
+ "scripts/emailClientAdapter.js",
+ "scripts/scrumHelper.js"
+ ]
}
],
diff --git a/src/popup.html b/src/popup.html
index 522ee84..5f8e5cf 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -260,6 +260,7 @@
Note:
+
diff --git a/src/scripts/background.js b/src/scripts/background.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/scripts/browser-polyfill.js b/src/scripts/browser-polyfill.js
new file mode 100644
index 0000000..688bd56
--- /dev/null
+++ b/src/scripts/browser-polyfill.js
@@ -0,0 +1,4 @@
+// scripts/browser-polyfill.js
+if (typeof browser === "undefined") {
+ window.browser = typeof chrome !== "undefined" ? chrome : {};
+}
\ No newline at end of file
diff --git a/src/scripts/main.js b/src/scripts/main.js
index 383e6e2..bf23580 100644
--- a/src/scripts/main.js
+++ b/src/scripts/main.js
@@ -12,7 +12,7 @@ let userReasonElement = document.getElementById('userReason');
let showCommitsElement = document.getElementById('showCommits');
function handleBodyOnLoad() {
- chrome.storage.local.get(
+ browser.storage.local.get(
[
'githubUsername',
'projectName',
@@ -102,7 +102,7 @@ document.getElementById('refreshCache').addEventListener('click', async (e) => {
});
// Reload the active tab to re-inject content
- chrome.tabs.reload(tabs[0].id);
+ browser.tabs.reload(tabs[0].id);
Materialize.toast({ html: 'Data refreshed successfully!', classes: 'green' });
} catch (err) {
@@ -116,16 +116,16 @@ document.getElementById('refreshCache').addEventListener('click', async (e) => {
});
function handleEnableChange() {
- let value = enableToggleElement.checked;
- chrome.storage.local.set({ enableToggle: value });
+ var value = enableToggleElement.checked;
+ browser.storage.local.set({ enableToggle: value });
}
function handleStartingDateChange() {
- let value = startingDateElement.value;
- chrome.storage.local.set({ startingDate: value });
+ var value = startingDateElement.value;
+ browser.storage.local.set({ startingDate: value });
}
function handleEndingDateChange() {
- let value = endingDateElement.value;
- chrome.storage.local.set({ endingDate: value });
+ var value = endingDateElement.value;
+ browser.storage.local.set({ endingDate: value });
}
function handleLastWeekContributionChange() {
let value = lastWeekContributionElement.checked;
@@ -145,8 +145,7 @@ function handleLastWeekContributionChange() {
labelElement.classList.add("unselectedLabel");
labelElement.classList.remove("selectedLabel");
}
-
- chrome.storage.local.set({ lastWeekContribution: value });
+ browser.storage.local.set({ lastWeekContribution: value });
}
function handleYesterdayContributionChange() {
@@ -168,7 +167,7 @@ function handleYesterdayContributionChange() {
labelElement.classList.add("unselectedLabel");
labelElement.classList.remove("selectedLabel");
}
- chrome.storage.local.set({ yesterdayContribution: value });
+ browser.storage.local.set({ yesterdayContribution: value });
}
function getLastWeek() {
@@ -215,20 +214,20 @@ function getToday() {
}
function handleGithubUsernameChange() {
- let value = githubUsernameElement.value;
- chrome.storage.local.set({ githubUsername: value });
+ var value = githubUsernameElement.value;
+ browser.storage.local.set({ githubUsername: value });
}
function handleGithubTokenChange() {
let value = githubTokenElement.value;
- chrome.storage.local.set({ githubToken: value });
+ browser.storage.local.set({ githubToken: value });
}
function handleProjectNameChange() {
- let value = projectNameElement.value;
- chrome.storage.local.set({ projectName: value });
+ var value = projectNameElement.value;
+ browser.storage.local.set({ projectName: value });
}
function handleCacheInputChange() {
let value = cacheInputElement.value;
- chrome.storage.local.set({ cacheInput: value });
+ browser.storage.local.set({ cacheInput: value });
}
function handleOpenLabelChange() {
let value = showOpenLabelElement.checked;
@@ -242,17 +241,17 @@ function handleOpenLabelChange() {
labelElement.classList.remove("selectedLabel");
}
- chrome.storage.local.set({ showOpenLabel: value });
+ browser.storage.local.set({ showOpenLabel: value });
}
function handleUserReasonChange() {
- let value = userReasonElement.value;
- chrome.storage.local.set({ userReason: value });
+ var value = userReasonElement.value;
+ browser.storage.local.set({ userReason: value });
}
function handleShowCommitsChange() {
let value = showCommitsElement.checked;
- chrome.storage.local.set({ showCommits: value });
+ browser.storage.local.set({ showCommits: value });
}
enableToggleElement.addEventListener('change', handleEnableChange);
@@ -267,4 +266,4 @@ lastWeekContributionElement.addEventListener('change', handleLastWeekContributio
yesterdayContributionElement.addEventListener('change', handleYesterdayContributionChange);
showOpenLabelElement.addEventListener('change', handleOpenLabelChange);
userReasonElement.addEventListener('keyup', handleUserReasonChange);
-document.addEventListener('DOMContentLoaded', handleBodyOnLoad);
\ No newline at end of file
+document.addEventListener('DOMContentLoaded', handleBodyOnLoad);
diff --git a/src/scripts/popup.js b/src/scripts/popup.js
index 855da2a..70f6085 100644
--- a/src/scripts/popup.js
+++ b/src/scripts/popup.js
@@ -52,6 +52,8 @@ document.addEventListener('DOMContentLoaded', function () {
const settingsToggle = document.getElementById('settingsToggle');
const reportSection = document.getElementById('reportSection');
const settingsSection = document.getElementById('settingsSection');
+ const orgInput = document.getElementById('orgInput');
+ const setOrgBtn = document.getElementById('setOrgBtn');
let isSettingsVisible = false;
const githubTokenInput = document.getElementById('githubToken');
@@ -59,15 +61,15 @@ document.addEventListener('DOMContentLoaded', function () {
const tokenEyeIcon = document.getElementById('tokenEyeIcon');
let tokenVisible = false;
- const orgInput = document.getElementById('orgInput');
- const setOrgBtn = document.getElementById('setOrgBtn');
-
- chrome.storage.local.get(['darkMode'], function (result) {
+ // Use browser-agnostic storage API
+ const storage = chrome.storage || browser.storage;
+
+ storage.local.get(['darkMode'], function (result) {
if (result.darkMode) {
body.classList.add('dark-mode');
darkModeToggle.src = 'icons/light-mode.png';
if (settingsIcon) {
- settingsIcon.src = 'icons/settings-night.png'; // Changed from settings-night.png
+ settingsIcon.src = 'icons/settings-night.png';
}
}
});
@@ -87,7 +89,7 @@ document.addEventListener('DOMContentLoaded', function () {
darkModeToggle.addEventListener('click', function () {
body.classList.toggle('dark-mode');
const isDarkMode = body.classList.contains('dark-mode');
- chrome.storage.local.set({ darkMode: isDarkMode });
+ storage.local.set({ darkMode: isDarkMode });
this.src = isDarkMode ? 'icons/light-mode.png' : 'icons/night-mode.png';
const settingsIcon = document.getElementById('settingsIcon');
if (settingsIcon) {
@@ -129,7 +131,6 @@ document.addEventListener('DOMContentLoaded', function () {
'githubToken',
'projectName',
'settingsToggle',
-
];
const radios = document.querySelectorAll('input[name="timeframe"]');
@@ -163,7 +164,6 @@ document.addEventListener('DOMContentLoaded', function () {
}
});
-
if (customDateContainer) {
if (!enableToggle) {
customDateContainer.style.opacity = '0.5';
@@ -188,7 +188,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
}
- chrome.storage.local.get(['enableToggle'], (items) => {
+ storage.local.get(['enableToggle'], (items) => {
const enableToggle = items.enableToggle !== false;
updateContentState(enableToggle);
if (!enableToggle) {
@@ -198,29 +198,25 @@ document.addEventListener('DOMContentLoaded', function () {
initializePopup();
})
- chrome.storage.onChanged.addListener((changes, namespace) => {
+ storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'local' && changes.enableToggle) {
updateContentState(changes.enableToggle.newValue);
if (changes.enableToggle.newValue) {
- // re-initialize if enabled
initializePopup();
}
}
});
function initializePopup() {
-
- // Button setup
const generateBtn = document.getElementById('generateReport');
const copyBtn = document.getElementById('copyReport');
generateBtn.addEventListener('click', function () {
- // Check org input value before generating report
let org = orgInput.value.trim().toLowerCase();
if (!org) {
org = 'fossasia';
}
- chrome.storage.local.set({ orgName: org }, () => {
+ storage.local.set({ orgName: org }, () => {
generateBtn.innerHTML = ' Generating...';
generateBtn.disabled = true;
window.generateScrumReport();
@@ -267,14 +263,14 @@ document.addEventListener('DOMContentLoaded', function () {
startDateInput.readOnly = false;
endDateInput.readOnly = false;
- chrome.storage.local.set({
+ storage.local.set({
lastWeekContribution: false,
yesterdayContribution: false,
selectedTimeframe: null
});
});
- chrome.storage.local.get([
+ storage.local.get([
'selectedTimeframe',
'lastWeekContribution',
'yesterdayContribution',
@@ -283,13 +279,11 @@ document.addEventListener('DOMContentLoaded', function () {
], (items) => {
console.log('Restoring state:', items);
-
if (items.startingDate && items.endingDate && !items.lastWeekContribution && !items.yesterdayContribution) {
const startDateInput = document.getElementById('startingDate');
const endDateInput = document.getElementById('endingDate');
if (startDateInput && endDateInput) {
-
startDateInput.value = items.startingDate;
endDateInput.value = items.endingDate;
startDateInput.readOnly = false;
@@ -325,7 +319,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
startDateInput.readOnly = endDateInput.readOnly = true;
- chrome.storage.local.set({
+ storage.local.set({
startingDate: startDateInput.value,
endingDate: endDateInput.value,
lastWeekContribution: items.selectedTimeframe === 'lastWeekContribution',
@@ -341,7 +335,6 @@ document.addEventListener('DOMContentLoaded', function () {
reportSection.classList.remove('hidden');
settingsSection.classList.add('hidden');
settingsToggle.classList.remove('active');
- console.log('Switched to report view');
}
function showSettingsView() {
@@ -349,7 +342,6 @@ document.addEventListener('DOMContentLoaded', function () {
reportSection.classList.add('hidden');
settingsSection.classList.remove('hidden');
settingsToggle.classList.add('active');
- console.log('Switched to settings view');
}
if (settingsToggle) {
@@ -372,7 +364,7 @@ document.addEventListener('DOMContentLoaded', function () {
showReportView();
// Load org from storage or default
- chrome.storage.local.get(['orgName'], function (result) {
+ storage.local.get(['orgName'], function (result) {
orgInput.value = result.orgName || '';
});
@@ -382,8 +374,8 @@ document.addEventListener('DOMContentLoaded', function () {
if (!org) {
org = 'fossasia';
}
- chrome.storage.local.set({ orgName: org }, function () {
- chrome.storage.local.remove('githubCache'); // Clear cache on org change
+ storage.local.set({ orgName: org }, function () {
+ storage.local.remove('githubCache');
});
});
@@ -425,17 +417,14 @@ document.addEventListener('DOMContentLoaded', function () {
}
const oldToast = document.getElementById('invalid-org-toast');
if (oldToast) oldToast.parentNode.removeChild(oldToast);
- chrome.storage.local.set({ orgName: org }, function () {
- // Always clear the scrum report and show org changed message
+ storage.local.set({ orgName: org }, function () {
const scrumReport = document.getElementById('scrumReport');
if (scrumReport) {
scrumReport.innerHTML = 'Organization changed. Click Generate button to fetch the GitHub activities.
';
}
- // Clear the githubCache for previous org
- chrome.storage.local.remove('githubCache');
+ storage.local.remove('githubCache');
setOrgBtn.disabled = false;
setOrgBtn.innerHTML = originalText;
- // Always show green toast: org is set
const toastDiv = document.createElement('div');
toastDiv.id = 'invalid-org-toast';
toastDiv.className = 'toast';
@@ -484,7 +473,7 @@ document.addEventListener('DOMContentLoaded', function () {
let cacheInput = document.getElementById('cacheInput');
if (cacheInput) {
- chrome.storage.local.get(['cacheInput'], function (result) {
+ storage.local.get(['cacheInput'], function (result) {
if (result.cacheInput) {
cacheInput.value = result.cacheInput;
} else {
@@ -506,13 +495,11 @@ document.addEventListener('DOMContentLoaded', function () {
this.style.borderColor = '#10b981';
}
- chrome.storage.local.set({ cacheInput: ttlValue }, function () {
+ storage.local.set({ cacheInput: ttlValue }, function () {
console.log('Cache TTL saved:', ttlValue, 'minutes');
});
});
-
}
-
});
// Tooltip bubble
@@ -567,7 +554,8 @@ document.querySelectorAll('input[name="timeframe"]').forEach(radio => {
startDateInput.readOnly = false;
endDateInput.readOnly = false;
- chrome.storage.local.set({
+ const storage = chrome.storage || browser.storage;
+ storage.local.set({
lastWeekContribution: false,
yesterdayContribution: false,
selectedTimeframe: null
@@ -592,10 +580,7 @@ document.getElementById('refreshCache').addEventListener('click', async function
button.disabled = true;
try {
- // Clear local cache
await forceGithubDataRefresh();
-
- // Clear the scrum report
const scrumReport = document.getElementById('scrumReport');
if (scrumReport) {
scrumReport.innerHTML = 'Cache cleared successfully. Click "Generate Report" to fetch fresh data.
';
@@ -608,7 +593,6 @@ document.getElementById('refreshCache').addEventListener('click', async function
button.innerHTML = originalText;
button.disabled = false;
}, 2000);
-
} catch (error) {
console.error('Cache clear failed:', error);
button.innerHTML = 'Failed to clear cache';
@@ -624,8 +608,7 @@ document.getElementById('refreshCache').addEventListener('click', async function
function toggleRadio(radio) {
const startDateInput = document.getElementById('startingDate');
const endDateInput = document.getElementById('endingDate');
-
- console.log('Toggling radio:', radio.id);
+ const storage = chrome.storage || browser.storage;
if (radio.id === 'lastWeekContribution') {
startDateInput.value = getLastWeek();
@@ -637,19 +620,12 @@ function toggleRadio(radio) {
startDateInput.readOnly = endDateInput.readOnly = true;
- chrome.storage.local.set({
+ storage.local.set({
startingDate: startDateInput.value,
endingDate: endDateInput.value,
lastWeekContribution: radio.id === 'lastWeekContribution',
yesterdayContribution: radio.id === 'yesterdayContribution',
selectedTimeframe: radio.id,
- githubCache: null // Clear cache to force new fetch
- }, () => {
- console.log('State saved, dates:', {
- start: startDateInput.value,
- end: endDateInput.value,
- isLastWeek: radio.id === 'lastWeekContribution'
- });
+ githubCache: null
});
-}
-
+}
\ No newline at end of file
diff --git a/src/scripts/scrumHelper.js b/src/scripts/scrumHelper.js
index 3aebad4..f9f4a99 100644
--- a/src/scripts/scrumHelper.js
+++ b/src/scripts/scrumHelper.js
@@ -9,20 +9,27 @@ function logError(...args) {
console.error('[SCRUM-HELPER]:', ...args);
}
}
+
console.log('Script loaded, adapter exists:', !!window.emailClientAdapter);
+
let refreshButton_Placed = false;
let enableToggle = true;
let hasInjectedContent = false;
let scrumGenerationInProgress = false;
-let orgName = 'fossasia'; // default
+let orgName = 'fossasia';
+
+// Unified storage API for Chrome/Firefox compatibility
+const storage = chrome.storage || browser.storage;
+
function allIncluded(outputTarget = 'email') {
if (scrumGenerationInProgress) {
- console.warn('[SCRUM-HELPER]: Scrum generation already in progress, aborting new call.');
+ log('Scrum generation already in progress, aborting new call');
return;
}
scrumGenerationInProgress = true;
- console.log('allIncluded called with outputTarget:', outputTarget);
- console.log('Current window context:', window.location.href);
+ log('allIncluded called with outputTarget:', outputTarget);
+ log('Current window context:', window.location.href);
+
let scrumBody = null;
let scrumSubject = null;
let startingDate = '';
@@ -45,25 +52,17 @@ function allIncluded(outputTarget = 'email') {
let showCommits = false;
let userReason = '';
- let pr_open_button =
- 'open
';
- let pr_closed_button =
- 'closed
';
- let pr_merged_button =
- 'merged
';
- let pr_draft_button =
- 'draft
';
-
- let issue_closed_button =
- 'closed
';
- let issue_opened_button =
- 'open
';
-
-
- // let linkStyle = '';
- function getChromeData() {
- console.log("Getting Chrome data for context:", outputTarget);
- chrome.storage.local.get(
+ // Button styles
+ let pr_open_button = 'open
';
+ let pr_closed_button = 'closed
';
+ let pr_merged_button = 'merged
';
+ let pr_draft_button = 'draft
';
+ let issue_closed_button = 'closed
';
+ let issue_opened_button = 'open
';
+
+ function getBrowserData() {
+ log("Getting browser data for context:", outputTarget);
+ storage.local.get(
[
'githubUsername',
'githubToken',
@@ -82,9 +81,9 @@ function allIncluded(outputTarget = 'email') {
'orgName'
],
(items) => {
- console.log("Storage items received:", items);
-
+ log("Storage items received:", items);
+ // Handle popup UI elements
if (outputTarget === 'popup') {
const usernameFromDOM = document.getElementById('githubUsername')?.value;
const projectFromDOM = document.getElementById('projectName')?.value;
@@ -96,7 +95,7 @@ function allIncluded(outputTarget = 'email') {
items.userReason = reasonFromDOM || items.userReason;
items.githubToken = tokenFromDOM || items.githubToken;
- chrome.storage.local.set({
+ storage.local.set({
githubUsername: items.githubUsername,
projectName: items.projectName,
userReason: items.userReason,
@@ -104,6 +103,7 @@ function allIncluded(outputTarget = 'email') {
});
}
+ // Set retrieved values
githubUsername = items.githubUsername;
projectName = items.projectName;
userReason = items.userReason || 'No Blocker at the moment';
@@ -111,10 +111,18 @@ function allIncluded(outputTarget = 'email') {
lastWeekContribution = items.lastWeekContribution;
yesterdayContribution = items.yesterdayContribution;
- if (!items.enableToggle) {
- enableToggle = items.enableToggle;
+ // Handle feature toggles
+ if (items.enableToggle !== undefined) enableToggle = items.enableToggle;
+ if (items.cacheInput) cacheInput = items.cacheInput;
+ if (items.showCommits !== undefined) showCommits = items.showCommits;
+ if (items.orgName) orgName = items.orgName;
+ if (items.showOpenLabel === false) {
+ showOpenLabel = false;
+ pr_open_button = '';
+ issue_opened_button = '';
}
+ // Handle date ranges
if (items.lastWeekContribution) {
handleLastWeekContributionChange();
} else if (items.yesterdayContribution) {
@@ -123,115 +131,75 @@ function allIncluded(outputTarget = 'email') {
startingDate = items.startingDate;
endingDate = items.endingDate;
} else {
- handleLastWeekContributionChange(); //on fresh unpack - default to last week.
+ handleLastWeekContributionChange(); // Default to last week
if (outputTarget === 'popup') {
- chrome.storage.local.set({ lastWeekContribution: true, yesterdayContribution: false });
+ storage.local.set({ lastWeekContribution: true, yesterdayContribution: false });
}
}
+
+ // Fetch data if username exists
if (githubUsername) {
- console.log("About to fetch GitHub data for:", githubUsername);
+ log("About to fetch GitHub data for:", githubUsername);
fetchGithubData();
} else {
- if (outputTarget === 'popup') {
- console.log("No username found - popup context");
- // Show error in popup
- const scrumReport = document.getElementById('scrumReport');
- const generateBtn = document.getElementById('generateReport');
- if (scrumReport) {
- scrumReport.innerHTML = 'Please enter your GitHub username to generate a report.
';
- }
- if (generateBtn) {
- generateBtn.innerHTML = ' Generate Report';
- generateBtn.disabled = false;
- }
- scrumGenerationInProgress = false;
- } else {
- console.warn('No GitHub username found in storage');
- scrumGenerationInProgress = false;
- }
- return;
- }
- if (items.cacheInput) {
- cacheInput = items.cacheInput;
- }
- if (items.showCommits !== undefined) {
- showCommits = items.showCommits;
- } else {
- showCommits = false; // Default value
+ handleMissingUsername();
}
-
-
- if (!items.showOpenLabel) {
- showOpenLabel = false;
- pr_unmerged_button = '';
- issue_opened_button = '';
- pr_merged_button = '';
- issue_closed_button = '';
- }
- if (items.githubCache) {
- githubCache.data = items.githubCache.data;
- githubCache.cacheKey = items.githubCache.cacheKey;
- githubCache.timestamp = items.githubCache.timestamp;
- log('Restored cache from storage');
- }
-
- if (items.orgName) {
- orgName = items.orgName;
- }
- },
+ }
);
}
- getChromeData();
+
+ function handleMissingUsername() {
+ if (outputTarget === 'popup') {
+ log("No username found - popup context");
+ const scrumReport = document.getElementById('scrumReport');
+ const generateBtn = document.getElementById('generateReport');
+ if (scrumReport) {
+ scrumReport.innerHTML = 'Please enter your GitHub username to generate a report.
';
+ }
+ if (generateBtn) {
+ generateBtn.innerHTML = ' Generate Report';
+ generateBtn.disabled = false;
+ }
+ scrumGenerationInProgress = false;
+ } else {
+ log('No GitHub username found in storage');
+ scrumGenerationInProgress = false;
+ }
+ }
function handleLastWeekContributionChange() {
endingDate = getToday();
startingDate = getLastWeek();
}
+
function handleYesterdayContributionChange() {
endingDate = getToday();
startingDate = getYesterday();
}
+
function getLastWeek() {
- let today = new Date();
- let lastWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
- let lastWeekMonth = lastWeek.getMonth() + 1;
- let lastWeekDay = lastWeek.getDate();
- let lastWeekYear = lastWeek.getFullYear();
- let lastWeekDisplayPadded =
- ('0000' + lastWeekYear.toString()).slice(-4) +
- '-' +
- ('00' + lastWeekMonth.toString()).slice(-2) +
- '-' +
- ('00' + lastWeekDay.toString()).slice(-2);
- return lastWeekDisplayPadded;
+ const today = new Date();
+ const lastWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
+ const lastWeekMonth = lastWeek.getMonth() + 1;
+ const lastWeekDay = lastWeek.getDate();
+ return `${lastWeek.getFullYear()}-${pad(lastWeekMonth)}-${pad(lastWeekDay)}`;
}
+
function getYesterday() {
- let today = new Date();
- let yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
- let yesterdayMonth = yesterday.getMonth() + 1;
- let yesterdayDay = yesterday.getDate();
- let yesterdayYear = yesterday.getFullYear();
- let yesterdayPadded =
- ('0000' + yesterdayYear.toString()).slice(-4) +
- '-' +
- ('00' + yesterdayMonth.toString()).slice(-2) +
- '-' +
- ('00' + yesterdayDay.toString()).slice(-2);
- return yesterdayPadded;
+ const today = new Date();
+ const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1);
+ const yesterdayMonth = yesterday.getMonth() + 1;
+ const yesterdayDay = yesterday.getDate();
+ return `${yesterday.getFullYear()}-${pad(yesterdayMonth)}-${pad(yesterdayDay)}`;
}
+
function getToday() {
- let today = new Date();
- let Week = new Date(today.getFullYear(), today.getMonth(), today.getDate());
- let WeekMonth = Week.getMonth() + 1;
- let WeekDay = Week.getDate();
- let WeekYear = Week.getFullYear();
- let WeekDisplayPadded =
- ('0000' + WeekYear.toString()).slice(-4) +
- '-' +
- ('00' + WeekMonth.toString()).slice(-2) +
- '-' +
- ('00' + WeekDay.toString()).slice(-2);
- return WeekDisplayPadded;
+ const today = new Date();
+ return `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`;
+ }
+
+ function pad(num) {
+ return num.toString().padStart(2, '0');
}
// Global cache object
@@ -242,21 +210,18 @@ function allIncluded(outputTarget = 'email') {
ttl: 10 * 60 * 1000, // cache valid for 10 mins
fetching: false,
queue: [],
- errors: {},
- errorTTL: 60 * 1000, // 1 min error cache
subject: null,
};
async function getCacheTTL() {
return new Promise((resolve) => {
- chrome.storage.local.get(['cacheInput'], function (result) {
+ storage.local.get(['cacheInput'], (result) => {
const ttlMinutes = result.cacheInput || 10;
resolve(ttlMinutes * 60 * 1000);
});
});
}
-
function saveToStorage(data, subject = null) {
const cacheData = {
data: data,
@@ -264,32 +229,19 @@ function allIncluded(outputTarget = 'email') {
timestamp: githubCache.timestamp,
subject: subject,
}
- log(`Saving data to storage:`, {
- cacheKey: githubCache.cacheKey,
- timestamp: githubCache.timestamp,
- hasSubject: !!subject,
- });
-
return new Promise((resolve) => {
- chrome.storage.local.set({ githubCache: cacheData }, () => {
- if (chrome.runtime.lastError) {
- logError('Storage save failed: ', chrome.runtime.lastError);
- resolve(false);
- } else {
- log('Cache saved successfuly');
- githubCache.data = data;
- githubCache.subject = subject;
- resolve(true);
- }
+ storage.local.set({ githubCache: cacheData }, () => {
+ githubCache.data = data;
+ githubCache.subject = subject;
+ resolve(true);
});
});
}
function loadFromStorage() {
- log('Loading cache from storage');
return new Promise(async (resolve) => {
const currentTTL = await getCacheTTL();
- chrome.storage.local.get('githubCache', (result) => {
+ storage.local.get('githubCache', (result) => {
const cache = result.githubCache;
if (!cache) {
log('No cache found in storage');
@@ -302,22 +254,10 @@ function allIncluded(outputTarget = 'email') {
resolve(false);
return;
}
- log('Found valid cache:', {
- cacheKey: cache.cacheKey,
- age: `${((Date.now() - cache.timestamp) / 1000 / 60).toFixed(1)} minutes`,
- });
-
githubCache.data = cache.data;
githubCache.cacheKey = cache.cacheKey;
githubCache.timestamp = cache.timestamp;
githubCache.subject = cache.subject;
- githubCache.usedToken = cache.usedToken || false;
-
-
- if (cache.subject && scrumSubject) {
- scrumSubject.value = cache.subject;
- scrumSubject.dispatchEvent(new Event('input', { bubbles: true }));
- }
resolve(true);
});
});
@@ -325,83 +265,45 @@ function allIncluded(outputTarget = 'email') {
async function fetchGithubData() {
const cacheKey = `${githubUsername}-${orgName}-${startingDate}-${endingDate}`;
-
if (githubCache.fetching || (githubCache.cacheKey === cacheKey && githubCache.data)) {
- log('Fetch already in progress or data already fetched. Skipping fetch.');
+ log('Using existing data or ongoing fetch');
return;
}
- log('Fetching Github data:', {
- username: githubUsername,
- startDate: startingDate,
- endDate: endingDate,
- });
-
- log('CacheKey in cache:', githubCache.cacheKey);
- log('Incoming cacheKey:', cacheKey);
- log('Has data:', !!githubCache.data);
-
- // Check if we need to load from storage
+ // Check cache status
if (!githubCache.data && !githubCache.fetching) {
await loadFromStorage();
};
const currentTTL = await getCacheTTL();
githubCache.ttl = currentTTL;
- log(`Caching for ${currentTTL / (60 * 1000)} minutes`);
+ githubCache.cacheKey = cacheKey;
const now = Date.now();
const isCacheFresh = (now - githubCache.timestamp) < githubCache.ttl;
const isCacheKeyMatch = githubCache.cacheKey === cacheKey;
- const needsToken = !!githubToken;
- const cacheUsedToken = !!githubCache.usedToken;
- if (githubCache.data && isCacheFresh & isCacheKeyMatch) { //should be && check after rebase
- if (needsToken & !cacheUsedToken) {
- log('Cache was fetched without token, but user now has a token. Invalidating cache.');
- githubCache.data = null;
- } else {
- log('Using cached data - cache is fresh and key matches');
- processGithubData(githubCache.data);
- return Promise.resolve();
- }
- }
- // if cache key does not match our cache is stale, fetch new data
- if (!isCacheKeyMatch) {
- log('Cache key mismatch - fetching new Data');
- githubCache.data = null;
- } else if (!isCacheFresh) {
- log('Cache is stale - fetching new data');
+
+ if (githubCache.data && isCacheFresh && isCacheKeyMatch) {
+ log('Using cached data');
+ processGithubData(githubCache.data);
+ return;
}
- // if fetching is in progress, queue the calls and return a promise resolved when done
- if (githubCache.fetching) {
- log('Fetch in progress, queuing requests');
- return new Promise((resolve, reject) => {
- githubCache.queue.push({ resolve, reject });
- });
- }
+ if (!isCacheKeyMatch) githubCache.data = null;
+ if (githubCache.fetching) return;
githubCache.fetching = true;
- githubCache.cacheKey = cacheKey;
-
const headers = {
'Accept': 'application/vnd.github.v3+json',
+ ...(githubToken && { 'Authorization': `token ${githubToken}` })
};
- if (githubToken) {
- log('Making authenticated requests.');
- headers['Authorization'] = `token ${githubToken}`;
-
- } else {
- log('Making public requests');
- }
-
- let issueUrl = `https://api.github.com/search/issues?q=author%3A${githubUsername}+org%3A${orgName}+updated%3A${startingDate}..${endingDate}&per_page=100`;
- let prUrl = `https://api.github.com/search/issues?q=commenter%3A${githubUsername}+org%3A${orgName}+updated%3A${startingDate}..${endingDate}&per_page=100`;
- let userUrl = `https://api.github.com/users/${githubUsername}`;
-
try {
- // throttling 500ms to avoid burst
+ const issueUrl = `https://api.github.com/search/issues?q=author%3A${githubUsername}+org%3A${orgName}+updated%3A${startingDate}..${endingDate}&per_page=100`;
+ const prUrl = `https://api.github.com/search/issues?q=commenter%3A${githubUsername}+org%3A${orgName}+updated%3A${startingDate}..${endingDate}&per_page=100`;
+ const userUrl = `https://api.github.com/users/${githubUsername}`;
+
+ // Throttle requests
await new Promise(res => setTimeout(res, 500));
const [issuesRes, prRes, userRes] = await Promise.all([
@@ -410,183 +312,58 @@ function allIncluded(outputTarget = 'email') {
fetch(userUrl, { headers }),
]);
- if (issuesRes.status === 401 || prRes.status === 401 || userRes.status === 401 ||
- issuesRes.status === 403 || prRes.status === 403 || userRes.status === 403) {
+ // Handle API errors
+ if (issuesRes.status === 401 || prRes.status === 401 || userRes.status === 401) {
showInvalidTokenMessage();
return;
}
- if (issuesRes.status === 404 || prRes.status === 404) {
- if (outputTarget === 'popup') {
- Materialize.toast && Materialize.toast('Organization not found on GitHub', 3000);
- }
- throw new Error('Organization not found');
- }
-
- if (!issuesRes.ok) throw new Error(`Error fetching Github issues: ${issuesRes.status} ${issuesRes.statusText}`);
- if (!prRes.ok) throw new Error(`Error fetching Github PR review data: ${prRes.status} ${prRes.statusText}`);
- if (!userRes.ok) throw new Error(`Error fetching Github userdata: ${userRes.status} ${userRes.statusText}`);
+ if (!issuesRes.ok) throw new Error(`Error fetching issues: ${issuesRes.status}`);
+ if (!prRes.ok) throw new Error(`Error fetching PRs: ${prRes.status}`);
+ if (!userRes.ok) throw new Error(`Error fetching user: ${userRes.status}`);
githubIssuesData = await issuesRes.json();
githubPrsReviewData = await prRes.json();
githubUserData = await userRes.json();
- if (githubIssuesData && githubIssuesData.items) {
- log('Fetched githubIssuesData:', githubIssuesData.items.length, 'items');
- // Collect open PRs
- const openPRs = githubIssuesData.items.filter(
- item => item.pull_request && item.state === 'open'
- );
- log('Open PRs for commit fetching:', openPRs.map(pr => pr.number));
- // Fetch commits for open PRs (batch)
- if (openPRs.length && githubToken) {
- const commitMap = await fetchCommitsForOpenPRs(openPRs, githubToken, startingDate, endingDate);
- log('Commit map returned from fetchCommitsForOpenPRs:', commitMap);
- // Attach commits to PR objects
- openPRs.forEach(pr => {
- pr._allCommits = commitMap[pr.number] || [];
- log(`Attached ${pr._allCommits.length} commits to PR #${pr.number}`);
- });
- }
- }
-
// Cache the data
githubCache.data = { githubIssuesData, githubPrsReviewData, githubUserData };
githubCache.timestamp = Date.now();
-
await saveToStorage(githubCache.data);
- processGithubData(githubCache.data);
- // Resolve queued calls
- githubCache.queue.forEach(({ resolve }) => resolve());
- githubCache.queue = [];
+ processGithubData(githubCache.data);
} catch (err) {
logError('Fetch Failed:', err);
- // Reject queued calls on error
- githubCache.queue.forEach(({ reject }) => reject(err));
- githubCache.queue = [];
+ showErrorMessage(err.message || 'An error occurred');
+ } finally {
githubCache.fetching = false;
+ }
+ }
- if (outputTarget === 'popup') {
+ function showInvalidTokenMessage() {
+ if (outputTarget === 'popup') {
+ const reportDiv = document.getElementById('scrumReport');
+ if (reportDiv) {
+ reportDiv.innerHTML = 'Invalid or expired GitHub token. Please check your token in the settings and try again.
';
const generateBtn = document.getElementById('generateReport');
- if (scrumReport) {
- let errorMsg = 'An error occurred while generating the report.';
- if (err) {
- if (typeof err === 'string') errorMsg = err;
- else if (err.message) errorMsg = err.message;
- else errorMsg = JSON.stringify(err)
- }
- scrumReport.innerHTML = `${err.message || 'An error occurred while generating the report.'}
`;
- generateBtn.innerHTML = ' Generate Report';
- generateBtn.disabled = false;
- }
if (generateBtn) {
generateBtn.innerHTML = ' Generate Report';
generateBtn.disabled = false;
}
}
- scrumGenerationInProgress = false;
- throw err;
- } finally {
- githubCache.fetching = false;
}
}
- async function fetchCommitsForOpenPRs(prs, githubToken, startDate, endDate) {
- log('fetchCommitsForOpenPRs called with PRs:', prs.map(pr => pr.number), 'startDate:', startDate, 'endDate:', endDate);
- if (!prs.length) return {};
- const since = new Date(startDate).toISOString();
- const until = new Date(endDate + 'T23:59:59').toISOString();
- let queries = prs.map((pr, idx) => {
- const repoParts = pr.repository_url.split('/');
- const owner = repoParts[repoParts.length - 2];
- const repo = repoParts[repoParts.length - 1];
- return `
- pr${idx}: repository(owner: "${owner}", name: "${repo}") {
- pullRequest(number: ${pr.number}) {
- commits(first: 100) {
- nodes {
- commit {
- messageHeadline
- committedDate
- url
- author {
- name
- user { login }
- }
- }
- }
- }
- }
-
- }`;
- }).join('\n');
- const query = `query { ${queries} }`;
- log('GraphQL query for commits:', query);
- const res = await fetch('https://api.github.com/graphql', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...(githubToken ? { Authorization: `bearer ${githubToken}` } : {})
- },
- body: JSON.stringify({ query })
- });
- log('fetchCommitsForOpenPRs response status:', res.status);
- const data = await res.json();
- log('fetchCommitsForOpenPRs response data:', data);
- let commitMap = {};
- prs.forEach((pr, idx) => {
- const prData = data.data && data.data[`pr${idx}`] && data.data[`pr${idx}`].pullRequest;
- if (prData && prData.commits && prData.commits.nodes) {
- const allCommits = prData.commits.nodes.map(n => n.commit);
- log(`PR #${pr.number} allCommits:`, allCommits);
- const filteredCommits = allCommits.filter(commit => {
- const commitDate = new Date(commit.committedDate);
- const sinceDate = new Date(since);
- const untilDate = new Date(until);
- return commitDate >= sinceDate && commitDate <= untilDate;
- });
- log(`PR #${pr.number} filteredCommits:`, filteredCommits);
- commitMap[pr.number] = filteredCommits;
- } else {
- log(`No commits found for PR #${pr.number}`);
- }
- });
- return commitMap;
- }
- async function verifyCacheStatus() {
- log('Cache Status: ', {
- hasCachedData: !!githubCache.data,
- cacheAge: githubCache.timestamp ? `${((Date.now() - githubCache.timestamp) / 1000 / 60).toFixed(1)} minutes` : `no cache`,
- cacheKey: githubCache.cacheKey,
- isFetching: githubCache.fetching,
- queueLength: githubCache.queue.length
- });
- const storageData = await new Promise(resolve => {
- chrome.storage.local.get('githubCache', resolve);
- });
- log('Storage Status:', {
- hasStoredData: !!storageData.githubCache,
- storedCacheKey: storageData.githubCache?.cacheKey,
- storageAge: storageData.githubCache?.timestamp ?
- `${((Date.now() - storageData.githubCache.timestamp) / 1000 / 60).toFixed(1)} minutes` :
- 'no data'
- });
- }
- verifyCacheStatus();
-
- function showInvalidTokenMessage() {
+ function showErrorMessage(message) {
if (outputTarget === 'popup') {
const reportDiv = document.getElementById('scrumReport');
if (reportDiv) {
- reportDiv.innerHTML = 'Invalid or expired GitHub token. Please check your token in the settings and try again.
';
+ reportDiv.innerHTML = `${message}
`;
const generateBtn = document.getElementById('generateReport');
if (generateBtn) {
generateBtn.innerHTML = ' Generate Report';
generateBtn.disabled = false;
}
- } else {
- alert('Invalid or expired GitHub token. Please check your token in the extension popup and try again.');
}
}
}
@@ -597,13 +374,6 @@ function allIncluded(outputTarget = 'email') {
githubPrsReviewData = data.githubPrsReviewData;
githubUserData = data.githubUserData;
- log('GitHub data set:', {
- issues: githubIssuesData?.items?.length || 0,
- prs: githubPrsReviewData?.items?.length || 0,
- user: githubUserData?.login
- });
-
-
lastWeekArray = [];
nextWeekArray = [];
reviewedPrsArray = [];
@@ -616,11 +386,13 @@ function allIncluded(outputTarget = 'email') {
if (!githubCache.subject && scrumSubject) {
scrumSubjectLoaded();
}
+
await Promise.all([
writeGithubIssuesPrs(),
writeGithubPrsReviews(),
])
- log('Both data processing functions completed, generating scrum body');
+
+ log('Generating scrum body');
writeScrumBody();
}
@@ -630,31 +402,29 @@ function allIncluded(outputTarget = 'email') {
return date.toLocaleDateString('en-US', options);
}
- //load initial text in scrum body
function writeScrumBody() {
if (!enableToggle || (outputTarget === 'email' && hasInjectedContent)) return;
if (outputTarget === 'email') {
if (!window.emailClientAdapter) {
- console.error('Email client adapter not found');
+ logError('Email client adapter not found');
return;
}
if (!window.emailClientAdapter.isNewConversation()) {
- console.log('Not a new conversation, skipping scrum helper');
+ log('Not a new conversation, skipping scrum helper');
return;
}
}
setTimeout(() => {
- // Generate content first
+ // Generate content
let lastWeekUl = '';
- let i;
- for (i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i];
- for (i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i];
+ for (let i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i];
+ for (let i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i];
lastWeekUl += '
';
let nextWeekUl = '';
- for (i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i];
+ for (let i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i];
nextWeekUl += '
';
let weekOrDay = lastWeekContribution ? 'last week' : (yesterdayContribution ? 'yesterday' : 'the period');
@@ -662,7 +432,7 @@ function allIncluded(outputTarget = 'email') {
// Create the complete content
let content;
- if (lastWeekContribution == true || yesterdayContribution == true) {
+ if (lastWeekContribution || yesterdayContribution) {
content = `1. What did I do ${weekOrDay}?
${lastWeekUl}
2. What do I plan to do ${weekOrDay2}?
@@ -678,59 +448,43 @@ ${nextWeekUl}
${userReason}`;
}
-
+ // Inject into UI
if (outputTarget === 'popup') {
const scrumReport = document.getElementById('scrumReport');
if (scrumReport) {
- log("found div, updating content");
scrumReport.innerHTML = content;
-
- // Reset generate button
const generateBtn = document.getElementById('generateReport');
if (generateBtn) {
generateBtn.innerHTML = ' Generate Report';
generateBtn.disabled = false;
}
- scrumGenerationInProgress = false;
- } else {
- logError('Scrum report div not found');
- scrumGenerationInProgress = false;
}
} else {
const elements = window.emailClientAdapter.getEditorElements();
- if (!elements || !elements.body) {
- console.error('Email client editor not found');
- return;
+ if (elements && elements.body) {
+ window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange);
+ hasInjectedContent = true;
}
- window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange);
- hasInjectedContent = true;
- scrumGenerationInProgress = false;
}
+
+ scrumGenerationInProgress = false;
}, 500);
}
- //load initial scrum subject
function scrumSubjectLoaded() {
try {
if (!enableToggle || hasInjectedContent) return;
if (!scrumSubject) {
- console.error('Subject element not found');
+ logError('Subject element not found');
return;
}
setTimeout(() => {
let name = githubUserData.name || githubUsername;
let project = projectName || '';
let curDate = new Date();
- let year = curDate.getFullYear().toString();
- let date = curDate.getDate();
- let month = curDate.getMonth();
- month++;
- if (month < 10) month = '0' + month;
- if (date < 10) date = '0' + date;
- let dateCode = year.toString() + month.toString() + date.toString();
+ let dateCode = `${curDate.getFullYear()}${pad(curDate.getMonth() + 1)}${pad(curDate.getDate())}`;
const subject = `[Scrum] ${name} - ${project} - ${dateCode}`;
- log('Generated subject:', subject);
githubCache.subject = subject;
saveToStorage(githubCache.data, subject);
@@ -740,83 +494,61 @@ ${userReason}`;
}
});
} catch (err) {
- console.err('Error while setting subject: ', err);
+ logError('Error setting subject:', err);
}
}
function writeGithubPrsReviews() {
let items = githubPrsReviewData.items;
- log('Processing PR reviews:', {
- hasItems: !!items,
- itemCount: items?.length,
- firstItem: items?.[0]
- });
if (!items) {
logError('No Github PR review data available');
return;
}
reviewedPrsArray = [];
githubPrsReviewDataProcessed = {};
- let i;
- for (i = 0; i < items.length; i++) {
+
+ for (let i = 0; i < items.length; i++) {
let item = items[i];
- if (item.user.login == githubUsername || !item.pull_request) continue;
+ if (item.user.login === githubUsername || !item.pull_request) continue;
let repository_url = item.repository_url;
let project = repository_url.substr(repository_url.lastIndexOf('/') + 1);
let title = item.title;
let number = item.number;
let html_url = item.html_url;
+
if (!githubPrsReviewDataProcessed[project]) {
- // first pr in this repo
githubPrsReviewDataProcessed[project] = [];
}
- let obj = {
+
+ githubPrsReviewDataProcessed[project].push({
number: number,
html_url: html_url,
title: title,
state: item.state,
- };
- githubPrsReviewDataProcessed[project].push(obj);
+ });
}
+
for (let repo in githubPrsReviewDataProcessed) {
- let repoLi =
- ' \
- (' +
- repo +
- ') - Reviewed ';
+ let repoLi = `(${repo}) - Reviewed `;
if (githubPrsReviewDataProcessed[repo].length > 1) repoLi += 'PRs - ';
- else {
- repoLi += 'PR - ';
- }
+ else repoLi += 'PR - ';
+
if (githubPrsReviewDataProcessed[repo].length <= 1) {
for (let pr in githubPrsReviewDataProcessed[repo]) {
let pr_arr = githubPrsReviewDataProcessed[repo][pr];
- let prText = '';
- prText +=
- "#" + pr_arr.number + ' (' + pr_arr.title + ') ';
+ let prText = `#${pr_arr.number} (${pr_arr.title}) `;
if (pr_arr.state === 'open') prText += issue_opened_button;
else prText += issue_closed_button;
-
- prText += ' ';
repoLi += prText;
}
} else {
repoLi += '';
for (let pr1 in githubPrsReviewDataProcessed[repo]) {
let pr_arr1 = githubPrsReviewDataProcessed[repo][pr1];
- let prText1 = '';
- prText1 +=
- "- #" +
- pr_arr1.number +
- ' (' +
- pr_arr1.title +
- ') ';
+ let prText1 = `
- #${pr_arr1.number} (${pr_arr1.title}) `;
if (pr_arr1.state === 'open') prText1 += issue_opened_button;
else prText1 += issue_closed_button;
-
- prText1 += '
';
+ prText1 += '
';
repoLi += prText1;
}
repoLi += '';
@@ -826,55 +558,12 @@ ${userReason}`;
}
prsReviewDataProcessed = true;
- if (outputTarget === 'email') {
- triggerScrumGeneration();
- }
-
- }
-
- function triggerScrumGeneration() {
- if (issuesDataProcessed && prsReviewDataProcessed) {
- log('Both data sets processed, generating scrum body.');
+ if (issuesDataProcessed && outputTarget === 'email') {
writeScrumBody();
- } else {
- log('Waiting for all data to be processed before generating scrum.', {
- issues: issuesDataProcessed,
- reviews: prsReviewDataProcessed,
- });
- }
- }
-
- // Helper: calculate days between two yyyy-mm-dd strings
- function getDaysBetween(start, end) {
- const d1 = new Date(start);
- const d2 = new Date(end);
- return Math.ceil((d2 - d1) / (1000 * 60 * 60 * 24));
- }
-
- // Session cache object
- let sessionMergedStatusCache = {};
-
- // Helper to fetch PR details for merged status (REST, single PR)
- async function fetchPrMergedStatusREST(owner, repo, number, headers) {
- const cacheKey = `${owner}/${repo}#${number}`;
- if (sessionMergedStatusCache[cacheKey] !== undefined) {
- return sessionMergedStatusCache[cacheKey];
- }
- const url = `https://api.github.com/repos/${owner}/${repo}/pulls/${number}`;
- try {
- const res = await fetch(url, { headers });
- if (!res.ok) return null;
- const data = await res.json();
- const merged = !!data.merged_at;
- sessionMergedStatusCache[cacheKey] = merged;
- return merged;
- } catch (e) {
- return null;
}
}
- async function writeGithubIssuesPrs() {
- log('writeGithubIssuesPrs called');
+ function writeGithubIssuesPrs() {
let items = githubIssuesData.items;
lastWeekArray = [];
nextWeekArray = [];
@@ -882,53 +571,7 @@ ${userReason}`;
logError('No Github issues data available');
return;
}
-
- const headers = { 'Accept': 'application/vnd.github.v3+json' };
- if (githubToken) headers['Authorization'] = `token ${githubToken}`;
- let useMergedStatus = false;
- let fallbackToSimple = false;
- let daysRange = getDaysBetween(startingDate, endingDate);
- // For token users, always enable useMergedStatus (no 7-day limit)
- if (githubToken) {
- useMergedStatus = true;
- } else if (daysRange <= 7) {
- useMergedStatus = true;
- }
- // Collect PRs to batch fetch merged status
- let prsToCheck = [];
- for (let i = 0; i < items.length; i++) {
- let item = items[i];
- if (item.pull_request && item.state === 'closed' && useMergedStatus && !fallbackToSimple) {
- let repository_url = item.repository_url;
- let repoParts = repository_url.split('/');
- let owner = repoParts[repoParts.length - 2];
- let repo = repoParts[repoParts.length - 1];
- prsToCheck.push({ owner, repo, number: item.number, idx: i });
- }
- }
-
- let mergedStatusResults = {};
- if (githubToken) {
- // Use GraphQL batching for all cases
- if (prsToCheck.length > 0) {
- mergedStatusResults = await fetchPrsMergedStatusBatch(prsToCheck, headers);
- log('Merged status results (GraphQL):', mergedStatusResults);
- }
- } else if (useMergedStatus) {
- if (prsToCheck.length > 30) {
- fallbackToSimple = true;
- if (typeof Materialize !== 'undefined' && Materialize.toast) {
- Materialize.toast('API limit exceeded. Please use a GitHub token for full status. Showing only open/closed PRs.', 5000);
- }
- } else {
- // Use REST API for each PR, cache results
- for (let pr of prsToCheck) {
- let merged = await fetchPrMergedStatusREST(pr.owner, pr.repo, pr.number, headers);
- mergedStatusResults[`${pr.owner}/${pr.repo}#${pr.number}`] = merged;
- }
- log('Merged status results (REST):', mergedStatusResults);
- }
- }
+
for (let i = 0; i < items.length; i++) {
let item = items[i];
let html_url = item.html_url;
@@ -938,262 +581,122 @@ ${userReason}`;
let number = item.number;
let li = '';
let isDraft = false;
+
if (item.pull_request && typeof item.draft !== 'undefined') {
isDraft = item.draft;
}
+
if (item.pull_request) {
-
- const prCreatedDate = new Date(item.created_at);
- const startDate = new Date(startingDate);
- const endDate = new Date(endingDate + 'T23:59:59');
- const isNewPR = prCreatedDate >= startDate && prCreatedDate <= endDate;
-
- if (!isNewPR) {
- const hasCommitsInRange = showCommits && item._allCommits && item._allCommits.length > 0;
-
- if (!hasCommitsInRange) {
-
- continue; //skip these prs - created outside daterange with no commits
- } else {
-
- }
- } else {
-
- }
- const prAction = isNewPR ? 'Made PR' : 'Existing PR';
if (isDraft) {
- li = `(${project}) - ${prAction} (#${number}) - ${title} ${pr_draft_button}`;
+ li = `(${project}) - Made PR (#${number}) - ${title} ${pr_draft_button}`;
} else if (item.state === 'open') {
- li = `(${project}) - ${prAction} (#${number}) - ${title} ${pr_open_button}`;
- if (showCommits && item._allCommits && item._allCommits.length && !isNewPR) {
- log(`[PR DEBUG] Rendering commits for existing PR #${number}:`, item._allCommits);
- item._allCommits.forEach(commit => {
- li += `${commit.messageHeadline} (${new Date(commit.committedDate).toLocaleString()})`;
- });
- }
- li += ``;
+ li = `(${project}) - Made PR (#${number}) - ${title} ${pr_open_button}`;
} else if (item.state === 'closed') {
- let merged = null;
- if ((githubToken || (useMergedStatus && !fallbackToSimple)) && mergedStatusResults) {
- let repoParts = repository_url.split('/');
- let owner = repoParts[repoParts.length - 2];
- let repo = repoParts[repoParts.length - 1];
- merged = mergedStatusResults[`${owner}/${repo}#${number}`];
- }
- if (merged === true) {
- li = `(${project}) - Made PR (#${number}) - ${title} ${pr_merged_button}`;
- } else {
- // Always show closed label for merged === false or merged === null/undefined
- li = `(${project}) - Made PR (#${number}) - ${title} ${pr_closed_button}`;
- }
+ li = `(${project}) - Made PR (#${number}) - ${title} ${pr_closed_button}`;
}
lastWeekArray.push(li);
- continue;
} else {
- // is a issue
+ // Handle issues
if (item.state === 'open' && item.body?.toUpperCase().indexOf('YES') > 0) {
- let li2 =
- '(' +
- project +
- ') - Work on Issue(#' +
- number +
- ") - " +
- title +
- ' ' +
- issue_opened_button +
- ' ';
+ let li2 = `(${project}) - Work on Issue(#${number}) - ${title} ${issue_opened_button}`;
nextWeekArray.push(li2);
}
+
if (item.state === 'open') {
li = `(${project}) - Opened Issue(#${number}) - ${title} ${issue_opened_button}`;
} else if (item.state === 'closed') {
li = `(${project}) - Opened Issue(#${number}) - ${title} ${issue_closed_button}`;
} else {
- li =
- '(' +
- project +
- ') - Opened Issue(#' +
- number +
- ") - " +
- title +
- ' ';
+ li = `(${project}) - Opened Issue(#${number}) - ${title}`;
}
+ lastWeekArray.push(li);
}
- lastWeekArray.push(li);
}
+
issuesDataProcessed = true;
- if (outputTarget === 'email') {
- triggerScrumGeneration();
+ if (prsReviewDataProcessed && outputTarget === 'email') {
+ writeScrumBody();
}
}
+ // Initialize email UI elements
let intervalBody = setInterval(() => {
if (!window.emailClientAdapter) return;
-
const elements = window.emailClientAdapter.getEditorElements();
if (!elements || !elements.body) return;
-
clearInterval(intervalBody);
scrumBody = elements.body;
- // writeScrumBody(); // This call is premature and causes the issue.
}, 500);
let intervalSubject = setInterval(() => {
if (!githubUserData || !window.emailClientAdapter) return;
-
const elements = window.emailClientAdapter.getEditorElements();
if (!elements || !elements.subject) return;
if (outputTarget === 'email' && !window.emailClientAdapter.isNewConversation()) {
- console.log('Not a new conversation, skipping subject interval');
clearInterval(intervalSubject);
return;
}
clearInterval(intervalSubject);
scrumSubject = elements.subject;
-
- setTimeout(() => {
- scrumSubjectLoaded();
- }, 500);
+ setTimeout(scrumSubjectLoaded, 500);
}, 500);
- // check for github safe writing
- let intervalWriteGithubIssues = setInterval(() => {
- if (outputTarget === 'popup') {
- return;
- } else {
- if (scrumBody && githubUsername && githubIssuesData && githubPrsReviewData) {
- clearInterval(intervalWriteGithubIssues);
- clearInterval(intervalWriteGithubPrs);
- writeGithubIssuesPrs();
- }
- }
- }, 500);
- let intervalWriteGithubPrs = setInterval(() => {
- if (outputTarget === 'popup') {
- return;
- } else {
- if (scrumBody && githubUsername && githubPrsReviewData && githubIssuesData) {
- clearInterval(intervalWriteGithubPrs);
- clearInterval(intervalWriteGithubIssues);
- writeGithubPrsReviews();
- }
- }
- }, 500);
- if (!refreshButton_Placed) {
- let intervalWriteButton = setInterval(() => {
- if (document.getElementsByClassName('F0XO1GC-x-b').length == 3 && scrumBody && enableToggle) {
- refreshButton_Placed = true;
- clearInterval(intervalWriteButton);
- let td = document.createElement('td');
- let button = document.createElement('button');
- button.style = 'background-image:none;background-color:#3F51B5;';
- button.setAttribute('class', 'F0XO1GC-n-a F0XO1GC-G-a');
- button.title = 'Rewrite your SCRUM using updated settings!';
- button.id = 'refreshButton';
- let elemText = document.createTextNode('↻ Rewrite SCRUM!');
- button.appendChild(elemText);
- td.appendChild(button);
- document.getElementsByClassName('F0XO1GC-x-b')[0].children[0].children[0].appendChild(td);
- document.getElementById('refreshButton').addEventListener('click', handleRefresh);
- }
- }, 1000);
- }
- function handleRefresh() {
- hasInjectedContent = false; // Reset the flag before refresh
- allIncluded();
- }
+ // Initialize
+ getBrowserData();
}
+// Cache refresh function
async function forceGithubDataRefresh() {
let showCommits = false;
-
await new Promise(resolve => {
- chrome.storage.local.get('showCommits', (result) => {
- if (result.showCommits !== undefined) {
- showCommits = result.showCommits;
- }
+ storage.local.get('showCommits', (result) => {
+ if (result.showCommits !== undefined) showCommits = result.showCommits;
resolve();
});
});
+ // Reset cache
if (typeof githubCache !== 'undefined') {
githubCache.data = null;
githubCache.cacheKey = null;
githubCache.timestamp = 0;
githubCache.subject = null;
- githubCache.fetching = false;
- githubCache.queue = [];
}
- await new Promise(resolve => {
- chrome.storage.local.remove('githubCache', resolve);
- });
-
- chrome.storage.local.set({ showCommits: showCommits });
-
+ await new Promise(resolve => storage.local.remove('githubCache', resolve));
+ storage.local.set({ showCommits });
hasInjectedContent = false;
-
return { success: true };
}
-
-// allIncluded('email');
-
+// Initialize based on context
if (window.location.protocol.startsWith('http')) {
allIncluded('email');
- $('button>span:contains(New conversation)').parent('button').click(() => {
- allIncluded();
- });
+ // Gmail-specific handler
+ const newConvBtn = document.querySelector('button>span:contains(New conversation)');
+ if (newConvBtn) newConvBtn.parentElement.addEventListener('click', () => allIncluded());
}
window.generateScrumReport = function () {
allIncluded('popup');
}
+// Message handling for cache refresh
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'forceRefresh') {
forceGithubDataRefresh()
- .then(result => sendResponse(result)).catch(err => {
- console.error('Force refresh failed:', err);
- sendResponse({ success: false, error: err.message });
- });
- return true;
+ .then(result => sendResponse(result))
+ .catch(err => sendResponse({ success: false, error: err.message }));
+ return true; // Indicates async response
}
});
-async function fetchPrsMergedStatusBatch(prs, headers) {
- // prs: Array of {owner, repo, number}
- const results = {};
- if (prs.length === 0) return results;
- // Use GitHub GraphQL API for batching
- const query = `query {
-${prs.map((pr, i) => ` repo${i}: repository(owner: \"${pr.owner}\", name: \"${pr.repo}\") {
- pr${i}: pullRequest(number: ${pr.number}) { merged }
- }`).join('\n')}
-}`;
- try {
- const res = await fetch('https://api.github.com/graphql', {
- method: 'POST',
- headers: {
- ...headers,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ query }),
- });
- if (!res.ok) return results;
- const data = await res.json();
- prs.forEach((pr, i) => {
- const merged = data.data[`repo${i}`]?.[`pr${i}`]?.merged;
- results[`${pr.owner}/${pr.repo}#${pr.number}`] = merged;
- });
- return results;
- } catch (e) {
- return results;
- }
+// Initialize for email clients
+if (window.emailClientAdapter) {
+ window.emailClientAdapter.onNewCompose(() => {
+ hasInjectedContent = false;
+ allIncluded('email');
+ });
}
\ No newline at end of file