-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbitbucket_pr_comment_quick_delete.user.js
More file actions
146 lines (131 loc) · 6.85 KB
/
Copy pathbitbucket_pr_comment_quick_delete.user.js
File metadata and controls
146 lines (131 loc) · 6.85 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// ==UserScript==
// @name Bitbucket PR Comment Quick Delete Button
// @namespace https://github.com/StaticPH
// @match https://bitbucket.org/*projects/*/repos/*/pull-requests/*/overview
// @version 1.1.0
// @createdAt 11/2/2023, 3:34:18 PM
// @author StaticPH
// @description Moves the "Delete" button on PR comments out of a submenu, and further allows skipping the confirmation prompt when the shift key is held.
// @license MIT
// @updateURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/bitbucket_pr_comment_quick_delete.user.js
// @downloadURL https://raw.githubusercontent.com/StaticPH/Userscripts/master/bitbucket_pr_comment_quick_delete.user.js
// @homepageURL https://github.com/StaticPH/UserScripts
// @supportURL https://github.com/StaticPH/UserScripts/issues
// @icon https://bitbucket.org/favicon.ico
// @grant none
// @run-at document-start
// @noframes
// ==/UserScript==
(async function(){
"use strict";
function mergeObjects(destinationObj, sourceObj) {
// loop all properties
for (const propKey in sourceObj) {
if(sourceObj.hasOwnProperty(propKey)) {
// leaf
if(typeof(sourceObj[propKey]) !== 'object' || !destinationObj[propKey]) {
destinationObj[propKey] = sourceObj[propKey];
continue;
}
// node
sourceObj && mergeObjects(destinationObj[propKey], sourceObj[propKey]);
}
}
return destinationObj;
}
const [currentProject, currentRepo, currentPR] = document.location.pathname.replace('/projects/','').replace('/repos/', '/').replace('/pull-requests/', '/').split('/').slice(0,3);
const __commentFetchOptionTemplate = {
'headers': {
'accept': 'application/json, text/javascript, */*; q=0.01',
'accept-language': 'en-US',
'content-type': 'application/json'
},
'body': null,
'cache': 'no-cache',
'mode': 'cors',
'credentials': 'include',
};
const __deletePRComment_options = mergeObjects({method: 'DELETE'}, __commentFetchOptionTemplate);
const __getPRComment_options = mergeObjects({method: 'GET'}, __commentFetchOptionTemplate);
function __PRComment_addressBuilder(commentID) {
return `${document.location.origin}/rest/api/latest/projects/${currentProject}/repos/${currentRepo}/pull-requests/${currentPR}/comments/${commentID}`;
// Nominally equivalent to: return `${document.location.origin}/rest/api/latest${document.location.pathname.split('/').slice(0, -1).join('/')}/comments/${commentID}?version=0`, except version unfortunately isn't implicit;
}
const __locale = {
'en-US': {
'toolTipHintNoConfirm': 'Hold Shift to skip confirmation',
'btnLabelDelete': 'Delete',
'consoleNoConfirmCommentDelete': 'shiftKey held, deleting comment without prompting for confirmation.',
'consoleCantDeleteComment': 'Error: unable to delete this comment.',
'consoleCantTargetComment': 'Error: unable to target this comment.',
'consoleSeekingCommentsWithoutButton': 'Seeking comments in need of quick delete button.',
'consoleRemindTimerVar': 'window.newCommentChecker holds the id of a timer which will repeat the scan every 3 seconds.',
},
localize: function(stringID) {
const preferredLang = this[navigator.language];
return (preferredLang && preferredLang[stringID]) ? preferredLang[stringID]: (this['en-US'][stringID] || '');
}
};
const commentDeleteBtnHTML = `<li class="action-item action-delete">
<button class="css-1vtwu8t commentDeleteButton" data-testid="comment-delete-button" type="button" tabindex="0" title="${__locale.localize('toolTipHintNoConfirm')}">
<span class="css-1we84oz">${__locale.localize('btnLabelDelete')}</span>
</button>
</li>`;
function commentDeletionNotPermitted(deleteBtn) {
deleteBtn.toggleAttribute('disabled');
console.error(__locale.localize('consoleCantDeleteComment'));
}
async function onClickQuickDelete(evnt) {
const closestBtn = evnt.target.closest('button');
if (! closestBtn || ! closestBtn.matches('.commentDeleteButton')) { return; }
const commentActions = evnt.target.closest('.comment-actions');
const commentID = evnt.target.closest('.comment').dataset.commentId;
const dropdownBtn = commentActions.querySelector(':scope > div:not(.comment-add-reaction) .Droplist__Trigger-sc-1z05y4v-3 > div > button.css-jiizgi');
if (! dropdownBtn) {
closestBtn.toggleAttribute('disabled');
return console.error(__locale.localize('consoleCantTargetComment')); // Execute function and return undefined, but in one line.
}
if (evnt.shiftKey) {
console.log(__locale.localize('consoleNoConfirmCommentDelete'));
// Send a request to determine the number of times the comment has been modified, because for some silly reason,
// that's a critical detail when deleting comments, despite the lack of comment history tracking.
// It seems odd that the API wont allow deleting comments with no regard for whether they've been edited, but it is what it is.
const version = await fetch( __PRComment_addressBuilder(commentID), __getPRComment_options )
.then(response => response.json())
.then(json => json.version);
// Send the network request for deletion directly, bypassing confirmation.
await fetch(`${__PRComment_addressBuilder(commentID)}?version=${version ? version : 0}`, __deletePRComment_options).then(
function onSuccess() { evnt.target.closest('.activity-item').remove(); },
function onFailure() { commentDeletionNotPermitted(closestBtn); }
);
}
else {
dropdownBtn.click();
requestAnimationFrame(() => undefined); // Briefly delay to allow the dropdown to populate
const builtinDeleteBtn = commentActions.querySelector('.styledContentContainer-p0j3f7-0 [role="menuitem"][data-testid="delete-comment"]');
if (! builtinDeleteBtn) {
return commentDeletionNotPermitted(closestBtn); // Execute function and return undefined, but in one line.
}
else {
builtinDeleteBtn.click();
}
}
}
function addQuickDeleteButtons() {
// Find all comment action-lists, and if they haven't already been added, add new quicker (either one or two fewer clicks) button for deleting comments.
document.querySelectorAll('.comment-actions > .action-list').forEach(actionList =>
actionList.querySelector(':scope > .action-item.action-delete') || actionList.insertAdjacentHTML('beforeEnd', commentDeleteBtnHTML)
);
}
// function onDocumentLoaded(loadEvnt) {
// Insert new buttons for faster comment deletion
console.log(__locale.localize('consoleSeekingCommentsWithoutButton'));
addQuickDeleteButtons();
console.log(__locale.localize('consoleRemindTimerVar'));
// Set a timer to check for additional comments every 3 seconds
window.newCommentChecker = setInterval(addQuickDeleteButtons, 3000);
// Add a document-scoped event listener to monitor clicks on quick-delete-comment buttons.
document.addEventListener('click', onClickQuickDelete);
// }
// document.addEventListener('load', onDocumentLoaded, {once: true});
})();