Jira New Tab Flow: Open Tickets in a New Tab, Not in Popup

Enhance your Jira experience open any clicked issue in a new tab

// ==UserScript==
// @name         Jira New Tab Flow: Open Tickets in a New Tab, Not in Popup
// @namespace    http://tampermonkey.net/
// @version      2
// @description  Enhance your Jira experience open any clicked issue in a new tab
// @icon         https://static-00.iconduck.com/assets.00/jira-icon-512x512-kkop6eik.png
// @author       Ameer Jamal
// @match        https://*.atlassian.net/jira/*
// @match        https://*.atlassian.com/jira/*
// @grant        none
// @require      https://unpkg.com/sweetalert@2/dist/sweetalert.min.js
// ==/UserScript==

(function() {
    'use strict';

    // Variables to store state information
    let lastSelectedIssue = null; // The last issue that was selected
    let isActive = true; // Whether the script is currently active
    let escapeHitCounter = 0; // Count of consecutive "Ctrl" key presses
    let justActivated = true; // Flag to indicate that the script was just activated
    let button = null; // Button for toggling script activation
    let buttonActiveText = 'Tickets Currently Open  in New Tab'; // Text for when the script is enabled
    let buttonInactiveText ='Tickets Currently Open in SideBar' ; // Text for when script is disabled
    let Red = "#DE350B"
    let Green = "#1F875A"
    // Function to toggle the active state of the script
    const toggleActiveState = () => {
        isActive = !isActive; // Toggle active state
        if (isActive) {
            justActivated = true;
            swal({ // Show a success alert when activated
                title: "Clicking on a ticket now opens it in a new tab",
                icon: "success",
                buttons: false,
                timer: 1500,
                allowEscapeKey: false
            });
        } else {
            lastSelectedIssue = null;
            swal({ // Show an error alert when deactivated
                title: "Clicking on a ticket opens in the sidebar as default",
                icon: "error",
                buttons: false,
                timer: 1500,
                allowEscapeKey: false
            });
        }

        // Update the button text and color based on the current state
        if (button) {
    button.textContent = !isActive ? buttonInactiveText : buttonActiveText; // Set the button text content
    button.style.color = 'white'; // Set the button text color
    button.style.backgroundColor = !isActive ? Red : Green; // Set the button background color
        }

        // Reset the control key press counter
        escapeHitCounter = 0;
    };

    // Function to check if a new issue is selected
    const checkForSelectedIssue = () => {
        if (!isActive) { // Skip if the script is not active
            return;
        }

        // Get the currently selected issue
        const urlParams = new URLSearchParams(window.location.search);
        const urlDomain = window.location.hostname;
        const selectedIssue = urlParams.get('selectedIssue');

        // Open the selected issue in a new tab if it's different from the last one
        if (selectedIssue && selectedIssue !== lastSelectedIssue && !justActivated) {
            window.open(`https://${urlDomain}/browse/${selectedIssue}`, '_blank');
        }

        lastSelectedIssue = selectedIssue;
        justActivated = false;
    };

    // Create a mutation observer to detect DOM changes
    const observer = new MutationObserver(checkForSelectedIssue);

    // Start observing the body of the page for changes in the child list and the subtree
    observer.observe(document.querySelector('body'), {
        childList: true,
        subtree: true
    });

    // Add an event listener for the keyup event
    window.addEventListener('keyup', (event) => {
        // Check if the "Ctrl" key was pressed
        if (event.key === 'Control') {
            escapeHitCounter += 1;

            // Toggle active state if the "Ctrl" key was pressed twice in a row
            if (escapeHitCounter === 2) {
                toggleActiveState();
            }

            // Reset the control key press counter after a short delay
            setTimeout(() => {
                escapeHitCounter = 0;
            }, 300);
        }
    });

    // Function to create the button for toggling script activation
    const createButton = () => {
    button = document.createElement('span'); // Create a new span element
    button.setAttribute('class', 'css-1gd7hga'); // Set the class attribute
    button.textContent = !isActive ? buttonInactiveText : buttonActiveText; // Set the button text content
    button.style.color = 'white'; // Set the button text color
    button.style.backgroundColor = !isActive ? Red : Green; // Set the button background color
    button.style.borderRadius = '10px'; // Round the corners of the button
    button.style.cursor = 'pointer'; // Change the cursor when hovering over the button
     // Add styles to make it look more like a button
    button.style.padding = "20px 15px"; // 20px top-bottom, 15px left-right
    button.style.margin = "20px 0"; // 20px top-bottom, 0 left-right
    button.style.cursor = "pointer"; // Change cursor to pointer on hover to indicate clickability

    button.style.textDecoration = 'none'; // Remove underline from text
    button.addEventListener('click', toggleActiveState); // Add an event listener for the click event

    return button;
    };

    // Function to replace the "Learn more" button with our custom button
    const replaceButton = () => {
        const learnMoreButton = document.querySelector('[data-item-description=true] .css-8nt2sa'); // Get the "Learn more" button

        if (learnMoreButton && learnMoreButton.textContent.includes('Learn more')) { // Check if the "Learn more" button exists
            const newButton = createButton(); // Create our custom button
            learnMoreButton.parentNode.replaceChild(newButton, learnMoreButton); // Replace the "Learn more" button with our custom button
        }
    };

    replaceButton(); // Replace the "Learn more" button immediately when the script is loaded

    // Create another mutation observer to detect when the "Learn more" button is added to the page
    const buttonObserver = new MutationObserver(replaceButton);

    // Start observing the body of the page for changes in the child list and the subtree
    buttonObserver.observe(document.querySelector('body'), {
        childList: true,
        subtree: true
    });
})();