Add quick edit button to hover toolbar
- // ==UserScript==
- // @name Slack: Quick Edit Button
- // @namespace Violentmonkey Scripts
- // @match https://app.slack.com/client/*
- // @grant none
- // @version 1.1
- // @author GorvGoyl
- // @supportURL https://github.com/gorvGoyl/
- // @description Add quick edit button to hover toolbar
- // @description 7/9/2024, 9:54:40 PM
- // @license MIT
- // ==/UserScript==
- var sender = "";
- // Function to create and insert the button
- function insertButton() {
- // Check if the button already exists to avoid duplicates
- if (isEditPresent()) {
- console.debug("btn already present so skipping");
- return;
- }
- // Create the new button element
- const newButton = document.createElement("button");
- // Set the button classes
- newButton.classList.add(
- "c-button-unstyled",
- "c-icon_button",
- "c-icon_button--size_small",
- "c-message_actions__button",
- "c-icon_button--default",
- "custom-edit-button"
- );
- // Set the SVG content inside the button
- newButton.innerHTML = `
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
- <path fill="currentColor" d="M5 19h1.425L16.2 9.225L14.775 7.8L5 17.575zm-1 2q-.425 0-.712-.288T3 20v-2.425q0-.4.15-.763t.425-.637L16.2 3.575q.3-.275.663-.425t.762-.15t.775.15t.65.45L20.425 5q.3.275.437.65T21 6.4q0 .4-.138.763t-.437.662l-12.6 12.6q-.275.275-.638.425t-.762.15zM19 6.4L17.6 5zm-3.525 2.125l-.7-.725L16.2 9.225z"/>
- </svg>
- `;
- // Set the onClick behavior
- newButton.onclick = async () => {
- // Find and click the "more_message_actions" button
- const moreActionsButton = document.querySelector(
- 'button[data-qa="more_message_actions"]'
- );
- if (moreActionsButton) {
- moreActionsButton.click();
- const editMessageButton = await getElement(
- 'button[data-qa="edit_message"]'
- );
- if (editMessageButton) {
- editMessageButton.click();
- setTimeout(() => {
- getMessageActionsContainer()?.remove();
- }, 400);
- }
- }
- };
- // Find the "start_thread" button
- const startThreadButton = document.querySelector(
- 'button[data-qa="start_thread"]'
- );
- // Insert the new button before the "start_thread" button
- if (startThreadButton) {
- startThreadButton.parentNode.insertBefore(newButton, startThreadButton);
- console.debug("btn added");
- }
- }
- function debounce(func, wait) {
- let timeout;
- return function (...args) {
- clearTimeout(timeout);
- timeout = setTimeout(() => func.apply(this, args), wait);
- };
- }
- // Function to observe mutations with debounce
- function observeMutations(targetNode) {
- // console.debug("observeMutations");
- const debouncedInsertButton = debounce(insertButton, 50); // 200ms debounce
- // Create a mutation observer
- const observer = new MutationObserver((mutationsList) => {
- for (const mutation of mutationsList) {
- if (mutation.type === "childList" || mutation.type === "attributes") {
- const messageActionsContainer = getMessageActionsContainer();
- if (
- messageActionsContainer &&
- isSender(messageActionsContainer) &&
- !isEditPresent()
- ) {
- debouncedInsertButton();
- }
- }
- }
- });
- // Configure the observer to watch for changes in the subtree
- observer.observe(targetNode, {
- attributes: true,
- childList: true,
- subtree: true,
- });
- }
- function getMessageActionsContainer() {
- return document.querySelector(
- "div.c-message_actions__container.c-message__actions>div.c-message_actions__group"
- );
- }
- // Find the slack kit list element and add a hover event listener to start observing
- async function init() {
- const slackKitList = await getElement("div.p-workspace__primary_view_body");
- if (slackKitList) {
- observeMutations(slackKitList);
- } else {
- console.error("couldnt find element");
- }
- }
- init();
- function isSender(messageActionsContainer) {
- if (!sender) {
- sender = document
- .querySelector('[data-qa="user-button"]')
- ?.getAttribute("aria-label")
- ?.replace("User: ", "");
- }
- if (!sender) {
- return;
- }
- const kitactions = messageActionsContainer?.closest(
- "div.c-message_kit__actions"
- );
- if (!kitactions) {
- return false;
- }
- // if it's the first row
- let name = kitactions
- ?.querySelector('button[data-qa="message_sender_name"]')
- ?.innerText?.trim();
- // if it's the 2nd row
- if (!name) {
- name = kitactions
- ?.querySelector("div.c-message_kit__gutter__right>span.offscreen")
- ?.innerText?.trim();
- }
- return name == sender;
- }
- function isEditPresent() {
- return document.querySelector("button.custom-edit-button");
- }
- // helper menthod: get element whenever it becomes available
- function getElement(selector) {
- return new Promise((resolve, reject) => {
- // Check if the element already exists
- const element = document.querySelector(selector);
- if (element) {
- resolve(element);
- return;
- }
- // Create a MutationObserver to listen for changes in the DOM
- const observer = new MutationObserver((mutations, observer) => {
- // Check for the element again within each mutation
- const element = document.querySelector(selector);
- if (element) {
- observer.disconnect(); // Stop observing
- resolve(element);
- }
- });
- // Start observing the document body for child list changes
- observer.observe(document.body, { childList: true, subtree: true });
- // Set a timeout to reject the promise if the element isn't found within 10 seconds
- const timeoutId = setTimeout(() => {
- observer.disconnect(); // Ensure to disconnect the observer to prevent memory leaks
- resolve(null); // Resolve with null instead of rejecting to indicate the timeout without throwing an error
- }, 10000); // 10 seconds
- // Ensure that if the element is found and the observer is disconnected, we also clear the timeout
- observer.takeRecords().forEach((record) => {
- clearTimeout(timeoutId);
- });
- });
- }