Bluesky Unified Block & Hide

Automatically hides Bluesky posts immediately after confirming the block action using the native Block button.

Version au 27/12/2024. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Bluesky Unified Block & Hide
// @namespace    https://greatest.deepsurf.us/en/users/567951-stuart-saddler
// @version      1.0
// @description  Automatically hides Bluesky posts immediately after confirming the block action using the native Block button.
// @icon         https://images.seeklogo.com/logo-png/52/2/bluesky-logo-png_seeklogo-520643.png
// @match        https://bsky.app/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Global variable to store the currently selected username
    let currentUsername = null;

    /**
     * Utility function to log messages with a prefix for easier debugging.
     * @param {string} message - The message to log.
     */
    function log(message) {
        console.log(`[Bluesky Auto Hide Blocked Posts] ${message}`);
    }

    /**
     * Determines if a given element is the "Three Dots" (Post Options) button.
     * @param {Element} element - The DOM element to check.
     * @returns {boolean} - True if it's the "Three Dots" button, else false.
     */
    function isPostOptionsButton(element) {
        if (!element) return false;
        const ariaLabel = element.getAttribute('aria-label') || '';
        return ariaLabel.includes('Open post options menu');
    }

    /**
     * Determines if a given element is the "Block account" button based on its text content.
     * @param {Element} element - The DOM element to check.
     * @returns {boolean} - True if it's the "Block account" button, else false.
     */
    function isBlockButton(element) {
        if (!element) return false;
        const blockButtonText = "Block account";
        return element.textContent.trim() === blockButtonText;
    }

    /**
     * Extracts the username from a post container.
     * @param {Element} postContainer - The post container element.
     * @returns {string|null} - The username or null if not found.
     */
    function getUsernameFromPost(postContainer) {
        if (!postContainer) return null;
        log('Attempting to extract username from post container.');

        // Attempt to find an <a> tag with href containing "/profile/"
        const usernameLink = postContainer.querySelector('a[href*="/profile/"]');
        if (usernameLink) {
            const href = usernameLink.getAttribute('href');
            const parts = href.split('/');
            const username = parts[parts.length - 1] || null;
            if (username) {
                log(`Extracted username: ${username}`);
                return username.toLowerCase(); // Ensure lowercase for consistency
            }
        }

        // Alternative method: Look for a span containing "@" symbol
        const possibleUsernameElements = postContainer.querySelectorAll('span, div');
        for (let el of possibleUsernameElements) {
            const text = el.textContent.trim();
            if (text.startsWith('@')) { // Look for text starting with "@"
                const username = text.substring(1); // Remove "@" symbol
                log(`Extracted username from span: ${username}`);
                return username.toLowerCase(); // Ensure lowercase for consistency
            }
        }

        log('Username could not be extracted from the post.');
        return null;
    }

    /**
     * Hides all posts from the specified username.
     * @param {string} username - The username whose posts should be hidden.
     */
    function hidePostsFromUser(username) {
        if (!username) return;
        log(`Hiding posts from user: @${username}`);

        // Define selectors based on post container identification
        const selector = `div[role="link"][tabindex="0"], div[role="article"], section[role="article"]`;
        const posts = document.querySelectorAll(selector);

        let hiddenCount = 0;
        posts.forEach(post => {
            const postUsername = getUsernameFromPost(post);
            if (postUsername && postUsername === username) {
                post.style.display = 'none';
                log(`Post from @${username} has been hidden.`);
                hiddenCount++;
            }
        });

        log(`Total posts hidden from @${username}: ${hiddenCount}`);
    }

    /**
     * Adds a username to the blocked list and hides their posts.
     * @param {string} username - The username to block.
     */
    function addBlockedUser(username) {
        if (!username) return;
        hidePostsFromUser(username);
    }

    /**
     * Hides all posts from blocked users.
     * (This function is now redundant since we're not maintaining a blocked users list.)
     */
    function hidePostsFromBlockedUsers() {
        // No longer needed as we're not maintaining a blocked users list.
        // Removed to streamline the script.
    }

    /**
     * Initializes the script by hiding posts from blocked users.
     * (This function is now redundant since we're not maintaining a blocked users list.)
     */
    function initializeBlockedUsers() {
        // No longer needed as we're not maintaining a blocked users list.
        // Removed to streamline the script.

        // Optionally, if you still want to hide posts immediately after blocking without relying on storage,
        // you can keep any necessary logic here.
    }

    /**
     * Sets up the listener for the "Three Dots" (Post Options) button to capture the username.
     */
    function setupPostOptionsListener() {
        document.addEventListener('click', function(event) {
            let target = event.target;

            // Traverse up the DOM tree to check if a "Three Dots" button was clicked
            while (target && target !== document.body) {
                if (isPostOptionsButton(target)) {
                    log('"Three Dots" button clicked.');
                    // Find the post container associated with this button
                    const postContainer = target.closest('div[role="link"][tabindex="0"], div[role="article"], section[role="article"]');
                    if (postContainer) {
                        const username = getUsernameFromPost(postContainer);
                        if (username) {
                            currentUsername = username;
                            log(`Current post's username set to: @${username}`);
                        } else {
                            log('Username could not be determined from the post.');
                            currentUsername = null;
                        }
                    } else {
                        log('Post container not found.');
                        currentUsername = null;
                    }
                    break; // Exit once handled
                }
                target = target.parentElement;
            }
        }, true); // Use capture phase
    }

    /**
     * Sets up the listener for the "Block account" button within the menu to handle confirmation.
     */
    function setupBlockButtonListener() {
        document.addEventListener('click', function(event) {
            let target = event.target;

            // Traverse up the DOM tree to check if a "Block account" button was clicked
            while (target && target !== document.body) {
                if (isBlockButton(target)) {
                    log('"Block account" button clicked.');
                    // Do NOT hide posts here; wait for confirmation
                    // The hiding will be handled in the confirmation dialog listener
                    break; // Exit once handled
                }
                target = target.parentElement;
            }
        }, true); // Use capture phase
    }

    /**
     * Sets up a listener for the confirmation button to add the user to the blocked list and hide their posts.
     */
    function setupConfirmationButtonListener() {
        document.addEventListener('click', function(event) {
            const target = event.target;

            // Check if the clicked element or its parent has data-testid="confirmBtn"
            const confirmBtn = target.closest('button[data-testid="confirmBtn"]');
            if (confirmBtn) {
                log('Confirmation button clicked.');
                if (currentUsername) {
                    addBlockedUser(currentUsername);
                    currentUsername = null;
                } else {
                    log('No user recorded for blocking.');
                }
            }
        }, true); // Use capture phase
    }

    /**
     * Utility function to debounce frequent function calls.
     * @param {Function} func - The function to debounce.
     * @param {number} delay - The delay in milliseconds.
     * @returns {Function} - The debounced function.
     */
    function debounce(func, delay) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), delay);
        };
    }

    /**
     * Initializes the script by setting up listeners.
     */
    function init() {
        setupPostOptionsListener();
        setupBlockButtonListener();
        setupConfirmationButtonListener();
        // initializeBlockedUsers(); // No longer needed
        log('Bluesky Auto Hide Blocked Posts script has been initialized.');
    }

    /**
     * Waits for the DOM to be fully loaded before initializing the script.
     */
    function waitForDOM() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                setTimeout(init, 500); // Slight delay to ensure all elements are loaded
            });
        } else {
            setTimeout(init, 500);
        }
    }

    // Start the script
    waitForDOM();

})();