Zendesk — Page Event Detector v1.0.1

← Back to User Scripts

Tests techniques for detecting Zendesk page events using the Navigation API and MutationObserver. Logs to the console when navigation occurs, identifies the page type (ticket, filter, or other), and registers a tightly-scoped MutationObserver to detect when the new page has fully loaded. Also processes the page type on initial load without waiting for a navigation event.

Script Content

// ==UserScript==
// @name         Zendesk: Page Event Detector
// @namespace    https://www.timhilton.xyz/user-scripts
// @version      1.0.1
// @description  Tests techniques for detecting Zendesk page events using the Navigation API and MutationObserver.
// @author       Tim Hilton using GitHub Copilot
// @match        https://*.zendesk.com/agent/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const LOG_PREFIX = '[Zendesk: Page Event Detector]';
    console.debug(`${LOG_PREFIX} Script initialised`);

    function getPageType(url) {
        if (/\/agent\/tickets\/\d+/.test(url)) {
            return 'ticket';
        }
        if (/\/agent\/(filters|views)\/\d+/.test(url)) {
            return 'filter';
        }
        return 'other';
    }

    let ticketObserverCallCount = 0;
    let filterObserverCallCount = 0;

    function waitForTicketLoad(url) {
        const mainEl = document.querySelector('main') || document.body;
        let callCount = 0;
        const observer = new MutationObserver((_mutations, obs) => {
            callCount++;
            ticketObserverCallCount++;
            console.debug(`${LOG_PREFIX} Ticket MutationObserver fired (call #${callCount}, total #${ticketObserverCallCount})`);

            if (document.querySelector('div[data-ticket-id]')) {
                const ticketId = url.match(/\/tickets\/(\d+)/)?.[1] ?? 'unknown';
                console.log(`${LOG_PREFIX} ✅ Ticket page fully loaded — ticket ID: ${ticketId}, observer fired ${callCount} time(s): ${url}`);
                obs.disconnect();
                console.debug(`${LOG_PREFIX} Ticket MutationObserver disconnected`);
            }
        });

        observer.observe(mainEl, { childList: true, subtree: true });
        console.debug(`${LOG_PREFIX} Ticket MutationObserver registered on <${mainEl.tagName.toLowerCase()}>`);
    }

    function waitForFilterLoad(url) {
        const mainEl = document.querySelector('main') || document.body;
        let callCount = 0;
        const observer = new MutationObserver((_mutations, obs) => {
            callCount++;
            filterObserverCallCount++;
            console.debug(`${LOG_PREFIX} Filter MutationObserver fired (call #${callCount}, total #${filterObserverCallCount})`);

            if (document.querySelector('[data-test-id="views-list"], [data-garden-id="tables.table"]')) {
                console.log(`${LOG_PREFIX} ✅ Filter page fully loaded — observer fired ${callCount} time(s): ${url}`);
                obs.disconnect();
                console.debug(`${LOG_PREFIX} Filter MutationObserver disconnected`);
            }
        });

        observer.observe(mainEl, { childList: true, subtree: true });
        console.debug(`${LOG_PREFIX} Filter MutationObserver registered on <${mainEl.tagName.toLowerCase()}>`);
    }

    function handleNavigation(url) {
        const pageType = getPageType(url);

        console.debug(`${LOG_PREFIX} Handling navigation to ${pageType} page: ${url}`);
        console.log(`${LOG_PREFIX} 📍 Page type: ${pageType} — ${url}`);

        if (pageType === 'ticket') {
            waitForTicketLoad(url);
        } else if (pageType === 'filter') {
            waitForFilterLoad(url);
        }
    }

    console.debug(`${LOG_PREFIX} Processing initial page URL: ${window.location.href}`);
    handleNavigation(window.location.href);

    if (window.navigation) {
        console.debug(`${LOG_PREFIX} Navigation API available, registering listener`);
        window.navigation.addEventListener('navigate', (event) => {
            const url = event.destination.url;
            console.debug(`${LOG_PREFIX} Navigation API event fired for: ${url}`);
            handleNavigation(url);
        });
        console.log(`${LOG_PREFIX} ✅ Navigation API listener registered`);
    } else {
        console.log(`${LOG_PREFIX} ❌ Navigation API not available in this browser`);
    }
})();