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 React from 'dom-chef';
import cache from 'webext-storage-cache';
import select from 'select-dom';
import delegate from 'delegate-it';
import CheckIcon from 'octicon/check.svg';
import elementReady from 'element-ready';
import * as pageDetect from 'github-url-detection';
import features from '.';
import * as api from '../github-helpers/api';
import {getRepoGQL, getRepoURL} from '../github-helpers';
const reviewsFilterSelector = '#reviews-select-menu';
function addDropdownItem(dropdown: HTMLElement, title: string, filterCategory: string, filterValue: string): void {
const filterQuery = `${filterCategory}:${filterValue}`;
const searchParameter = new URLSearchParams(location.search);
const currentQuerySegments = searchParameter.get('q')?.split(/\s+/) ?? [];
const isSelected = currentQuerySegments.some(
segment => segment.toLowerCase() === filterQuery
);
const query = currentQuerySegments.filter(
segment => !segment.startsWith(`${filterCategory}:`)
).join(' ');
const search = new URLSearchParams({
q: query + (isSelected ? '' : ` ${filterQuery}`)
});
dropdown.append(
<a
href={`?${String(search)}`}
className="SelectMenu-item"
aria-checked={isSelected ? 'true' : 'false'}
role="menuitemradio"
>
<CheckIcon className="SelectMenu-icon SelectMenu-icon--check"/>
<span>{title}</span>
</a>
);
}
const hasDraftFilter = new WeakSet();
function addDraftFilter({delegateTarget: reviewsFilter}: delegate.Event): void {
if (hasDraftFilter.has(reviewsFilter)) {
return;
}
hasDraftFilter.add(reviewsFilter);
const dropdown = select('.SelectMenu-list', reviewsFilter)!;
dropdown.append(
<div className="SelectMenu-divider">
Filter by draft pull requests
</div>
);
addDropdownItem(dropdown, 'Ready for review', 'draft', 'false');
addDropdownItem(dropdown, 'Not ready for review (Draft PR)', 'draft', 'true');
}
const hasChecks = cache.function(async (): Promise<boolean> => {
const {repository} = await api.v4(`
repository(${getRepoGQL()}) {
head: object(expression: "HEAD") {
... on Commit {
history(first: 10) {
nodes {
statusCheckRollup {
state
}
}
}
}
}
}
`);
return repository.head.history.nodes.some((commit: AnyObject) => commit.statusCheckRollup);
}, {
maxAge: 3,
cacheKey: () => __filebasename + ':' + getRepoURL()
});
async function addChecksFilter(): Promise<void> {
const reviewsFilter = await elementReady(reviewsFilterSelector);
if (!reviewsFilter) {
return;
}
if (!await hasChecks()) {
return;
}
// Copy existing element and adapt its content
const checksFilter = reviewsFilter.cloneNode(true);
checksFilter.id = '';
select('summary', checksFilter)!.firstChild!.textContent = 'Checks\u00A0'; // Only replace text node, keep caret
select('.SelectMenu-title', checksFilter)!.textContent = 'Filter by checks status';
const dropdown = select('.SelectMenu-list', checksFilter)!;
dropdown.textContent = ''; // Drop previous filters
for (const status of ['Success', 'Failure', 'Pending']) {
addDropdownItem(dropdown, status, 'status', status.toLowerCase());
}
reviewsFilter.after(checksFilter);
}
async function init(): Promise<void> {
delegate(document, reviewsFilterSelector, 'toggle', addDraftFilter, true);
await addChecksFilter();
}
void features.add({
id: __filebasename,
description: 'Adds Checks and Draft PR dropdown filters in PR lists.',
screenshot: 'https://user-images.githubusercontent.com/202916/74453250-6d9de200-4e82-11ea-8fd4-7c0de57e001a.png'
}, {
include: [
pageDetect.isPRList
],
waitForDomReady: false,
init
});
|