Skip to content

Instantly share code, notes, and snippets.

@tbekaert
Last active December 12, 2022 12:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tbekaert/d50ff9b09a5d1138b701669d49530c57 to your computer and use it in GitHub Desktop.
Save tbekaert/d50ff9b09a5d1138b701669d49530c57 to your computer and use it in GitHub Desktop.
Arc Boost to add a `Mark as review` utils on GitHub pull request
let previousPathname = '';
// This Observer is needed as GitHub is a SPA
let observer = new MutationObserver(function(mutations) {
if (location.pathname !== previousPathname) {
previousPathname = location.pathname;
if (/\/pull\/[\d]+\/files/.test(location.pathname)) {
// The timeout is here to wait for the page to load
// before injecting the button
setTimeout(() => {
injectMarkAsViewed()
}, 0)
}
}
});
observer.observe(document, {subtree: true, childList: true})
function injectMarkAsViewed () {
const $dropdown = createDropdown({
label: "Mark as viewed",
title: "Select an option",
buttonClassNames: ["mr-3"],
buttonStyle: "float: left;",
items: [
{
title: "Matching pattern",
subtitle:
"Mark as viewed all files whose filename match the pattern (string based)",
onClick: () => {
let query = window.prompt(
"What is the pattern to match? (string based)"
);
Array.from(
document.querySelectorAll(`.file-header[data-path*='${query}']`)
).forEach(($el) => {
let $checkbox = $el.querySelector(".js-reviewed-checkbox");
if (!$checkbox.checked) {
$checkbox.click();
}
});
},
},
{
title: "Renamed files",
subtitle: "Mark all renamed files as viewed",
onClick: () => {
Array.from(
document.querySelectorAll(".file-header + .js-file-content")
)
.filter(($el) => {
let $data = $el.querySelector(".data");
return (
$data &&
$data.innerText.replace(/\s/g, "") ===
"Filerenamedwithoutchanges."
);
})
.map(($el) => $el.previousElementSibling)
.forEach(($el) => {
let $checkbox = $el.querySelector(".js-reviewed-checkbox");
if (!$checkbox.checked) {
$checkbox.click();
}
});
},
},
{
title: "Deleted files",
subtitle: "Mark all deleted files as viewed",
onClick: () => {
Array.from(
document.querySelectorAll('.file-header[data-file-deleted="true"]')
).forEach(($el) => {
let $checkbox = $el.querySelector(".js-reviewed-checkbox");
if (!$checkbox.checked) {
$checkbox.click();
}
});
},
},
{
title: "Reset",
subtitle: "Reset all viewed files",
onClick: () => {
Array.from(
document.querySelectorAll(".js-reviewed-checkbox")
).forEach((checkbox) => {
if (checkbox.checked) {
checkbox.click();
}
});
},
},
{
title: "Reset matching pattern",
subtitle: "Reset all viewed files whose filename match the pattern",
onClick: () => {
let query = window.prompt(
"What is the pattern to match? (string based)"
);
Array.from(
document.querySelectorAll(`.file-header[data-path*="${query}"]`)
).forEach(($el) => {
let $checkbox = $el.querySelector(".js-reviewed-checkbox");
if ($checkbox.checked) {
$checkbox.click();
}
});
},
},
],
});
document
.querySelector(".diffbar-item.dropdown.js-reviews-container")
.before($dropdown);
}
function createDropdown({
label,
title,
buttonClassNames = [],
buttonStyle = "",
items,
}) {
const containerId = `${slugify(label)}-details`;
const buttonToRegister = [];
const $dropdown = stringToHTML(`
<details class="details-reset details-overlay f5 position-relative ${buttonClassNames.join(
" "
)}" style="${buttonStyle}" id="${containerId}">
<summary class="btn-sm btn" aria-haspopup="menu" role="button">
<span>${label}&nbsp;</span>
<span class="dropdown-caret"></span>
</summary>
<details-menu class="SelectMenu" role="menu">
<div class="SelectMenu-modal notifications-component-menu-modal">
<header class="SelectMenu-header">
<h3 class="SelectMenu-title">${title}</h3>
</header>
<div class="SelectMenu-list">
${items
.map((item) => {
const buttonId = `${slugify(item.title)}-details__button`;
buttonToRegister.push({
id: buttonId,
onClick: item.onClick,
});
return `
<button class="SelectMenu-item flex-items-start" id="${buttonId}">
<div>
<div class="f5 text-bold">${item.title}</div>
<div class="text-small color-fg-muted text-normal pb-1">${item.subtitle}</div>
</div>
</button>
`;
})
.join("")}
</div>
</div>
</details-menu>
</details>
`);
buttonToRegister.forEach(({ id, onClick }) => {
$dropdown.querySelector(`#${id}`).addEventListener("click", () => {
$dropdown.removeAttribute("open");
onClick();
});
});
return $dropdown;
}
function slugify(text) {
return text
.toString()
.normalize("NFKD")
.toLowerCase()
.trim()
.replace(/\s+/g, "-")
.replace(/[^\w\-]+/g, "")
.replace(/\_/g, "-")
.replace(/\-\-+/g, "-")
.replace(/\-$/g, "");
}
function stringToHTML(str) {
var parser = new DOMParser();
var doc = parser.parseFromString(str, "text/html");
return doc.body.firstChild;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment