Last active
December 17, 2018 18:07
-
-
Save sirbrillig/6473dff58511c1672d644af33f078535 to your computer and use it in GitHub Desktop.
Allows collapsing Phabricator notifications to one-per-revision
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
// ==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 <payton@foolord.com> | |
// @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)); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FYI: this has been updated and given its own repo here: https://github.com/sirbrillig/phabricator-grouping