Jira Copy Ticket

Adds a "Copy Ticket" button to copy the title and ticket link as rich text

当前为 2023-11-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Jira Copy Ticket
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Adds a "Copy Ticket" button to copy the title and ticket link as rich text
  6. // @author Othman Shareef (othmanosx@gmail.com)
  7. // @match https://eventmobi.atlassian.net/*
  8. // @icon https://www.google.com/s2/favicons?domain=atlassian.net
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12. (function() {
  13. 'use strict';
  14. let button
  15. let debounceTimer;
  16.  
  17. // Wait for the Jira ticket title element to be available
  18. const waitForElement = (selector, callback) => {
  19. const element = document.querySelector(selector);
  20. if (element) {
  21. callback();
  22. } else {
  23. setTimeout(() => {
  24. waitForElement(selector, callback);
  25. }, 500);
  26. }
  27. };
  28.  
  29. // copy ticket title to clipboard
  30. const copyTicketTitle = () => {
  31. button.innerText = 'Loading...';
  32. const ticketTitleElement = document.querySelector('[data-testid="issue.views.issue-base.foundation.summary.heading"]');
  33. const ticketLinkElement = document.querySelector('[data-testid="issue.views.issue-base.foundation.breadcrumbs.current-issue.item"]');
  34. if (ticketTitleElement && ticketLinkElement) {
  35. const ticketTitle = ticketTitleElement.innerText;
  36. const ticketLink = ticketLinkElement.href
  37. const ticketID = ticketLinkElement.firstChild.innerText
  38. const html = `<a href="${ticketLink}">${ticketID}: ${ticketTitle}</a>`
  39. const clipboardItem = new ClipboardItem({
  40. 'text/html': new Blob([html], {
  41. type: 'text/html'
  42. }),
  43. 'text/plain': new Blob([html], {
  44. type: 'text/plain'
  45. })
  46. });
  47. navigator.clipboard.write([clipboardItem]).then(_ => {
  48. // Change button text to "Copied!" for a moment
  49. button.innerText = 'Copied!';
  50. setTimeout(() => {
  51. // Change button text back to the original text after a delay
  52. button.innerText = 'Copy Ticket';
  53. }, 1000); // You can adjust the delay (in milliseconds) as needed
  54. }, error => alert(error));
  55. } else {
  56. alert('Ticket title element not found!');
  57. }
  58. };
  59.  
  60. // Add button next to the ticket title
  61. const addButton = () => {
  62. const existingCopyBtn = document.getElementById("copy-button")
  63. if(existingCopyBtn) return
  64. const copyButton = document.createElement('button');
  65. copyButton.innerText = 'Copy Ticket';
  66. copyButton.className = 'css-1xewsy6';
  67. copyButton.id = "copy-button"
  68. copyButton.style.marginLeft = '10px';
  69. copyButton.addEventListener('click', copyTicketTitle);
  70.  
  71. // Add hover and click effects
  72. copyButton.addEventListener('mouseover', () => {
  73. copyButton.style.filter = 'brightness(2) contrast(2)';
  74. });
  75. copyButton.addEventListener('mouseout', () => {
  76. copyButton.style.filter = 'none';
  77. });
  78. copyButton.addEventListener('mousedown', () => {
  79. copyButton.style.filter = 'brightness(0.1) contrast(0.1)';
  80. });
  81. copyButton.addEventListener('mouseup', () => {
  82. copyButton.style.filter = 'none';
  83. });
  84.  
  85. button = copyButton
  86. const element = document.querySelector('[data-testid="issue.views.issue-base.foundation.quick-add.link-button.ui.link-dropdown-button"]');
  87. element.parentElement.parentElement.parentElement.appendChild(copyButton);
  88. };
  89.  
  90. const debounce = (func, delay) => {
  91. clearTimeout(debounceTimer);
  92. debounceTimer = setTimeout(func, delay);
  93. };
  94.  
  95. // Use MutationObserver to detect changes in the DOM
  96. const observer = new MutationObserver(() => {
  97. debounce(() => {
  98. waitForElement('[data-testid="issue.views.issue-base.foundation.summary.heading"]', addButton)
  99. }, 100);
  100. });
  101.  
  102. // Observe changes in the body and its descendants
  103. observer.observe(document.body, {
  104. childList: true,
  105. subtree: true,
  106. });
  107.  
  108. })();