Created an internal plugin using tampermonkey to see the latest comment in a jira ticket within a list view.

// ==UserScript==
// @name Jira Latest Comment Hover
// @namespace [jiraurl]
// @version 1.0
// @description Show latest comment when hovering over a Jira issue in a queue
// @author You
// @match [jiraurl]/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect [jiraurl]
// @require https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
(function() {
'use strict';
// Fetch the stored email and API key
const EMAIL = "[email]";
const API_TOKEN = "[token]";
const JIRA_BASE_URL = "[jiraurl]";
// Add tooltip styles
GM_addStyle(`
.jira-tooltip {
position: absolute;
background: #2E7D32; /* Green color */
color: #ffffff; /* White text */
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
max-width: 300px;
word-wrap: break-word;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
z-index: 9999;
display: none;
}
`);
let tooltip = document.createElement("div");
tooltip.className = "jira-tooltip";
document.body.appendChild(tooltip);
// Simple cache to store comments
const commentCache = new Map();
function fetchLatestComment(issueKey, callback) {
// Check cache first
if (commentCache.has(issueKey)) {
callback(commentCache.get(issueKey));
return;
}
const authHeader = "Basic " + btoa(`${EMAIL}:${API_TOKEN}`);
GM_xmlhttpRequest({
method: "GET",
url: `${JIRA_BASE_URL}/rest/api/3/issue/${issueKey}?fields=comment`,
headers: {
"Accept": "application/json",
"Authorization": authHeader
},
onload: function(response) {
try {
if (response.status === 200) {
const issueData = JSON.parse(response.responseText);
const comments = issueData.fields.comment?.comments || [];
let latestComment = "No comments available";
if (comments.length > 0) {
const lastComment = comments[comments.length - 1];
if (typeof lastComment.body === 'string') {
latestComment = lastComment.body;
} else if (lastComment.body.content) {
latestComment = lastComment.body.content
.map(block => {
if (block.type === 'paragraph') {
return block.content
.map(text => text.text || '')
.join('');
}
return '';
})
.join('\n')
.trim();
}
}
// Cache the result
commentCache.set(issueKey, latestComment);
callback(latestComment);
} else {
const errorMsg = `Error ${response.status}: Unable to load comment`;
commentCache.set(issueKey, errorMsg);
callback(errorMsg);
}
} catch (error) {
console.error("Error processing response:", error);
callback("Error loading comment");
}
},
onerror: function(error) {
console.error("Request failed:", error);
callback("Failed to fetch comment");
}
});
}
function showTooltip(event, text) {
const maxWidth = 300;
const padding = 10;
tooltip.textContent = text;
tooltip.style.display = "block";
let left = event.pageX + padding;
let top = event.pageY + padding;
// Adjust position if tooltip would go off screen
if (left + maxWidth > window.innerWidth) {
left = window.innerWidth - maxWidth - padding;
}
if (top + tooltip.offsetHeight > window.innerHeight) {
top = event.pageY - tooltip.offsetHeight - padding;
}
tooltip.style.left = `${left}px`;
tooltip.style.top = `${top}px`;
}
function hideTooltip() {
tooltip.style.display = "none";
}
// Initialize the hover functionality
function init() {
// Add hover listeners
document.addEventListener("mouseover", (event) => {
const issueElement = event.target.closest('[data-issue-key]');
if (issueElement) {
const issueKey = issueElement.getAttribute("data-issue-key");
fetchLatestComment(issueKey, (comment) => {
showTooltip(event, comment);
});
}
});
document.addEventListener("mouseout", (event) => {
if (event.target.closest('[data-issue-key]')) {
hideTooltip();
}
});
}
// Wait for page to be ready
if (document.readyState === "complete") {
init();
} else {
window.addEventListener("load", init);
}
})();