// ==UserScript== // @name GitHub Issue Comments // @version 1.4.7 // @description A userscript that toggles issues/pull request comments & messages // @license MIT // @author Rob Garrison // @namespace https://github.com/Mottie // @match https://github.com/* // @run-at document-idle // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163 // @icon https://github.githubassets.com/pinned-octocat.svg // @updateURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-issue-comments.user.js // @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-issue-comments.user.js // @supportURL https://github.com/Mottie/GitHub-userscripts/issues // ==/UserScript== (() => { "use strict"; GM_addStyle(` .ghic-button { float:right; } .ghic-button .btn:hover div.select-menu-modal-holder { display:block; top:auto; bottom:25px; right:0; } .ghic-right { position:absolute; right:10px; top:9px; } .ghic-button .select-menu-header, .ghic-participants { cursor:default; display:block; } .ghic-participants { border-top:1px solid #484848; padding:15px; } .ghic-avatar { display:inline-block; float:left; margin: 0 2px 2px 0; cursor:pointer; position:relative; } .ghic-avatar:last-child { margin-bottom:5px; } .ghic-avatar.comments-hidden svg { display:block; position:absolute; top:-2px; left:-2px; z-index:1; } .ghic-avatar.comments-hidden img { opacity:0.5; } .ghic-button .dropdown-item { font-weight:normal; position:relative; } .ghic-button .dropdown-item span { font-weight:normal; opacity:.5; } .ghic-button .dropdown-item.ghic-has-content span { opacity:1; } .ghic-button .dropdown-item.ghic-checked span { font-weight:bold; } .ghic-button .dropdown-item.ghic-checked svg, .ghic-button .dropdown-item:not(.ghic-checked) .ghic-count { display:inline-block; } .ghic-button .dropdown-item:not(.ghic-checked) { text-decoration:line-through; } .ghic-button .ghic-count { margin-left:5px; } .ghic-button .select-menu-modal { margin:0; } .ghic-button .ghic-participants { margin-bottom:20px; } /* for testing: ".ghic-hidden { opacity: 0.3; } */ body .ghic-hidden { display:none !important; } .ghic-hidden-participant, body .ghic-avatar svg, .dropdown-item.ghic-checked .ghic-count, .ghic-hide-reactions .TimelineItem .comment-reactions, .select-menu-header.ghic-active + .select-menu-list .dropdown-item:not(.ghic-has-content) { display:none; } .ghic-menu-wrapper input[type=checkbox] { height:0; width:0; visibility:hidden; position:absolute; } .ghic-menu-wrapper .ghic-toggle { cursor:pointer; text-indent:-9999px; width:20px; height:10px; background:grey; display:block; border-radius:10px; position:relative; } .ghic-menu-wrapper .ghic-toggle:after { content:''; position:absolute; top:0; left:1px; width:9px; height:9px; background:#fff; border-radius:9px; transition:.3s; } .ghic-menu-wrapper input:checked + .ghic-toggle { background:#070; } .ghic-menu-wrapper input:checked + .ghic-toggle:after { top:0; left:calc(100% - 1px); transform:translateX(-100%); } .ghic-menu-wrapper .ghic-toggle:active:after { width:13px; } .TimelineItem.ghic-highlight .comment { border-color:#800 !important; } `); const regex = /(svg|path)/i; // ZenHub addon active (include ZenHub Enterprise) const hasZenHub = $(".zhio, .zhe") ? true : false; const exceptions = [ "ghsr-sort-block" // sort reactions block (github-sort-reactions.user.js) ]; const settings = { // example: https://github.com/Mottie/Keyboard/issues/448 title: { isHidden: false, name: "ghic-title", selector: ".TimelineItem-badge .octicon-pencil", containsText: "changed the title", label: "Title Changes" }, labels: { isHidden: false, name: "ghic-labels", selector: ".TimelineItem-badge .octicon-tag", containsText: "label", label: "Label Changes" }, state: { isHidden: false, name: "ghic-state", selector: `.TimelineItem-badge .octicon-primitive-dot, .TimelineItem-badge .octicon-circle-slash`, label: "State Changes (close/reopen)" }, // example: https://github.com/jquery/jquery/issues/2986 milestone: { isHidden: false, name: "ghic-milestone", selector: ".TimelineItem-badge .octicon-milestone", label: "Milestone Changes" }, refs: { isHidden: false, name: "ghic-refs", selector: ".TimelineItem-badge .octicon-bookmark", containsText: "referenced", label: "References" }, mentioned: { isHidden: false, name: "ghic-mentions", selector: ".TimelineItem-badge .octicon-bookmark", containsText: "mentioned", label: "Mentioned" }, assigned: { isHidden: false, name: "ghic-assigned", selector: ".TimelineItem-badge .octicon-person", label: "Assignment Changes" }, // Pull Requests commits: { isHidden: false, name: "ghic-commits", selector: `.TimelineItem-badge .octicon-repo-push, .TimelineItem-badge .octicon-git-commit`, wrapper: ".js-timeline-item", label: "Commits" }, forcePush: { isHidden: false, name: "ghic-force-push", selector: ".TimelineItem-badge .octicon-repo-force-push", label: "Force Push" }, // example: https://github.com/jquery/jquery/pull/3014 reviews: { isHidden: false, name: "ghic-reviews", selector: `.TimelineItem-badge .octicon-eye, .TimelineItem-badge .octicon-x, .TimelineItem-badge .octicon-check, .js-resolvable-timeline-thread-container`, wrapper: ".js-timeline-item", label: "Reviews (All)" }, outdated: { isHidden: false, name: "ghic-outdated", selector: ".js-resolvable-timeline-thread-container .Label--outline[title*='Outdated']", wrapper: ".js-resolvable-timeline-thread-container", label: "- Reviews (Outdated)" }, resolved: { isHidden: false, name: "ghic-resolved", selector: ".js-resolvable-timeline-thread-container[data-resolved='true']", label: "- Reviews (Resolved)" }, diffNew: { isHidden: false, name: "ghic-diffNew", selector: ".js-resolvable-timeline-thread-container", notSelector: ".Label--outline[title*='Outdated']", wrapper: ".js-resolvable-timeline-thread-container", label: "- Reviews (Current)" }, // example: https://github.com/jquery/jquery/pull/2949 merged: { isHidden: false, name: "ghic-merged", selector: ".TimelineItem-badge .octicon-git-merge", label: "Merged" }, integrate: { isHidden: false, name: "ghic-integrate", selector: ".TimelineItem-badge .octicon-rocket", label: "Integrations" }, // bot: { // isHidden: false, // name: "ghic-bot", // selector: ".Label--outline", // containsText: "bot", // label: "Bot" // }, // similar comments similar: { isHidden: false, name: "ghic-similar", selector: `.js-discussion > .Details-element.details-reset:not([open]), #js-progressive-timeline-item-container > .Details-element.details-reset:not([open])`, label: "Similar comments" }, // extras (special treatment - no selector) plus1: { isHidden: false, name: "ghic-plus1", label: "+1 Comments", callback: hidePlus1, }, reactions: { isHidden: false, name: "ghic-reactions", label: "Reactions", callback: hideReactions, }, projects: { isHidden: false, name: "ghic-projects", selector: `.discussion-item-added_to_project, .discussion-item-moved_columns_in_project, .discussion-item-removed_from_project`, label: "Project Changes" }, // Jenkins auto-merged autoMerged: { isHidden: false, name: "ghic-automerged", selector: ".Details a[title*='auto-merged' i]", label: "Auto merged" }, // Jenkins temp deployments that have become inactive inactive: { isHidden: false, name: "ghic-inactive", selector: ".deployment-status-label.is-inactive, .Label[title*='inactive' i]", label: "Inactive deployments" }, // page with lots of users to hide: // https://github.com/isaacs/github/issues/215 // ZenHub pipeline change pipeline: { isHidden: false, name: "ghic-pipeline", selector: ".TimelineItem-badge .zh-icon-board-small", label: "ZenHub Pipeline Changes" } }; const iconHidden = ``; const plus1Icon = `:+1:`; function addMenu() { if ($("#discussion_bucket") && !$(".ghic-button")) { // update "isHidden" values getSettings(); let name, isHidden, isChecked, list = "", keys = Object.keys(settings), onlyActive = GM_getValue("onlyActive", false), header = $(".discussion-sidebar-item:last-child"), menu = document.createElement("div"); for (name of keys) { if (!(name === "pipeline" && !hasZenHub)) { isHidden = settings[name].isHidden; isChecked = isHidden ? "" : "ghic-checked"; list += ``; } } menu.className = "ghic-button"; menu.innerHTML = `
`; if (hasZenHub) { header.insertBefore(menu, header.childNodes[0]); } else { header.appendChild(menu); } addAvatars(); } update(); } function addAvatars() { let indx = 0; let str = ""; const list = $(".ghic-list"); const unique = $$("span.ghic-avatar", list).map(el => el.getAttribute("aria-label")); // get all avatars const avatars = $$(".TimelineItem-avatar img"); const len = avatars.length - 1; // last avatar is the new comment with the current user const updateAvatars = () => { list.innerHTML += str; str = ""; }; const loop = () => { let el, name; let max = 0; while (max < 50 && indx <= len) { if (indx > len) { return updateAvatars(); } el = avatars[indx]; name = (el.getAttribute("alt") || "").replace("@", ""); if (!unique.includes(name)) { str += ` ${iconHidden} `; unique[unique.length] = name; max++; } indx++; } updateAvatars(); if (indx < len) { setTimeout(() => { window.requestAnimationFrame(loop); }, 200); } }; loop(); } function getSettings() { const keys = Object.keys(settings); for (let name of keys) { settings[name].isHidden = GM_getValue(settings[name].name, false); } } function saveSettings() { const keys = Object.keys(settings); for (let name of keys) { GM_setValue(settings[name].name, settings[name].isHidden); } } function getInputValues() { const keys = Object.keys(settings); const menu = $(".ghic-menu"); for (let name of keys) { if (!(name === "pipeline" && !hasZenHub)) { const item = $(`.${settings[name].name}`, menu).closest(".dropdown-item"); if (item) { settings[name].isHidden = !$("input", item).checked; toggleClass(item, "ghic-checked", !settings[name].isHidden); } } } } function hideStuff(name, init) { const obj = settings[name]; const item = $(".ghic-menu .dropdown-item." + obj.name); if (item) { const isHidden = obj.isHidden; if (typeof obj.callback === "function") { obj.callback({ obj, item, init }); } else if (obj.selector) { let results = $$(obj.selector).map(el => el.closest(obj.wrapper || ".TimelineItem, .Details-element") ); if (obj.containsText) { results = results.filter( el => el && el.textContent.includes(obj.containsText) ); } if (obj.notSelector) { results = results.filter(el => el && !$(obj.notSelector, el)); } toggleClass(item, "ghic-checked", !isHidden); if (isHidden) { const count = addClass(results, "ghic-hidden"); $(".ghic-count", item).textContent = count ? `(${count} hidden)` : " "; } else if (!init) { // no need to remove classes on initialization removeClass(results, "ghic-hidden"); } toggleClass(item, "ghic-has-content", results.length); } } } function hideReactions({ obj, item }) { toggleClass($("body"), "ghic-hide-reactions", obj.isHidden); toggleClass(item, "ghic-has-content", $$(".has-reactions").length > 0); // make first comment reactions visible const origPost = $(".TimelineItem .comment-reactions"); if (origPost && origPost.classList.contains("has-reactions")) { origPost.style.display = "block"; } } function hidePlus1({ item, init }) { let max, indx = 0, count = 0, total = 0; // keep a list of post authors to prevent duplicate +1 counts const authors = []; // used https://github.com/isaacs/github/issues/215 for matches here... // matches "+1!!!!", "++1", "+!", "+99!!!", "-1", "+ 100", "thumbs up"; ":+1:^21425235" // ignoring -1's... add unicode for thumbs up; it gets replaced with an image in Windows const regexPlus = /([?!*,.:^[\]()\'\"+-\d]|bump|thumbs|up|\ud83d\udc4d)/gi; // other comments to hide - they are still counted towards the +1 counter (for now?) // seen "^^^" to bump posts; "bump plleeaaassee"; "eta?"; "pretty please" // "need this"; "right now"; "still nothing?"; "super helpful"; "for gods sake" const regexHide = new RegExp("(" + [ "@\\w+", "\\b(it|is|a|so|the|and|no|on|oh|do|this|any|very|much|here|just|my|me|too|want|yet|image)\\b", "pretty", "pl+e+a+s+e+", "plz", "totally", "y+e+s+", "eta", "fix", "right", "now", "hope(ful)?", "still", "wait(ed|ing)?", "nothing", "really", "add(ed|ing)?", "need(ed|ing)?", "updat(es|ed|ing)?", "(months|years)\\slater", "back", "features?", "infinity", // +Infinity "useful", "super", "helpful", "thanks", "for\\sgod'?s\\ssake", "c['emon]+" // c'mon, com'on, comeon ].join("|") + ")", "gi"); // image title ":{anything}:", etc. const regexEmoji = /(:.*:)|[\u{1f300}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{1f900}-\u{1f9ff}]/gu; const regexWhitespace = /\s+/g; const comments = $$(".js-discussion .TimelineItem").filter(comment => { const classes = comment.className.split(" "); return !exceptions.some(ex => classes.includes(ex)); }); const len = comments.length; const loop = () => { let wrapper, el, tmp, txt, img, hasLink, dupe; max = 0; while (max < 20 && indx < len) { if (indx >= len) { if (init) { item.classList.toggle("ghic-has-content", count > 0); } return; } wrapper = comments[indx]; // save author list to prevent repeat +1s el = $(".timeline-comment-header .author", wrapper); txt = (el ? el.textContent || "" : "").toLowerCase(); dupe = true; if (txt && authors.indexOf(txt) < 0) { authors[authors.length] = txt; dupe = false; } // .js-comments-holder wraps review comments el = $(".comment-body, .js-comments-holder", wrapper); if (el) { // ignore quoted messages, but get all fragments tmp = $$(".email-fragment", el); // some posts only contain a link to related issues; these should not be counted as a +1 // see https://github.com/isaacs/github/issues/618#issuecomment-200869630 hasLink = $$(tmp.length ? ".email-fragment .issue-link" : ".issue-link", el).length; if (tmp.length) { // ignore quoted messages txt = getAllText(tmp); } else { txt = (el ? el.textContent || "" : "").trim(); } if (!txt) { img = $("img", el); if (img) { txt = img.getAttribute("title") || img.getAttribute("alt"); } } // remove fluff txt = (txt || "") .replace(regexEmoji, "") .replace(regexHide, "") .replace(regexPlus, "") .replace(regexWhitespace, " ") .trim(); if (txt === "" || (txt.length <= 4 && !hasLink)) { if (init && !settings.plus1.isHidden) { // +1 Comments has-content item.classList.toggle("ghic-has-content", true); return; } if (settings.plus1.isHidden) { wrapper.classList.add("ghic-hidden", "ghic-highlight"); total++; // one +1 per author if (!dupe) { count++; } } else if (!init) { wrapper.classList.remove("ghic-hidden"); } max++; } } indx++; } if (indx < len) { setTimeout(() => { window.requestAnimationFrame(loop); }, 200); } else { if (init) { item.classList.toggle("ghic-has-content", count > 0); } $(".ghic-menu .ghic-plus1 .ghic-count").textContent = total ? "(" + total + " hidden)" : " "; addCountToReaction(count); } }; loop(); } function getAllText(els) { let txt = ""; let indx = els.length; // text order doesn't matter while (indx--) { txt += els[indx].textContent.trim(); } return txt; } function addCountToReaction(count) { if (!count) { count = ($(".ghic-menu .ghic-plus1 .ghic-count").textContent || "") .replace(/[()]/g, "") .trim(); } const origPost = $(".timeline-comment"); const hasPositiveReaction = $( ".has-reactions button[value='THUMBS_UP react'], .has-reactions button[value='THUMBS_UP unreact']", origPost ); let el = $(".ghic-count", origPost); if (el) { // the count may have been appended to the comment & now // there is a reaction, so remove any "ghic-count" elements el.parentNode.removeChild(el); } if (count) { if (hasPositiveReaction) { el = document.createElement("span"); el.className = "ghic-count"; el.textContent = count ? " + " + count + " (from hidden comments)" : ""; hasPositiveReaction.appendChild(el); } else { el = document.createElement("p"); el.className = "ghic-count"; el.innerHTML = "
" + plus1Icon + " " + count + " (from hidden comments)"; $(".comment-body", origPost).appendChild(el); } } } function hideParticipant(el) { if (el) { el.classList.toggle("comments-hidden"); let name = el.getAttribute("aria-label"); const results = $$(".TimelineItem, .commit-comment, .discussion-item") .filter(el => { const author = $(".js-discussion .author", el); return author ? name === author.textContent.trim() : false; }); // use a different participant class name to hide timeline events // or unselecting all users will show everything if (el.classList.contains("comments-hidden")) { addClass(results, "ghic-hidden-participant"); } else { removeClass(results, "ghic-hidden-participant"); } results = []; } } function update() { if ($("#discussion_bucket") && $(".ghic-button")) { const keys = Object.keys(settings); let indx = keys.length; while (indx--) { // true flag for init - no need to remove classes hideStuff(keys[indx], true); } addAvatars(); } } function checkItem(event) { if (document.getElementById("discussion_bucket")) { const menuItem = event.target; const wrap = menuItem && menuItem.closest(".dropdown-item, .ghic-participants"); if (menuItem && wrap) { if (menuItem.nodeName === "INPUT") { getInputValues(); saveSettings(); const name = wrap.dataset.ghic; if (name) { hideStuff(name); } } else if (menuItem.classList.contains("ghic-avatar")) { // make sure we're targeting the span wrapping the image hideParticipant(menuItem.nodeName === "IMG" ? menuItem.parentNode : menuItem ); } else if (regex.test(menuItem.nodeName)) { // clicking on the SVG may target the svg or path inside hideParticipant(menuItem.closest(".ghic-avatar")); } } else if (menuItem.id === "ghic-only-active") { menuItem .closest(".select-menu-header") .classList .toggle("ghic-active", menuItem.checked); GM_setValue("onlyActive", menuItem.checked); } // Make button show if it is active const button = $(".ghic-button .btn"); if (button) { const active = $$(".ghic-hidden, .ghic-hidden-participant").length > 0; button.classList.toggle("btn-outline", active); } } } function $(selector, el) { return (el || document).querySelector(selector); } function $$(selector, el) { return [...(el || document).querySelectorAll(selector)]; } function addClass(els, name) { let indx; const len = els.length; for (indx = 0; indx < len; indx++) { els[indx] && els[indx].classList.add(name); } return len; } function removeClass(els, name) { let indx; const len = els.length; for (indx = 0; indx < len; indx++) { els[indx] && els[indx].classList.remove(name); } } function toggleClass(els, name, flag) { els = Array.isArray(els) ? els : [els]; const undef = typeof flag === "undefined"; let indx = els.length; while (indx--) { const el = els[indx]; if (el) { if (undef) { flag = !el.classList.contains(name); } if (flag) { el.classList.add(name); } else { el.classList.remove(name); } } } } function init() { getSettings(); addMenu(); $("body").addEventListener("click", checkItem); update(); } // update list when content changes document.addEventListener("ghmo:container", addMenu); document.addEventListener("ghmo:comments", update); init(); })(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy