Last active
December 12, 2022 12:33
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} </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