Gmail Sender Icons

Quickly identify the sender of email messages in Gmail without opening the message. Now marks emails in red if the domain is not allowed, and applies changes to all Gmail tabs.

// ==UserScript==
// @name           Gmail Sender Icons
// @name:es        Gmail Iconos de Remitente
// @namespace      http://tampermonkey.net/
// @version        1.5
// @description    Quickly identify the sender of email messages in Gmail without opening the message. Now marks emails in red if the domain is not allowed, and applies changes to all Gmail tabs.
// @description:es Identifica rápidamente al remitente de los mensajes de correo en Gmail sin abrir el mensaje. Ahora marca los correos en rojo si el dominio no está permitido y aplica cambios a todas las pestañas de Gmail.
// @author         IgnaV
// @match          https://mail.google.com/*
// @icon           https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico
// @license        MIT
// @grant          GM_setValue
// @grant          GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    if (window.self !== window.top) return;

    const userId = window.location.href.match(/\/u\/(\d+)\//)[1];

    const addIcon = GM_getValue('addIcon', true);
    const addDomain = GM_getValue('addDomain', true);
    let allowedCommonDomains = GM_getValue('allowedDomains', []);
    let allowedUserDomains = GM_getValue('allowedUserDomains', {});

    let userDomains = allowedUserDomains[userId] || [];
    allowedUserDomains[userId] = userDomains;

    GM_setValue('addIcon', addIcon);
    GM_setValue('addDomain', addDomain);
    GM_setValue('allowedUserDomains', allowedUserDomains);

    let allowedDomains = allowedCommonDomains.concat(userDomains);
    const hasDomains = allowedDomains.length !== 0;

    const channel = new BroadcastChannel('mi-canal');
    channel.onmessage = (event) => {
        allowedCommonDomains = GM_getValue('allowedDomains', []);
        allowedUserDomains = GM_getValue('allowedUserDomains', {});
        allowedDomains = allowedCommonDomains.concat(userDomains);
        updateAllDomainStates();
        console.log('Mensaje recibido:', event.data);
    };

    if (!addIcon && !addDomain && !hasDomains) return;

    const processedElements = new Set();

    function addDomainContainer(element, domain) {
        const domainContainer = document.createElement('div');
        domainContainer.className = 'domain-container';
        domainContainer.onclick = () => domainContainerEvent(domainContainer, domain);
        updateDomainState(domainContainer, domain, userDomains, allowedCommonDomains);

        addIconToContainer(domainContainer, domain);
        addDomainToContainer(domainContainer, domain);
        element.appendChild(domainContainer);
        return domainContainer;
    }

    function updateDomainState(container, domain, userDomains, globalDomains) {
        container.classList.remove('not-allowed-domain', 'allowed-domain');
        if (userDomains.includes(domain) || globalDomains.includes(domain)) {
            container.classList.add('allowed-domain');
        } else {
            container.classList.add('not-allowed-domain');
        }
    }

    function domainContainerEvent(domainContainer, domain) {
        event.preventDefault();
        event.stopPropagation();

        let message;
        if (userDomains.includes(domain)) {
            userDomains.splice(userDomains.indexOf(domain), 1);
            allowedCommonDomains.push(domain);
            message = 'Añadido a global';
        } else if (allowedCommonDomains.includes(domain)) {
            allowedCommonDomains.splice(allowedCommonDomains.indexOf(domain), 1);
            message = 'Eliminado';
        } else {
            userDomains.push(domain);
            message = 'Añadido a personal';
        }

        allowedUserDomains[userId] = userDomains;
        GM_setValue('allowedUserDomains', allowedUserDomains);
        GM_setValue('allowedDomains', allowedCommonDomains);

        updateAllDomainStates();

        showTooltip(domainContainer, message);

        setTimeout(() => channel.postMessage(message), 200);
    };

    function updateAllDomainStates() {
        const nameElements = document.querySelectorAll('.bA4, .bAK, .bAp');
        nameElements.forEach((element) => {
            const emailElement = element.querySelector('[email]');
            if (!emailElement) return;

            const email = emailElement.getAttribute('email');
            const domain = extractDomain(email);
            const domainContainer = element.querySelector('.domain-container');

            if (domainContainer) {
                updateDomainState(domainContainer, domain, userDomains, allowedCommonDomains);
            } else {
                addDomainContainer(element, domain);
            }
        });
    }

    function showTooltip(element, message) {
        const tooltip = document.createElement('span');
        tooltip.className = 'custom-tooltip';
        tooltip.textContent = message;

        element.appendChild(tooltip);
        setTimeout(() => {
            if (element.contains(tooltip)) {
                element.removeChild(tooltip);
            }
        }, 2000);
    }

    function addIconToContainer(domainContainer, domain) {
        const icon = document.createElement('img');
        icon.src = `https://www.google.com/s2/favicons?domain=${domain}`;
        icon.className = 'domain-icon';
        domainContainer.appendChild(icon);
    }

    function addDomainToContainer(domainContainer, domain) {
        const domainSpan = document.createElement('span');
        domainSpan.className = 'domain-text';
        domainSpan.textContent = domain;
        domainContainer.appendChild(domainSpan);
    }

    function addStyles(addIcon, addDomain, hasDomains) {
        const style = document.createElement('style');
        style.type = 'text/css';
        let css = ``;
        if (addIcon || addDomain) {
            css += `
                .bA4, .bAK, .bAp {
                    padding-top: 9px;
                }
                .domain-container {
                    display: flex;
                    align-items: center;
                    margin-top: -4px;
                    font-size: 10px;
                    color: #888;
                    width: fit-content;
                    height: 11px;
                    padding: 1px 2px;
                }
            `;
        }
        if (addIcon) {
            css += `
                .domain-icon {
                    width: 10px;
                    height: 10px;
                    margin-right: 3px;
                }
            `;
        }
        if (addDomain) {
            css += `
                .domain-text {
                    white-space: nowrap;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    font-size: 10px;
                    color: #888;
                }
            `;
        }
        if (hasDomains) {
            css += `
                .not-allowed-domain {
                    background-color: #f8d7da;
                    color: #721c24;
                }
                .allowed-domain {
                    background-color: transparent;
                    color: inherit;
                }
                .custom-tooltip {
                    position: absolute;
                    background-color: #000;
                    color: #fff;
                    padding: 4px;
                    border-radius: 4px;
                    font-size: 12px;
                    white-space: nowrap;
                    z-index: 1000;
                    top: 40px;
                    opacity: 0;
                    transition: opacity 0.3s ease-in-out;
                }
                .domain-container:hover .custom-tooltip {
                    opacity: 1;
                }
            `;
        }
        style.appendChild(document.createTextNode(css));
        document.head.appendChild(style);
    }

    addStyles(addIcon, addDomain, hasDomains);

    function addDomainBelowName() {
        const nameElements = document.querySelectorAll('.bA4, .bAK, .bAp');

        nameElements.forEach((element) => {
            if (processedElements.has(element)) return;

            const emailElement = element.querySelector('[email]');
            if (!emailElement) return;

            const email = emailElement.getAttribute('email');
            const domain = extractDomain(email);

            const domainElement = addDomainContainer(element, domain);

            processedElements.add(element);
        });
    }

    function extractDomain(email) {
        const domainParts = email.split('@')[1].split('.');
        if (domainParts[domainParts.length - 2] === 'com') {
            return domainParts.slice(-3).join('.');
        }
        return domainParts.slice(-2).join('.');
    }

    const observer = new MutationObserver((mutations) => {
        mutations.forEach(() => {
            addDomainBelowName();
        });
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    window.addEventListener('load', () => {
        addDomainBelowName();
    });
})();