// ==UserScript==
// @name Github Notifications Dropdown
// @namespace joeytwiddle
// @author joeytwiddle
// @contributors SkyzohKey, Marti, darkred
// @copyright 2014-2022, Paul "Joey" Clark (http://neuralyte.org/~joey)
// @version 2.0.1
// @license MIT
// @description When clicking the notifications icon, displays notifications in a dropdown pane, without leaving the current page.
// @include https://github.com/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @grant GM_addStyle
// @downloadURL none
// ==/UserScript==
/* eslint-env jquery */
// bug: If the notifications list is longer than the page, scroll down to the bottom and then try to click on the white space below the Github document's content. The event does not fire there!
// When using @grant none then we should also avoid messing with the page's jQuery (if it has one)
this.$ = this.jQuery = jQuery.noConflict(true);
// ==Options==
// Fetch notifications where you are a participant, before all notifications
var showParticipatingNotificationsFirst = true;
var makeBlocksCollapsableOnNotificationsPage = true;
// Disabled by default because it was conflicting with other scripts (https://github.com/joeytwiddle/code/issues/2)
var makeAllFileAndDiffBlocksCollapsable = false;
// If you want to change the colour of the blue notification dot, uncomment one of the following
var notificationDotStyle = '';
// Github's blue dot (2017)
//var notificationDotStyle = 'linear-gradient(hsl(212, 100%, 66%), hsl(212, 100%, 46%))';
// Github's blue dot (2016)
//var notificationDotStyle = 'linear-gradient(hsl(214, 50%, 65%), hsl(214, 50%, 50%))';
// Strong red dot
//var notificationDotStyle = 'linear-gradient(hsla(0, 80%, 75%, 1), hsla(0, 80%, 50%, 1))';
// Calm amber dot
//var notificationDotStyle = 'linear-gradient(hsla(35, 90%, 65%, 1), hsla(35, 90%, 40%, 1))';
// Gentle green dot
//var notificationDotStyle = 'linear-gradient(hsla(120, 50%, 65%, 1), hsla(120, 50%, 40%, 1))';
var hideQuodAIWarning = true;
// ==/Options==
var mainNotificationsPath = '/notifications';
var notificationsToFetch = [
{
title: 'Participating',
path: '/notifications?query=reason%3Aparticipating+is%3Aunread',
},
{
title: 'Mentions',
path: '/notifications?query=reason%3Amention+is%3Aunread',
},
{
title: 'All notifications',
path: '/notifications?query=is%3Aunread',
},
];
var notificationButtonLinkSelector = 'header a.notification-indicator[href]';
var notificationButtonLink = null;
var notificationButtonContainer = null;
var closeClickTargets = 'body';
var notificationsDropdown = null;
var tabArrow = null;
function listenForNotificationClick() {
//notificationButtonContainer.on('click', onNotificationButtonClicked);
$('body').on('click', notificationButtonLinkSelector, onNotificationButtonClicked);
}
function onNotificationButtonClicked(evt) {
// Act normally (do nothing) if a modifier key is pressed, or if it was a right or middle click.
if (evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey || evt.which !== 1) {
return;
}
evt.preventDefault();
if (isNotificationsDropdownOpen()) {
closeNotificationsDropdown();
return;
}
// We used to set these when the script loaded, but now GitHub is dynamically loading some of the content, it's better to regenerate them when needed
notificationButtonLink = $(notificationButtonLinkSelector);
// In v1, the click listener was on the containing
so we had to listen there
//var notificationButtonContainer = notificationButtonLink.closest("li");
// In v2, the listener needs to go on the link
notificationButtonContainer = notificationButtonLink;
// We used to make it fall back to its default behaviour after the first click, but now that GitHub is more like a Single Page App, we prefer to keep it alive, and trust that it works properly!
//notificationButtonContainer.off('click', onNotificationButtonClicked);
// For GM 4.0 we must use an absolute path, so we use .prop() instead of .attr(). "This is an issue with Firefox and content scripts"
var targetPage = notificationButtonLink.prop('href');
// When Microsoft revamped the notifications, I don't think the icon ever shows anything different
var notificationPagesToTry = notificationsToFetch.slice(0);
fetchNotifications(notificationPagesToTry);
}
function fetchNotifications(notificationPagesToTry) {
var currentAttempt = notificationPagesToTry.shift();
var title = currentAttempt.title;
var targetPage = 'https://github.com' + currentAttempt.path;
var morePagesToTry = notificationPagesToTry.length > 0;
notificationButtonContainer.css({
opacity: '0.3',
outline: 'none',
});
$.ajax({
url: targetPage,
dataType: 'html',
}).then((data, textStatus, jqXHR) => {
var notificationPage = $('').append($.parseHTML(data));
var countNotifications = notificationPage.find('.notifications-list').find('.notifications-list-item').length;
var hasNotifications = countNotifications > 0;
if (hasNotifications || !morePagesToTry) {
receiveNotificationsPage(targetPage, title, data, textStatus, jqXHR);
} else {
console.log('No notifications on', targetPage, 'but we still have others we can try:', notificationPagesToTry);
fetchNotifications(notificationPagesToTry);
}
}).fail(receiveNotificationsPage);
}
function receiveNotificationsPage(targetPage, title, data, textStatus, jqXHR) {
notificationButtonContainer.css('opacity', '');
notificationsDropdown = $('
').addClass('notifications-dropdown');
var titleElem = $('
').append( $('').text(title) );
notificationsDropdown.prepend( $("").append(titleElem) );
var notificationPage = $('').append($.parseHTML(data));
var notificationsList = notificationPage.find('.notifications-list');
// Provide hover text for all links, so if the text is too long to display, it can at least be seen on hover.
notificationsList.find('a').each(function() {
$(this).attr('title', $(this).text().trim().replace(/[ \n]+/g, ' '));
// Remove the query params which make the target page scroll down and show a banner about notifications
// That might be nice when you're coming from the notifications page, but it interferes with the flow of using this script (because it pushes the nofications bell icon off the top of the viewport, and because we already marked it as done)
this.href = this.href.replace(/[?].*/, '');
});
var minWidth = Math.min(700, window.innerWidth - 48);
if (notificationsList.children().length === 0) {
notificationsDropdown.append('
No new notifications');
minWidth = 200;
}
notificationsDropdown.append(notificationsList);
var linkToPage = mainNotificationsPath;
//var linkToPage = targetPage;
var seeAll = $('
Go to notifications page');
seeAll.on('click', () => closeNotificationsDropdown());
notificationsList.append(seeAll);
var arrowSize = 10;
//var dropdownBackgroundColor = 'var(--color-notifications-row-bg) !important';
var dropdownBackgroundColor = '#f8f8f8';
var unreadBackgroundColor = dropdownBackgroundColor;
//var readOrDoneBackgroundColor = 'var(--color-canvas-subtle) !important';
//var readOrDoneBackgroundColor = '#f0f3f6';
var readOrDoneBackgroundColor = '#edf0f3';
// In v2, this appears on the notifications page, but not other pages.
// It is needed to activate some of the CSS for the notifications list.
document.body.classList.add('notifications-v2');
$('