diff --git a/src/auth.js b/src/auth.js index 030cfb8..dd5e3bf 100644 --- a/src/auth.js +++ b/src/auth.js @@ -1,10 +1,10 @@ +/* global chrome, queryString, getSyncStorage, setSyncStorage */ + const client_id = "1a3ac4d44a9e65a75a77"; const client_secret = "ab3c3a116e35d9fe11d409cb8c1205e9ae5a7e91"; const githubBaseUrl = "https://github.com/login/oauth/authorize"; const githubTokenUrl = "https://github.com/login/oauth/access_token"; -const redirectUri = chrome.identity.getRedirectURL('provider_cb'); - -console.log(redirectUri); +const redirectUri = chrome.identity.getRedirectURL("provider_cb"); const getAuthUrl = (base, callbackUrl, scope) => { let obj = { @@ -15,7 +15,7 @@ const getAuthUrl = (base, callbackUrl, scope) => { }; return `${base}?${queryString.stringify(obj)}`; -} +}; function getTokenFromCode(code) { let obj = { @@ -25,7 +25,7 @@ function getTokenFromCode(code) { }; return fetch(`${githubTokenUrl}?${queryString.stringify(obj)}`) - .then((res) => res.text(), (err) => { + .then((res) => res.text(), () => { throw new Error("Failed to get access_token"); }); } @@ -62,29 +62,29 @@ function getToken(url, interactive) { resolve(access_token); }); } else { - reject(new Error ('neither access_token nor code available')); + reject(new Error ("neither access_token nor code available")); } } else { - reject(new Error('Invalid redirect URI')); + reject(new Error("Invalid redirect URI")); } }); }); } function getTokenFromOauth() { - getSyncStorage({ 'access_token': null }) + getSyncStorage({ "access_token": null }) .then((res) => { if (!res.access_token) { - const url = getAuthUrl(githubBaseUrl, redirectUri, 'public_repo'); + const url = getAuthUrl(githubBaseUrl, redirectUri, "public_repo"); getToken(url, true) .then((token) => { - setSyncStorage({ 'access_token': token }); + setSyncStorage({ "access_token": token }); const accessTokenInput = document.getElementById("token-input"); accessTokenInput.value = token; document.querySelector("#feedback").textContent = "Access Token Set!"; }, (message) => { document.querySelector("#feedback").textContent = message; - }) + }); } else { document.querySelector("#feedback").textContent = "Access Token Already Set!"; } diff --git a/src/content.css b/src/content.css index 2837ad7..e950d0b 100644 --- a/src/content.css +++ b/src/content.css @@ -3,6 +3,7 @@ } #gce-update:hover ~ #gce-update-time, -#gce-num-prs:hover ~ #gce-update-time { +#gce-num-prs:hover ~ #gce-update-time, +#gce-num-issues:hover ~ #gce-update-time { display: block; } diff --git a/src/content.js b/src/content.js index 9957f4e..396dfe4 100644 --- a/src/content.js +++ b/src/content.js @@ -1,10 +1,11 @@ "use strict"; -/* global chrome, getSyncStorage, setStorage, getStorage */ +/* global $, getSyncStorage, setStorage, getStorage, gitHubInjection */ const isPR = (path) => /^\/[^/]+\/[^/]+\/pull\/\d+/.test(path); const isIssue = (path) => /^\/[^/]+\/[^/]+\/issues\/\d+/.test(path); -const getCurrentUser = () => $('.js-menu-target img').attr('alt').slice(1) || ""; +const getCurrentUser = () => $(".js-menu-target img").attr("alt").slice(1) || ""; +const isPrivate = () => $(".repo-private-label").length > 0; function getContributor() { let $contributor = $(".timeline-comment-wrapper .timeline-comment-header-text strong"); @@ -18,13 +19,13 @@ function getContributorInfo() { let pathNameArr = location.pathname.split("/"); let org = pathNameArr[1]; // babel let repo = pathNameArr[2]; // babel-eslint - let currentPR = pathNameArr[4]; // 3390 + let currentNum = pathNameArr[4]; // 3390 let repoPath = org + "/" + repo; // babel/babel-eslint let contributor = getContributor(); let ret = { - contributor: getContributor(), - currentPR, + contributor, + currentNum, repoPath }; @@ -46,7 +47,7 @@ function buildUrl({base, q: {type, filterUser, author, repo}, sort, order, per_p return query; } -function contributorCount({access_token, contributor, repoPath, type}) { +function contributorCount({access_token, contributor, repoPath, old = {}, type}) { let searchURL = buildUrl({ access_token, base: "https://api.github.com/search/issues", @@ -68,34 +69,52 @@ function contributorCount({access_token, contributor, repoPath, type}) { } let obj = { - prs: json.total_count, lastUpdate: Date.now() }; - if (json.items && json.items.length) { - obj.firstPRNumber = json.items[0].number; + if (type === "pr") { + obj.prs = json.total_count; + } else if (type === "issue") { + obj.issues = json.total_count; } - if (obj.prs) { - setStorage(contributor, repoPath, obj); + if (json.items && json.items.length) { + obj[`first${type[0].toUpperCase() + type.slice(1)}Number`] = json.items[0].number; } + obj = Object.assign(old, obj); + + setStorage(contributor, repoPath, obj); + return obj; }); } -function appendPRText(currentPR, repoInfo) { - let {prs, firstPRNumber} = repoInfo; - let text = `${prs} PRs`; +function appendPRText(currentNum, repoInfo) { + let {issues, prs, firstPrNumber, firstIssueNumber} = repoInfo; + + if (prs !== undefined) { + let prText = `${prs} PRs`; + if (firstPrNumber === +currentNum) { + prText = "First PR"; + if (prs > 1) { + prText += ` out of ${prs} (to the repo)`; + } + } + repoInfo.prText = prText; + } - if (firstPRNumber === +currentPR) { - text = "First PR"; - if (prs > 1) { - text += ` out of ${prs} (to the repo)`; + if (issues !== undefined) { + let issueText = `${issues} Issues`; + if (firstIssueNumber === +currentNum) { + issueText = "First Issue"; + if (issues > 1) { + issueText += ` out of ${issues} (to the repo)`; + } } + repoInfo.issueText = issueText; } - repoInfo.text = text; return repoInfo; } @@ -108,57 +127,70 @@ function makeUpdateLabel(time) { } function injectInitialUI({ contributor, repoPath }) { - if ($("#gce-num-prs").length) return; - let $elem = $(".timeline-comment-header-text").first(); - let id = "gce-num-prs"; - let prText = makeLabel("Loading # of PRs.."); - let updateText = makeLabel("🔄 PRs"); - - if (!$(id).length) { - $elem.before(`${prText}`); - $elem.before(`${updateText}`); - $elem.before(`N/A`); - - let $update = $("#gce-update"); - $update.dom[0].addEventListener("click", function() { - setStorage(contributor, repoPath, {}); - update(getContributorInfo()); - }); - } + let prId = "gce-num-prs"; + let prText = makeLabel("Loading.."); + + if ($(`#${prId}`).length) return; + + let issueId = "gce-num-issues"; + let issueText = makeLabel("Loading.."); + let updateText = makeLabel("🔄"); + + $elem.before(`${prText}`); + $elem.before(`${issueText}`); + $elem.before(`${updateText}`); + $elem.before(`N/A`); + + let $update = $("#gce-update"); + $update.dom[0].addEventListener("click", function() { + setStorage(contributor, repoPath, {}); + update(getContributorInfo()); + }); } -function updatePRText({ text, lastUpdate }) { - let prText = $("#gce-num-prs .timeline-comment-label"); - if (prText.length) { - prText.text(text); +function updateTextNodes({ prText, issueText, lastUpdate }) { + let prNode = $("#gce-num-prs .timeline-comment-label"); + if (prNode.length) { + prNode.text(prText); + } + + let issueNode = $("#gce-num-issues .timeline-comment-label"); + if (issueNode.length) { + issueNode.text(issueText); } + let updateTime = $("#gce-update-time"); if (updateTime && typeof lastUpdate === "number") { updateTime.html(`Last Updated ${makeUpdateLabel(new Date(lastUpdate))}`); } } -function update({ contributor, repoPath, currentPR }) { +function update({ contributor, repoPath, currentNum }) { getStorage(contributor, repoPath) .then((storage) => { let storageRes = storage[contributor][repoPath]; - if (storageRes.prs) { - updatePRText(appendPRText(currentPR, storageRes)); + if (storageRes.prs || storageRes.issues) { + updateTextNodes(appendPRText(currentNum, storageRes)); } else { getSyncStorage({ "access_token": null }) .then((res) => { - contributorCount({ access_token: res.access_token, type: "pr", contributor, repoPath}) - .then((repoInfo) => { + Promise.all([ + contributorCount({ old: storageRes, access_token: res.access_token, type: "pr", contributor, repoPath}), + contributorCount({ old: storageRes, access_token: res.access_token, type: "issue", contributor, repoPath}) + ]) + .then(([prInfo, issueInfo]) => { + let repoInfo = Object.assign(prInfo, issueInfo); + if (repoInfo.errors) { - updatePRText(repoInfo.errors[0].message); + updateTextNodes(repoInfo.errors[0].message); return; } if (repoInfo.message) { // API rate limit exceeded for hzoo. if (repoInfo.message.indexOf(`API rate limit exceeded for ${getCurrentUser()}`) >= 0) { - updatePRText("More than 30 req/min :D"); + updateTextNodes("More than 30 req/min :D"); return; } @@ -166,11 +198,11 @@ function update({ contributor, repoPath, currentPR }) { // (But here's the good news: Authenticated requests get a higher rate limit. // Check out the documentation for more details.) if (repoInfo.message.indexOf("the good news") >= 0) { - updatePRText("More than 10 req/min: Maybe add a access_token!"); + updateTextNodes("More than 10 req/min: Maybe add a access_token!"); return; } } - updatePRText(appendPRText(currentPR, repoInfo)); + updateTextNodes(appendPRText(currentNum, repoInfo)); }); }); } @@ -179,10 +211,12 @@ function update({ contributor, repoPath, currentPR }) { document.addEventListener("DOMContentLoaded", () => { gitHubInjection(window, () => { + // if (isPrivate()) return; + if (isPR(location.pathname) || isIssue(location.pathname)) { if (getContributor()) { update(getContributorInfo()); } - }; + } }); }); diff --git a/src/options.html b/src/options.html index c58a618..0809bb3 100644 --- a/src/options.html +++ b/src/options.html @@ -17,6 +17,10 @@ background-color: #f0ad4e; text-align: center; } + #clear-cache { + text-align: center; + cursor: pointer; + } @@ -33,6 +37,7 @@

Access Token

+

Clear Contributor Data

Github's Search API Rate Limit
diff --git a/src/options.js b/src/options.js index 4fb2e5e..d256c9d 100644 --- a/src/options.js +++ b/src/options.js @@ -1,8 +1,9 @@ -/* global getSyncStorage, setSyncStorage */ +/* global getSyncStorage, setSyncStorage, clearSyncStorage, getTokenFromOauth */ document.addEventListener("DOMContentLoaded", () => { const accessTokenInput = document.getElementById("token-input"); const oauthLink = document.getElementById("use-oauth"); + const clearCacheLink = document.getElementById("clear-cache"); getSyncStorage({ "access_token": null }) .then(({ access_token }) => { @@ -15,5 +16,15 @@ document.addEventListener("DOMContentLoaded", () => { oauthLink.addEventListener("click", () => { getTokenFromOauth(); - }) + }); + + clearCacheLink.addEventListener("click", () => { + let temp = accessTokenInput.value; + clearSyncStorage() + .then(() => { + setSyncStorage({ "access_token": temp }); + document.querySelector("#feedback").textContent = "Storage Cleared"; + }); + }); }); + diff --git a/src/storage.js b/src/storage.js index 200abca..542abc9 100644 --- a/src/storage.js +++ b/src/storage.js @@ -25,6 +25,7 @@ function promisify(func) { window.getSyncStorage = promisify(chrome.storage.sync.get.bind(chrome.storage.sync)); window.setSyncStorage = promisify(chrome.storage.sync.set.bind(chrome.storage.sync)); +window.clearSyncStorage = promisify(chrome.storage.sync.clear.bind(chrome.storage.sync)); window.setStorage = (CONTRIBUTOR, ORG_REPO_PATH, value) => { return window.setSyncStorage({