Skip to content

Instantly share code, notes, and snippets.

@mRoca
Last active April 1, 2025 08:42
Show Gist options
  • Save mRoca/95a49c86fcb30fe2b1cca7f97ac73129 to your computer and use it in GitHub Desktop.
Save mRoca/95a49c86fcb30fe2b1cca7f97ac73129 to your computer and use it in GitHub Desktop.
// In Github PRs list page,
// this scripts adds "You approved this pull request, you opened this pull request, You left a review, ..." for each PR line
//
// Recommanded usage for Chrome
// - extension: https://chromewebstore.google.com/detail/user-javascript-and-css/nbhcbdghjpllgmfilhnhkllmkecfmpld
// - for: https://github.com/*
//
// Recommanded usage for Firefox
// - extension: https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/
// greasemonkey config
// ==UserScript==
// @name Add review information on Github PR list
// @version 1
// @include https://github.com/*
// @grant none
// ==/UserScript==
const hovercardSubject = document.querySelector('meta[name=hovercard-subject-tag]').getAttribute('content');
const getHovercard = async (prEl) => {
const url = window.location.origin + prEl.getAttribute('data-hovercard-url');
const params = new URLSearchParams();
params.append('subject', hovercardSubject);
params.append('current_path', window.location.pathname);
const response = await fetch(`${url}?${params.toString()}`, { headers: { 'x-requested-with': 'XMLHttpRequest' } });
if (!response.ok) {
throw new Error(response);
}
return await response.text();
};
const displayHovercard = async (prEl) => {
const html = await getHovercard(prEl);
const parser = new DOMParser();
const content = parser.parseFromString(html, 'text/html');
const newInfoEl = content.getElementsByClassName('border-top')[0];
// Getting the div already containing: #1739 opened xx minutes ago by xxx • Review required
const targetEl = prEl.parentNode.getElementsByClassName('opened-by')[0]?.parentNode;
if (!newInfoEl || !targetEl) {
return;
}
[...targetEl.getElementsByClassName('ext-added')].forEach(el => el.remove());
[...newInfoEl.children].forEach(child => {
if (child.innerText.trim() === 'Review required') {
// Already displayed
return;
}
child.className = 'd-none d-md-inline-flex ml-auto ext-added';
targetEl.append(child);
});
};
const displayHovercardForAllItems = () => {
document.querySelectorAll('[data-hovercard-type="pull_request"]').forEach(displayHovercard);
};
if (window.navigation) {
// Chrome can detect url change, see https://developer.mozilla.org/en-US/docs/Web/API/Navigation/navigate_event
window.navigation.addEventListener('navigate', displayHovercardForAllItems);
} else {
// Firefox needs a custom observer
let previousUrl = '';
const observer = new MutationObserver(() => {
if (location.href === previousUrl) {
return;
}
previousUrl = location.href;
console.log(`URL changed to ${location.href}`);
setTimeout(displayHovercardForAllItems, 100);
});
observer.observe(document, { subtree: true, childList: true });
}
window.addEventListener("load", displayHovercardForAllItems);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment