Bind Delete Key to Delete in Rabbit Hole with multiple selection support

Binds the Delete key to trigger delete in rabbit hole journal backend. Multiple selection is made with ctrl key.

Versión del día 08/10/2024. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Bind Delete Key to Delete in Rabbit Hole with multiple selection support
// @namespace    http://tampermonkey.net/
// @license      GNU GPLv3
// @version      2.0
// @description  Binds the Delete key to trigger delete in rabbit hole journal backend. Multiple selection is made with ctrl key.
// @author       Rekt
// @match        *://hole.rabbit.tech/*
// @grant        none
// ==/UserScript==
(function() {
    'use strict';

    let selectedItems = new Set();
    let accessToken = null;

    // Hook into fetch to capture access token
    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const response = await originalFetch(...args);
        if (args[1] && args[1].body) {
            try {
                const body = JSON.parse(args[1].body);
                if (body.accessToken) {
                    accessToken = body.accessToken;
                }
            } catch (error) {
                console.error("Error parsing request body:", error);
            }
        }
        return response;
    };

    // Function to delete entries using HTTP deletion and remove placeholders
    async function deleteEntries(entryIds) {
        if (!accessToken) {
            console.error('Access token not available');
            return;
        }

        for (let entryId of entryIds) {
            try {
                const payload = { accessToken, entryId, deleteEntry: true };

                const response = await fetch('/apis/updateJournalEntry', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${accessToken}`
                    },
                    body: JSON.stringify(payload)
                });

                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }

                // Remove placeholder from the page
                const element = document.querySelector(`[data-entry-id="${entryId}"]`);
                if (element) {
                    element.remove();
                }
            } catch (error) {
                console.error(`Error deleting entry ${entryId}:`, error);
            }
        }
    }

    // Function to handle the delete key
    function handleDeleteKey(event) {
        // Prevent deletion if the user is focused on a contenteditable element
        const focusedElement = document.activeElement;
        if (focusedElement.hasAttribute('contenteditable')) {
            return;
        }

        // If delete key is pressed and there are selected items, delete them
        if (event.key === 'Delete') {
            const entryIds = Array.from(selectedItems);
            if (entryIds.length > 0) {
                deleteEntries(entryIds);
                selectedItems.clear();
            } else {
                // Delete single selected item without Ctrl
                const target = document.querySelector('li[data-entry-id].selected');
                if (target) {
                    const entryId = target.getAttribute('data-entry-id');
                    deleteEntries([entryId]);
                    target.classList.remove('selected');
                }
            }
            event.preventDefault(); // Prevent default behavior if necessary
        }
    }

    // Function to handle selection with Ctrl key
    function handleSelection(event) {
        // Check if the clicked element is a list item with a data-entry-id
        const target = event.target.closest('li[data-entry-id]');
        if (!target) return;

        const entryId = target.getAttribute('data-entry-id');

        // If Ctrl is pressed, allow multiple selection and toggle current selection state
        if (event.ctrlKey) {
            if (selectedItems.has(entryId)) {
                selectedItems.delete(entryId);
                target.classList.remove('selected');
            } else {
                selectedItems.add(entryId);
                target.classList.add('selected');
            }
        } else { 
            // Single selection without Ctrl, clear previous selections and select current item
            selectedItems.forEach(id => {
                const element = document.querySelector(`[data-entry-id="${id}"]`);
                if (element) element.classList.remove('selected');
            });
            selectedItems.clear();
            selectedItems.add(entryId);

            // Do not apply styling for single selection
        }

        // Apply styling only for multiple selections
        selectedItems.forEach(id => {
            const element = document.querySelector(`[data-entry-id="${id}"]`);
            if (element && selectedItems.size > 1) element.classList.add('selected');
        });
    }

    // Add event listeners
    document.addEventListener('keydown', handleDeleteKey);
    document.addEventListener('click', handleSelection);

    // Add CSS for the selected class to highlight the selected items
    const style = document.createElement('style');
    style.innerHTML = `
        .selected {
            background-color: yellow;
        }
    `;
    document.head.appendChild(style);

})();