WhatsApp Right-Click Dropdown Menu

Opens the options menu of a message on right-click in WhatsApp Web, replacing normal right click behavior.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WhatsApp Right-Click Dropdown Menu
// @namespace    https://web.whatsapp.com/
// @version      1.1
// @license      CC BY-NC-SA 4.0
// @description  Opens the options menu of a message on right-click in WhatsApp Web, replacing normal right click behavior.
// @author       Dan
// @match        *://web.whatsapp.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function attachContextMenuHandler(node) {
        if (node.classList && (node.classList.contains('message-out') || node.classList.contains('message-in'))) {
            node.addEventListener('contextmenu', function(event) {
                // If the target is a link, image, or selected text, allow the default context menu
                const target = event.target;
                if (target.nodeName === 'A' || target.nodeName === 'IMG' || window.getSelection().toString().length > 0) {
                    return; // Don't prevent default behavior
                }

                event.preventDefault();

                // Try to find the contextual menu button for the message
                let menuButton = node.querySelector('._ahkm'); // Selector for the contextual menu button

                // If it's not found, try another selector (in case of changes in the structure)
                if (!menuButton) {
                    menuButton = node.querySelector('[role="button"][aria-label="Context menu"]');
                }

                if (menuButton) {
                    menuButton.click(); // Open the options menu
                }

                // Make the message blink
                blinkMessage(node);
            });
        }
    }

    function blinkMessage(node) {
        // Add the class that causes the blinking effect
        node.classList.add('blink');

        // Remove the class after a short period (blinking)
        setTimeout(function() {
            node.classList.remove('blink');
        }, 500); // The message will blink for 500ms
    }

    // CSS style for blinking
    const style = document.createElement('style');
    style.innerHTML = `
        .blink {
            animation: blink 0.5s alternate;
        }

        @keyframes blink {
            0% {
                opacity: 1;
            }
            50% {
                opacity: 0;
            }
            100% {
                opacity: 1;
            }
        }
    `;
    document.head.appendChild(style);

    // Observe the chat container for new messages
    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === 1) { // Ensure it's an element
                    attachContextMenuHandler(node);
                    node.querySelectorAll('.message-out, .message-in').forEach(attachContextMenuHandler);
                }
            });
        });
    });

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