Bluesky Unified Block & Hide

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

Stan na 27-12-2024. Zobacz najnowsza wersja.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==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();

})();