Lemmy mlmym community bar customisation

Allows customisation of the lemmy community bar

// ==UserScript==
// @name         Lemmy mlmym community bar customisation
// @namespace    https://greasyfork.org/
// @version      0.3
// @description  Allows customisation of the lemmy community bar
// @author       9point6
// @match        https://old.lemmy.world/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=lemmy.world
// @grant        none
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    const INITIAL_COMMUNITIES = [
        'football',
        '[email protected]',
        '[email protected]',
    ];

    const LOCAL_STORAGE_KEY = 'lemmyCommunityBarSettings';

    const updateSettings = (newCommunitiesList) => {
        const settings = {
            settingsVersion: 1,
            communities: newCommunitiesList.filter((item) => typeof item === 'string')
        };

        window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(settings));

        return settings;
    }

    const loadSettings = () => {
        try {
            const existingSettings = window.localStorage.getItem(LOCAL_STORAGE_KEY);
            if (existingSettings) {
                return JSON.parse(existingSettings);
            }
        } catch (ex) {}

        return updateSettings(INITIAL_COMMUNITIES);
    }

    const hideEditMenu = () => {
        const container = document.querySelector('#community-bar-editor');
        if (container) {
            container.remove();
        }
    }

    const createConfigButton = (icon, title, callback) => {
        const configButton = document.createElement('button');
        const configText = document.createTextNode(icon);

        configButton.title = title;
        configButton.style.padding = '0';
        configButton.style.fontSize = '10px';
        configButton.style.marginRight = '4px';
        configButton.style.cursor = 'pointer';
        configButton.style.background = 'none';
        configButton.style.border = 'none';
        configButton.style.transition = 'filter 0.1s ease-in-out, background-color 0.1s ease-in-out';

        configButton.append(configText);

        configButton.addEventListener('mouseover', (e) => {
            if (!configButton.disabled) {
                configButton.style.filter = 'brightness(1.2)';
                configButton.style.background = 'white';
            }
        });

        configButton.addEventListener('mouseout', (e) => {
            if (!configButton.disabled) {
                configButton.style.filter = 'none';
                configButton.style.background = 'none';
            }
        });

        configButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            callback();
        });

        return configButton;
    }

    const showEditMenu = () => {
        hideEditMenu();

        const settings = loadSettings();

        const communties = document.querySelector('.communities');
        const background = document.createElement('div');
        const container = document.createElement('ol');

        background.id = 'community-bar-editor';
        background.style.backgroundColor = 'rgba(0,0,0,0.5)';
        background.style.position = 'absolute';
        background.style.top = 0;
        background.style.left = 0;
        background.style.width = '100%';
        background.style.height = '100vh';
        background.style.zIndex = '102';

        container.style.backgroundColor = '#ccc';
        container.style.position = 'absolute';
        container.style.top = '8px';
        container.style.right = 0;
        container.style.padding = '4px';
        container.style.border = '1px #ccc solid';

        background.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            hideEditMenu();
        });

        background.append(container);
        communties.append(background);

        settings.communities.map((community, communityIndex) => {
            const item = document.createElement('li');
            const text = document.createTextNode(community);

            const deleteButton = createConfigButton('❌', 'Remove community from your favourites', () => {
                updateSettings(settings.communities.filter((communityName) =>
                    communityName !== community
                ));
                showEditMenu();
            });

            const upButton = createConfigButton('⬆️', 'Move this community up in the list', () => {
                const { communities } = settings;
                [
                    communities[communityIndex - 1],
                    communities[communityIndex]
                ] = [
                    communities[communityIndex],
                    communities[communityIndex - 1]
                ];

                updateSettings(communities);
                showEditMenu();
            });

            if (communityIndex === 0) {
                upButton.disabled = true;
                upButton.style.cursor = 'default';
            }

            const downButton = createConfigButton('⬇️', 'Move this community down in the list', () => {
                const { communities } = settings;
                [
                    communities[communityIndex + 1],
                    communities[communityIndex]
                ] = [
                    communities[communityIndex],
                    communities[communityIndex + 1]
                ];

                updateSettings(communities);
                showEditMenu();
            });

            if (communityIndex === settings.communities.length - 1) {
                downButton.disabled = true;
                downButton.style.cursor = 'default';
            }

            item.style.display = 'block';
            item.style.fontSize = '10px';
            item.style.fontWeight = '400';
            item.style.color = '#000';

            item.append(deleteButton);
            item.append(upButton);
            item.append(downButton);
            item.append(text);
            container.append(item);
        })

        const buttonRow = document.createElement('li');
        const addButton = document.createElement('button')
        const addText = document.createTextNode('add');
        const closeButton = document.createElement('button')
        const closeText = document.createTextNode('finish editing');
        const separatorText = document.createTextNode(' ');

        addButton.append(addText);
        closeButton.append(closeText);
        buttonRow.append(addButton);
        buttonRow.append(separatorText);
        buttonRow.append(closeButton);
        container.append(buttonRow);

        addButton.style.cursor = 'pointer';
        addButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            const newCommunity = prompt('New community to add, make sure you include the instance suffix if it isn\'t a local community');
            updateSettings(settings.communities.concat(newCommunity));
            showEditMenu();
        })

        closeButton.style.cursor = 'pointer';
        closeButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            hideEditMenu();
            window.location.reload();
        })
    };

    const addFavouriteButton = () => {
        const { pathname } = window.location;
        if (!(pathname.startsWith('/c/') || pathname.startsWith('/post/'))) {
            return;
        }

        const { communities } = loadSettings();

        const thisCommunityTitle = document.querySelector('nav .title').textContent.trim();
        const alreadySubscribed = communities.includes(thisCommunityTitle);

        const sidebar = document.querySelector('.side');
        const blockButton = document.querySelector('.side .block');
        const favouriteButton = document.createElement('button');
        const favouriteText = document.createTextNode( alreadySubscribed ? 'unfavourite' : 'favourite');


        favouriteButton.style.fontSize = '10px';
        favouriteButton.style.fontWeight = 'bold';
        favouriteButton.style.display = 'inline-block';
        favouriteButton.style.padding = '1px 4px';
        favouriteButton.style.borderRadius = '4px';
        favouriteButton.style.border = '1px solid #444444';
        favouriteButton.style.color = 'white';
        favouriteButton.style.position = 'relative';
        favouriteButton.style.bottom = '1px';
        favouriteButton.style.cursor = 'pointer';
        favouriteButton.style.backgroundColor = alreadySubscribed ? '#cf6165' : 'green';
        favouriteButton.style.marginRight = '4px';

        favouriteButton.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();

            updateSettings(
                alreadySubscribed
                ? communities.filter((title) => title !== thisCommunityTitle)
                : communities.concat(thisCommunityTitle)
            );

            window.location.reload();
        });

        favouriteButton.append(favouriteText);
        sidebar.insertBefore(favouriteButton, blockButton);
    }

    const addEditButton = () => {
        const more = document.querySelector('.communities .more');
        const communties = document.querySelector('.communities');
        const button = document.createElement('button');
        const buttonSpan = document.createElement('span');
        const buttonText = document.createTextNode('⚙️');

        buttonSpan.append(buttonText);
        button.append(buttonSpan);
        communties.append(button);

        more.style.right = '20px';

        button.title = 'Edit favourite communities';
        button.style.backgroundColor = '#ccc';
        button.style.position = 'absolute';
        button.style.top = 0;
        button.style.right = 0;
        button.style.border = 0;
        button.style.fontSize = '10px';
        button.style.cursor = 'pointer';
        buttonSpan.style.filter = 'brightness(0.5)';

        button.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            const container = document.querySelector('#community-bar-editor');
            if (container) {
                hideEditMenu();
            } else {
                showEditMenu();
            }
        });
    }

    const addCommunityToBar = (name) => {
        const shortName = name.split('@')[0];

        const a = document.createElement('a');
        const link = document.createTextNode(shortName);
        const separator = document.createTextNode(" - ");

        a.appendChild(link);
        a.href = `/c/${name}`;
        a.title = `Go to !${name}`;


        const communities = document.querySelector('.communities')
        const firstLink = document.querySelector('.communities a:nth-child(5)')

        communities.insertBefore(separator, firstLink);
        communities.insertBefore(a, separator);
    }

    loadSettings().communities.reverse().map(addCommunityToBar)
    addEditButton();
    addFavouriteButton();

})();