From b87ef5d5d886fa3b873a7d74e39453afad1dee37 Mon Sep 17 00:00:00 2001 From: Aniruddha Shastri Date: Fri, 11 Jul 2025 16:57:00 -0500 Subject: [PATCH 1/3] Changes to fix follower-lists in work items --- src/azdo-pr-dashboard.user.js | 45 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/azdo-pr-dashboard.user.js b/src/azdo-pr-dashboard.user.js index 87cdf04..0071043 100644 --- a/src/azdo-pr-dashboard.user.js +++ b/src/azdo-pr-dashboard.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name More Awesome Azure DevOps (userscript) -// @version 3.7.5 +// @version 3.8.0 // @author Alejandro Barreto (NI) // @description Makes general improvements to the Azure DevOps experience, particularly around pull requests. Also contains workflow improvements for NI engineers. // @license MIT @@ -972,14 +972,14 @@ }`); function watchForWorkItemForms() { - eus.globalSession.onEveryNew(document, '.menu-item.follow-item-menu-item-gray', followButton => { + eus.globalSession.onEveryNew(document, '#__bolt-follow', followButton => { followButton.addEventListener('click', async _ => { - await eus.sleep(100); // We need to allow the other handlers to send the request to follow/unfollow. After the request is sent, we can annotate our follows list correctly. - await annotateWorkItemWithFollowerList(document.querySelector('.discussion-messages-right')); + await eus.sleep(1000); // We need to allow the other handlers to send the request to follow/unfollow. After the request is sent, we can annotate our follows list correctly. + await annotateWorkItemWithFollowerList(document.querySelector('.comment-editor.enter-new-comment')); }); }); // Annotate work items (under the comment box) with who is following it. - eus.globalSession.onEveryNew(document, '.discussion-messages-right', async commentEditor => { + eus.globalSession.onEveryNew(document, '.comment-editor.enter-new-comment', async commentEditor => { await annotateWorkItemWithFollowerList(commentEditor); }); } @@ -987,8 +987,8 @@ async function annotateWorkItemWithFollowerList(commentEditor) { document.querySelectorAll('.work-item-followers-list').forEach(e => e.remove()); - const workItemId = commentEditor.closest('.witform-layout').querySelector('.work-item-form-id > span').innerText; - const queryResponse = await fetch(`${azdoApiBaseUrl}/_apis/notification/subscriptionquery?api-version=6.0`, { + const workItemId = getCurrentWorkItemId(); + const queryResponse = await fetch(`${azdoApiBaseUrl}_apis/notification/subscriptionquery?api-version=6.0`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -1007,15 +1007,34 @@ queryFlags: 'alwaysReturnBasicInformation', }), }); - const followers = [...(await queryResponse.json()).value].sort((a, b) => a.subscriber.displayName.localeCompare(b.subscriber.displayName)); const followerList = followers - .map(s => `${s.subscriber.displayName}`) - .join(', ') - || 'Nobody'; + .map(s => `${s.subscriber.displayName}`) + .join(', '); + if (followerList) { + const annotation = `
${followerList}
`; + commentEditor.insertAdjacentHTML('afterend', annotation); + } + } + + function getCurrentWorkItemId() { + // Try getting the link from the work item header, in case this is opened in preview view + const header = document.querySelector('.work-item-form-header'); + const links = header.querySelectorAll('a'); + // Loop through the links and check if their target matches a link for a work item + const workItemLink = Array.from(links).find(link => link.href.includes('_workitems')); + + // Default to the window URL if the find operation fails + let currentUrl = window.location.href; + if(workItemLink) { + currentUrl = workItemLink.href; + } + + const [baseUrl] = currentUrl.split('?'); + const urlSegments = baseUrl.split('/'); + const workItemId = urlSegments[urlSegments.length - 1]; - const annotation = `
${followerList}
`; - commentEditor.insertAdjacentHTML('BeforeEnd', annotation); + return workItemId; } function watchForRepoBrowsingPages(session) { From feb21c5ce328efffddfa06f599492829a9a72d2c Mon Sep 17 00:00:00 2001 From: Aniruddha Shastri Date: Fri, 11 Jul 2025 16:57:39 -0500 Subject: [PATCH 2/3] Fix style warning --- src/azdo-pr-dashboard.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azdo-pr-dashboard.user.js b/src/azdo-pr-dashboard.user.js index 0071043..84e7c58 100644 --- a/src/azdo-pr-dashboard.user.js +++ b/src/azdo-pr-dashboard.user.js @@ -1026,7 +1026,7 @@ // Default to the window URL if the find operation fails let currentUrl = window.location.href; - if(workItemLink) { + if (workItemLink) { currentUrl = workItemLink.href; } From ba20326df1bc63e53c511c228e5c223e4d0032ba Mon Sep 17 00:00:00 2001 From: Aniruddha Shastri Date: Mon, 14 Jul 2025 10:31:01 -0500 Subject: [PATCH 3/3] Reference the closest comment-editor section in the case where a work item is opened from the links section of another work item --- src/azdo-pr-dashboard.user.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/azdo-pr-dashboard.user.js b/src/azdo-pr-dashboard.user.js index 84e7c58..79df467 100644 --- a/src/azdo-pr-dashboard.user.js +++ b/src/azdo-pr-dashboard.user.js @@ -985,9 +985,10 @@ } async function annotateWorkItemWithFollowerList(commentEditor) { - document.querySelectorAll('.work-item-followers-list').forEach(e => e.remove()); + const commentEditorContainer = commentEditor.closest('.new-comment-div'); + commentEditorContainer.querySelectorAll('.work-item-followers-list').forEach(e => e.remove()); - const workItemId = getCurrentWorkItemId(); + const workItemId = getCurrentWorkItemId(commentEditor); const queryResponse = await fetch(`${azdoApiBaseUrl}_apis/notification/subscriptionquery?api-version=6.0`, { method: 'POST', headers: { @@ -1017,9 +1018,10 @@ } } - function getCurrentWorkItemId() { + function getCurrentWorkItemId(commentEditor) { // Try getting the link from the work item header, in case this is opened in preview view - const header = document.querySelector('.work-item-form-header'); + const workItemPage = commentEditor.closest('.work-item-form-page'); + const header = workItemPage.querySelector('.work-item-form-header'); const links = header.querySelectorAll('a'); // Loop through the links and check if their target matches a link for a work item const workItemLink = Array.from(links).find(link => link.href.includes('_workitems'));