Skip to content

Instantly share code, notes, and snippets.

@sirbrillig
Last active December 17, 2018 18:07
Show Gist options
  • Save sirbrillig/6473dff58511c1672d644af33f078535 to your computer and use it in GitHub Desktop.
Save sirbrillig/6473dff58511c1672d644af33f078535 to your computer and use it in GitHub Desktop.
Allows collapsing Phabricator notifications to one-per-revision
// ==UserScript==
// @name Phabricator Notification Grouping
// @namespace https://gist.github.com/sirbrillig/6473dff58511c1672d644af33f078535
// @version 0.1
// @description Allows collapsing Phabricator notifications to one-per-revision
// @author Payton Swick <[email protected]>
// @match https://code.a8c.com/notification/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
function getRevisionFromNotificationNode(node) {
const idNode = node.querySelector('.phui-handle:nth-of-type(2)');
return idNode ? idNode.getAttribute('href') : null;
}
function createNoteFromNotificationNode(node) {
return {
node,
id: getRevisionFromNotificationNode(node),
collapsed: false,
styleCopy: {
maxHeight: node.style.maxHeight,
overflow: node.style.overflow,
padding: node.style.padding,
border: node.style.border,
},
};
}
function groupNotesById(notes) {
return notes.reduce((grouped, note) => {
if (! note.id) {
return grouped;
}
if (! grouped[note.id]) {
grouped[note.id] = {notes:[]};
}
grouped[note.id].notes.push(note);
return grouped;
}, {});
}
function changeNoteStyle(note, key, value) {
note.styleCopy[key] = note.node.style[key];
note.node.style[key] = value;
}
function restoreNoteStyle(note, key) {
note.node.style[key] = note.styleCopy[key];
}
function addCollapsedMarkerToNode(node) {
const collapsedMarker = document.createElement('div');
collapsedMarker.innerText = '⇥ ';
collapsedMarker.style.display = 'inline';
collapsedMarker.style.padding = '0 2px 0 2px';
collapsedMarker.title = 'This Revision has multiple notifications';
collapsedMarker.className = 'phabricator-notification-grouping-collapsed';
node.insertBefore(collapsedMarker, node.firstChild);
}
function removeCollapsedMarkerFromNode(node) {
const collapsedMarker = node.querySelector('.phabricator-notification-grouping-collapsed');
if (collapsedMarker) {
node.removeChild(collapsedMarker);
}
}
function collapseNote(note) {
changeNoteStyle(note, 'maxHeight', 0);
changeNoteStyle(note, 'overflow', 'hidden');
changeNoteStyle(note, 'padding', 0);
changeNoteStyle(note, 'border', 'none');
note.collapsed = true;
return note;
}
function expandNote(note) {
restoreNoteStyle(note, 'maxHeight');
restoreNoteStyle(note, 'overflow');
restoreNoteStyle(note, 'padding');
restoreNoteStyle(note, 'border');
removeCollapsedMarkerFromNode(note.node);
note.collapsed = false;
return note;
}
function collapseNoteGroup(notes) {
if (notes.length < 2) {
return notes;
}
addCollapsedMarkerToNode(notes[0].node);
return [
notes.slice(0,1),
notes.slice(1).map(collapseNote),
];
}
function collapseNotificationsByRevision(notes) {
const groups = groupNotesById(notes);
Object.values(groups).map(group => collapseNoteGroup(group.notes));
return notes;
}
function expandNotifications(notes) {
return notes.map(expandNote);
}
function areNotesCollapsed(notes) {
return notes.reduce((areCollapsed, note) => {
return areCollapsed || note.collapsed;
}, false);
}
function toggleCollapsedNotes(notes) {
if (areNotesCollapsed(notes)) {
return expandNotifications(notes);
}
return collapseNotificationsByRevision(notes);
}
function addCollapseToggleButton() {
const button = document.createElement('a');
button.className = 'button button-grey has-text phui-button-default msl phui-header-action-link';
button.href = '#';
const buttonTitle = document.createElement('div');
buttonTitle.className = 'phui-button-text';
buttonTitle.innerText = 'Group Notifications';
button.appendChild(buttonTitle);
const buttonArea = document.querySelector('.phui-header-action-links');
if (buttonArea) {
buttonArea.appendChild(button);
}
return button;
}
function toggleCollapsedButton(button, isCollapsed) {
button.querySelector('.phui-button-text').innerText = isCollapsed ? 'Expand Notifications' : 'Group Notifications';
}
function getCollapsedState() {
try {
return localStorage.getItem('phabricator-notification-grouping-is-collapsed') === 'true' || false;
} catch (err) {
return false;
}
}
function setCollapsedState(isCollapsed) {
try {
localStorage.setItem('phabricator-notification-grouping-is-collapsed', isCollapsed);
} catch (err) {
}
}
// ------- Main Program -------
let notes = Array.from(document.querySelectorAll('.phabricator-notification')).map(createNoteFromNotificationNode);
const button = addCollapseToggleButton();
button.addEventListener('click', () => {
notes = toggleCollapsedNotes(notes);
setCollapsedState(areNotesCollapsed(notes));
toggleCollapsedButton(button, areNotesCollapsed(notes));
});
if (getCollapsedState()) {
notes = toggleCollapsedNotes(notes);
toggleCollapsedButton(button, areNotesCollapsed(notes));
}
})();
@sirbrillig
Copy link
Author

FYI: this has been updated and given its own repo here: https://github.com/sirbrillig/phabricator-grouping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment