1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
import './open-all-notifications.css';
import React from 'dom-chef';
import select from 'select-dom';
import * as pageDetect from 'github-url-detection';
import {LinkExternalIcon} from '@primer/octicons-react';
import delegate, {DelegateEvent} from 'delegate-it';
import features from '../feature-manager.js';
import openTabs from '../helpers/open-tabs.js';
import {appendBefore} from '../helpers/dom-utils.js';
import observe from '../helpers/selector-observer.js';
// Selector works on:
// https://github.com/notifications (Grouped by date)
// https://github.com/notifications (Grouped by repo)
// https://github.com/notifications?query=reason%3Acomment (which is an unsaved filter)
const notificationHeaderSelector = '.js-check-all-container .js-bulk-action-toasts ~ div .Box-header';
const openUnread = features.getIdentifiers('open-notifications-button');
const openSelected = features.getIdentifiers('open-selected-button');
function getUnreadNotifications(container: ParentNode = document): HTMLElement[] {
return select.all('.notification-unread', container);
}
async function openNotifications(notifications: Element[], markAsDone = false): Promise<void> {
const urls: string[] = [];
for (const notification of notifications) {
urls.push(notification.querySelector('a')!.href);
}
const openingTabs = openTabs(urls);
if (!await openingTabs) {
return;
}
for (const notification of notifications) {
if (markAsDone) {
notification.querySelector('[title="Done"]')!.click();
} else {
// Mark all as read instead
notification.classList.replace('notification-unread', 'notification-read');
}
}
}
async function openUnreadNotifications({delegateTarget, altKey}: DelegateEvent<MouseEvent>): Promise<void> {
const container = delegateTarget.closest('.js-notifications-group') ?? document;
await openNotifications(getUnreadNotifications(container), altKey);
// Remove all now-unnecessary buttons
removeOpenUnreadButtons(container);
}
async function openSelectedNotifications(): Promise<void> {
const selectedNotifications = select.all('.notifications-list-item :checked')
.map(checkbox => checkbox.closest('.notifications-list-item')!);
await openNotifications(selectedNotifications);
if (!select.exists('.notification-unread')) {
removeOpenUnreadButtons();
}
}
function removeOpenUnreadButtons(container: ParentNode = document): void {
for (const button of select.all(openUnread.selector, container)) {
button.remove();
}
}
function addSelectedButton(selectedActionsGroup: HTMLElement): void {
const button = (
<button className={'btn btn-sm ' + openSelected.class} type="button">
<LinkExternalIcon className="mr-1"/>Open
</button>
);
appendBefore(
selectedActionsGroup,
'details',
button,
);
}
function addToRepoGroup(markReadButton: HTMLElement): void {
const repository = markReadButton.closest('.js-notifications-group')!;
if (getUnreadNotifications(repository).length === 0) {
return;
}
markReadButton.before(
<button
type="button"
className={'btn btn-sm mr-2 tooltipped tooltipped-w ' + openUnread.class}
aria-label="Open all unread notifications from this repo"
>
<LinkExternalIcon width={16}/> Open unread
</button>,
);
}
function addToMainHeader(notificationHeader: HTMLElement): void {
if (getUnreadNotifications().length === 0) {
return;
}
notificationHeader.append(
<button className={'btn btn-sm ml-auto d-none ' + openUnread.class} type="button">
<LinkExternalIcon className="mr-1"/>Open all unread
</button>,
);
}
function init(signal: AbortSignal): void {
delegate(openSelected.selector, 'click', openSelectedNotifications, {signal});
delegate(openUnread.selector, 'click', openUnreadNotifications, {signal});
observe(notificationHeaderSelector + ' .js-notifications-mark-selected-actions', addSelectedButton, {signal});
observe(notificationHeaderSelector, addToMainHeader, {signal});
observe('.js-grouped-notifications-mark-all-read-button', addToRepoGroup, {signal});
}
void features.add(import.meta.url, {
include: [
pageDetect.isNotifications,
],
exclude: [
pageDetect.isBlank, // Empty notification list
],
init,
});
|