您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Give Jira issue page an update button, in the top navigation bar, when the issue is updated while the page is shown. Also update the title.
当前为
// ==UserScript== // @name Jira issue page updated notification // @namespace https://gf.qytechs.cn/users/1047370 // @description Give Jira issue page an update button, in the top navigation bar, when the issue is updated while the page is shown. Also update the title. // @author Marnix Klooster <[email protected]> // @copyright public domain // @license public domain // @version 0.7 // @homepage https://gf.qytechs.cn/en/scripts/462479-jira-issue-page-updated-notification // @include /^https?://(jira\.[^/]*|[^/]*\.atlassian\.net)/(browse|projects/[^/]+/issues)// // @require https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js // @grant none // ==/UserScript== /// TODO list /// /// * If the updated time is not today, show 'yesterday' or other relative date. /// (And perhaps update the button on date change?) /// /// * Perhaps: Also show the type of changes in the 'updated' button's tooltip. /// (It seems this needs both its ...?field=&expand=changelog /// and the issue's full .../comment list, and perhaps more.) /// If this is built, it also would make sense to keep watching for issue updates, /// and keep updating the tooltip (and perhaps the time, see other TODO item). /// We could also use this to ignore changes that the logged in user made., /// so that we can reduce or `issueUpdateDelayInSeconds`, /// and are robust against missing `ISSUE_REFRESHED` events (see other TODO item). /// /// * Bug/limitation: From a query result page (https://jira.infor.com/issues/?jql=...) /// clicking on a specific issue, the page is updated 'in place', /// so even though the address bar URL changes, this userscript is not activated. /// See if there is a way to fix that. /// /// * Make this script work on https://jira.infor.com/projects/SOMEPROJ/issues/SOMEPROJ-... pages. /// /// * Better error handling around unexpected URLs; stop retrying if no issue name can be found? /// /// * Robustness in case `aui-nav` element does not exist. /// /// * Perhaps: Wait longer after an error response, to reduce server load? /// /// * Perhaps: If another update is done, update the time on the button. /// END of TODO list "use strict"; /// Configuration settings /// (also look at @include and @match in the manifest above, /// which you can usually override in your userscript browser extension configuration) /// Setting the following too high will overload the Jira server; /// setting it too low will not show 'updated' notifications while editing an issue. var timeBetweenChecksInSeconds = 10; /// Setting the following too low will give a useless 'updated' notification for your own edits; /// setting it too high will make you miss notifications. var issueUpdateDelayInSeconds = 5; // the max observed time from browser issue change to issue `updated` timestamp /// END of Configuration settings /// Helper functions /// From https://stackoverflow.com/a/35385518/223837 function htmlToElement(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } /// END of Helper functions // Documentation about the Jira REST API that is used here can be found at // https://docs.atlassian.com/software/jira/docs/api/REST/latest/ // (currently https://docs.atlassian.com/software/jira/docs/api/REST/9.7.0/), // specifically // // - https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-getIssue // - https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-getComments (function () { var pageLastUpdatedInMillis = Date.now(); // `undefined` means that we are not checking for issue updates function regularlyCheckForUpdates() { if (!pageLastUpdatedInMillis) { console.log(`ERROR: internal inconsistency, we don't know when this page was last updated...`); return; } var issueNumber = new URL(window.location.href).pathname.split('/').findLast((_) => true); console.log(`Checking whether issue ${issueNumber} has been recently updated`); $.ajax({url:`/rest/api/latest/issue/${issueNumber}?fields=updated`, type:"GET", dataType:"json", contenType: "application/json", success: function(response) { var issueLastUpdated = luxon.DateTime.fromISO(response.fields.updated); console.log(`Issue ${issueNumber} was updated ${(Date.now() - issueLastUpdated.toMillis())/1000} seconds ago`); console.log(`This page was updated ${(Date.now() - pageLastUpdatedInMillis)/1000} seconds ago`); if (pageLastUpdatedInMillis + issueUpdateDelayInSeconds*1000 < issueLastUpdated.toMillis()) { // issue was updated after the page was loaded var issueLastUpdatedText = issueLastUpdated.toLocaleString(luxon.DateTime.TIME_WITH_SHORT_OFFSET, {}); console.log(`Issue updated after last page update, just now at ${issueLastUpdatedText}: showing update button and updating title`); document.title = `\u21BB ${document.title}`; // We put the button as the last in the <ul class="aui-nav"> top navigation bar. // (The button is the same as the 'Create' button; `href="#"` is needed for the correct hover color.) var updateButtonElement = htmlToElement(` <li id="marnix_update_page_button"> <a href="#" class="aui-button aui-button-primary aui-style" title="Update page" >Update page (issue changed ${issueLastUpdatedText})</a> </li> `); updateButtonElement.addEventListener("click", updateThisPage); document.getElementsByClassName("aui-nav")[0].appendChild(updateButtonElement); // From now on leave the page alone, the `ISSUE_REFRESHED` handler (below) will re-enable the regular check pageLastUpdatedInMillis = undefined; return; } console.log(`No recent issue ${issueNumber} update, will check again in a little while.`); setTimeout(regularlyCheckForUpdates, timeBetweenChecksInSeconds*1000); }, error: function(xhr, textStatus, errorThrown) { console.log(`Something went wrong checking for updates of issue ${issueNumber}, will retry in a little while: ${textStatus} : ${errorThrown}`); setTimeout(regularlyCheckForUpdates, timeBetweenChecksInSeconds*1000); } }); } function updateThisPage() { console.log(`Let this page update its information (which also updates the title)`); JIRA.trigger(JIRA.Events.REFRESH_ISSUE_PAGE, [JIRA.Issue.getIssueId()]); // this event is caught by the event handler below, which will re-enable the regular check for issue updates // (or it triggers a full page reload, sometimes) } JIRA.bind(JIRA.Events.ISSUE_REFRESHED, function (e, context) { console.log(`Something triggered a refresh of this page`); if (!pageLastUpdatedInMillis) { console.log(`We will start to look for issue updates again in a little while`); setTimeout(regularlyCheckForUpdates, timeBetweenChecksInSeconds*1000); } pageLastUpdatedInMillis = Date.now(); var updateButtonElement = document.getElementById('marnix_update_page_button') if (updateButtonElement) { console.log(`We can remove the update button again`); updateButtonElement.remove(); } }); JIRA.bind(JIRA.Events.INLINE_EDIT_SAVE_COMPLETE, function (e, context) { console.log(`INLINE_EDIT_SAVE_COMPLETE (e.g., inline Description change), force page update`); updateThisPage(); }); regularlyCheckForUpdates(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址