Bitcoin Forum

Local => Português (Portuguese) => Topic started by: TryNinja on August 31, 2022, 04:36:27 PM



Title: [Script] Notas do Usuário
Post by: TryNinja on August 31, 2022, 04:36:27 PM
Pediram esse script lá no board gringo e também vou postar ele aqui.

Ele adiciona uma opção de adicionar uma nota/descrição em cada usuário, que só pode ser vista localmente. É uma forma de lembrar detalhes de uma pessoal (i.e bem conhecida, amigável, já negociei, estou devendo X reais, etc...).

Tópico original: https://bitcointalk.org/index.php?topic=5411599

https://talkimg.com/images/2023/05/14/blobbc21ff12c60cafe1.png
https://talkimg.com/images/2023/05/14/blobab76f2ddd1240a27.png
https://talkimg.com/images/2023/05/14/blob5f9d7265f8a4eda9.png

Installation

- Install Tampermonkey (https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en) (Chrome, Brave...) or Greasymonkey (https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) (Firefox).
- Add a new script and paste the code:

Code:
// ==UserScript==
// @name         BitcoinTalk User Notes
// @version      0.3
// @description  Adds an note field to each user on BitcoinTalk
// @author       TryNinja
// @match        https://bitcointalk.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bitcointalk.org
// @grant GM.setValue
// @grant GM.getValue
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==

const enableModal = 1;

(async function() {
    'use strict';

    const addStyle = (css) => {
        const style = document.getElementById("GM_addStyleBy8626") || (() => {
        const style = document.createElement('style');
        style.id = "GM_addStyleBy8626";
        document.head.appendChild(style);
        return style;
        })();
        const sheet = style.sheet;
        sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
    }

    if (enableModal) {
        addStyle(`.modal {
            position: fixed;
            width: 100vw;
            height: 100vh;
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
        }`);

        addStyle(`.modal-bg {
            position: absolute;
            width: 100%;
            height: 100%;
        }`);

        addStyle(`.modal-container {
            min-width: 30vh;
            border-radius: 10px;
            background: #fff;
            position: relative;
            padding: 10px;
        }`);

        addStyle(`.modal-close {
            position: absolute;
            right: 15px;
            top: 15px;
            outline: none;
            appearance: none;
            color: red;
            background: none;
            border: 0px;
            font-weight: bold;
            cursor: pointer;
        }`);
    };

    const getValue = typeof GM_getValue === 'undefined' ? GM.getValue : GM_getValue;
    const setValue = typeof GM_setValue === 'undefined' ? GM.setValue : GM_setValue;

    const getParentNodeNth = (element, num) => {
        let parent = element;
        for (let i = 0; i < num; i++) {
            if (parent.parentNode) {
                parent = parent.parentNode;
            }
        }
        return parent;
    };

    const getNotes = async () => {
        let notes;
        try {
            notes = JSON.parse(await getValue('notes') ?? '{}');
        } catch (error) {
            notes = {};
        };
        return notes;
    };

    const setNotes = async notes => {
        if (typeof notes === 'string') {
            try {
                JSON.parse(notes);
                await setValue('notes', notes);
            } catch (error) {
                console.error('Notes value is an invalid JSON format')
            };
        } else if (typeof notes === 'object') {
            await setValue('notes', JSON.stringify(notes ?? {}));
        };
    };

    const getUserNote = async user => {
        const notes = await getNotes();
        if (!notes) {
            return null;
        }
        return notes[user];
    };

    const setUserNote = async (user, note) => {
        const notes = await getNotes();
        notes[user] = note;
        await setNotes(notes)
    };

    const texts = {
        addNote: '<a style="cursor: pointer; font-weight: bold" href="javascript:;">📜 Add Note</a>',
        withNote: note => `<a style="cursor: pointer; font-weight: bold" href="javascript:;"><b>📜</b> ${note}</a>`
    };

    const addNote = async (user, element) => {
        const note = prompt('Input the note (empty to remove):');
        await setUserNote(user, note);
        if (note) {
            element.innerHTML = texts.withNote(note);
        } else if (note !== null) {
            element.innerHTML = texts.addNote;
        }
    };

    const exportNotesToInput = async () => {
        const notesInput = document.querySelector('#notesInput');
        const notesImportExportDiv = document.querySelector('#notesImportExportDiv');
        const doneImportButton = document.querySelector('#doneImportButton');
        const notes = await getNotes();
        const notesJsonString = JSON.stringify(Object.keys(notes)
            .filter(user => notes[user]).reduce((obj, user) => ({...obj, [user]: notes[user]}), {}));

        notesInput.value = notesJsonString;
        notesImportExportDiv.querySelector('span').innerText = 'Export (copy the code)';
        notesImportExportDiv.style.display = 'flex';
        doneImportButton.style.display = 'none';
    };

    const importNotesFromInput = async () => {
        const notesInput = document.querySelector('#notesInput');
        const notesImportExportDiv = document.querySelector('#notesImportExportDiv');
        const doneImportButton = document.querySelector('#doneImportButton');

        notesInput.value = '';
        notesImportExportDiv.querySelector('span').innerText = 'Import (paste the code)';
        notesImportExportDiv.style.display = 'flex';
        doneImportButton.style.display = 'inline-block';
    };

    const importNotesFromInputDone = async () => {
        const notesInput = document.querySelector('#notesInput');
        const confirmImport = confirm('Are you sure you want to override your local notes?');

        if (confirmImport && notesInput.value) {
            setNotes(notesInput.value);
            loadUserNotesList();
        }
    };

    const insertNotesModal = async () => {
        let notesModal = document.querySelector('#userNotesModal');

        if (!notesModal) {
            const moreMenuBtn = document.querySelector('body');
            notesModal = document.createElement('div');

            notesModal.innerHTML = `
                <div class="modal" id="modal-one">
                    <div class="modal-bg modal-exit"></div>
                    <div class="modal-container">
                        <div style="margin-bottom: 5px;">
                            <b style="font-size: 2rem;">User Notes</b>
                            <button class="modal-close modal-exit">X</button>
                        </div>

                        <div style="display: flex; align-items: center; margin-bottom: 5px;">
                            <button id="exportUserNotes">Export</button>
                            <button id="importUserNotes">Import</button>
                        </div>

                        <div>
                            <div style="display: none; flex-direction: column;" id="notesImportExportDiv">
                                <span id="notesInputText"></span>
                                <input id="notesInput" />
                                <button id="doneImportButton" style="display: none;">Done</button>
                            </div>

                        </div>

                        <div id="userNotesList" />
                    </div>
                </div>`;
            notesModal.classList.add('modal');
            notesModal.style.visibility = 'hidden';
            notesModal.setAttribute('id', 'userNotesModal');

            moreMenuBtn.after(notesModal);

            const exportButton = document.querySelector('#exportUserNotes');
            const importButton = document.querySelector('#importUserNotes');
            const doneImportButton = document.querySelector('#doneImportButton');

            exportButton.addEventListener('click', () => exportNotesToInput());
            importButton.addEventListener('click', () => importNotesFromInput());
            doneImportButton.addEventListener('click', () => importNotesFromInputDone());
        };

        return notesModal;
    };

    const loadUserNotesList = async () => {
        const userNotesList = document.querySelector('#userNotesList');

        const notes = await getNotes();

        if (Object.keys(notes).length) {
            userNotesList.innerHTML = Object.keys(notes)
            .filter(user => notes[user])
            .map((user) => `<a href="https://bitcointalk.org/index.php?action=profile;u=${user}" target="_blank">${user}</a>: ${notes[user]}`).join('<br/>');
        } else {
            userNotesList.innerHTML = 'No notes...';
        };
    };

    const insertUserNotesMenuButton = async () => {
        let notesBtn = document.querySelector('#userNotesMenuBtn');
        const modal = await insertNotesModal();
        const modalExit = modal.querySelectorAll('.modal-exit');

        if (!notesBtn) {
            const moreMenuBtn = document.querySelector(`a[href='/more.php']`).parentNode;
            notesBtn = document.createElement('td');

            notesBtn.innerHTML = '<td><a href="javascript:;" id="openUserNotes">User Notes</a></td>';
            notesBtn.classList.add('maintab_back');
            notesBtn.setAttribute('id', 'userNotesMenuBtn');
            moreMenuBtn.after(notesBtn);

            const openUserNotes = document.querySelector('#openUserNotes')
            const notesImportExportDiv = document.querySelector('#notesImportExportDiv');
            const notesInput = document.querySelector('#notesInput');

            openUserNotes.addEventListener('click', () => {
                modal.style.visibility = 'visible';
                modal.style.opacity = 1;
                notesImportExportDiv.style.display = 'none';
                notesInput.value = '';
                loadUserNotesList();
            });
            modalExit.forEach(el => el.addEventListener('click', () => {
                modal.style.visibility = 'hidden';
                modal.style.opacity = 0;
            }));
        }

        return notesBtn;
    };

    if (enableModal) {
        insertNotesModal();
        insertUserNotesMenuButton();
    };

    if (window.location.href.match(/topic=\d+/)) {
        const targets = [...document.querySelectorAll('td.poster_info div a:last-child')]
        .filter(e => window.getComputedStyle(getParentNodeNth(e, 11)).display !== 'none');

        targets.map(async target => {
            const [_, userId] = [...target.parentNode.parentNode.childNodes].find(childNode => childNode.innerHTML).innerHTML.match(/u=(\d+)/);
            const noteDiv = document.createElement('div');
            const note = await getUserNote(userId);
            if (!note) {
                noteDiv.innerHTML = texts.addNote;
            } else {
                noteDiv.innerHTML = texts.withNote(note);
            }
            target.before(noteDiv);
            noteDiv.addEventListener('click', () => addNote(userId, noteDiv), false);
        });
    } else if (window.location.href.match(/profile;u=\d+/)) {
        const [_, userId] = window.location.href.match(/u=(\d+)/);
        const target = getParentNodeNth(document.querySelector('#bodyarea table tr td tbody tr:nth-child(2) tr:last-child').parentNode, 1);
        const noteDiv = document.createElement('div');
        const note = await getUserNote(userId);
        if (!note) {
            noteDiv.innerHTML = texts.addNote;
        } else {
            noteDiv.innerHTML = texts.withNote(note);
        }
        target.before(noteDiv);
        noteDiv.addEventListener('click', () => addNote(userId, noteDiv), false);
    }
})();


Title: Re: [Script] Notas do Usuário
Post by: joker_josue on August 31, 2022, 07:23:38 PM
Mas um excelente trabalho e contribuição para a comunidade!
Obrigado! ;)

Não é que eu pessoalmente vá usar, pelos a primeira vista. Mas, não deixa de ser uma ferramenta com alguma utilidade.

Tens de criar uma lista dos teus scripts.  ::)


Por falar disso, sei que existe mais vários scripts para o fórum. Onde é que eu os posso encontrar?


Title: Re: [Script] Notas do Usuário
Post by: TryNinja on August 31, 2022, 07:52:44 PM
Por falar disso, sei que existe mais vários scripts para o fórum. Onde é que eu os posso encontrar?
List of Bitcointalk.org Userscripts/ Add-ons (https://bitcointalk.org/index.php?topic=5148488.0)

Eu uso os meus, o Enhanced merit UI (https://bitcointalk.org/index.php?topic=2833350.msg52249695#msg52249695), e para fazer o quote de posts em tópicos trancados (https://bitcointalk.org/index.php?topic=5226578.0). Também uso a extensão do BPIP (https://bitcointalk.org/index.php?topic=5224821.0) que tem várias adições do ninjastic.space. 8)


Title: Re: [Script] Notas do Usuário
Post by: joker_josue on August 31, 2022, 11:07:24 PM
Eu sou muito purista... não uso extensões nenhumas.  8)
Uso o teu bot, mas não é nenhuma extensão.  ::)

Obrigado pelo link, pode ser que analise com mais detalhe esses extras e comece a usar algum.

Eu uso os meus [...]

Era um pouco mau, criares extensões e depois não as usares.   :P


Nota: não dou mais merits pelo excelente trabalho, porque não tenho.  :-\
Tenho ver se me inspiro e candidato-me a fonte de merit.


Title: Re: [Script] Notas do Usuário
Post by: alegotardo on September 01, 2022, 01:02:04 AM
Por falar disso, sei que existe mais vários scripts para o fórum. Onde é que eu os posso encontrar?
List of Bitcointalk.org Userscripts/ Add-ons (https://bitcointalk.org/index.php?topic=5148488.0)

Eu uso os meus, o Enhanced merit UI (https://bitcointalk.org/index.php?topic=2833350.msg52249695#msg52249695), e para fazer o quote de posts em tópicos trancados (https://bitcointalk.org/index.php?topic=5226578.0). Também uso a extensão do BPIP (https://bitcointalk.org/index.php?topic=5224821.0) que tem várias adições do ninjastic.space. 8)

Legal!!!
O Enhanced merit é bastante útil, eu não entendo porque ainda não simplificaram o processo de envio de merits para que possa ser feito na mesma página (nativamente sem extensões), seria muito mais fácil... creio que a encrenca seja mais por compatibilidade com dispositivos móveis, que já é um problema para várias outras tarefas.

Sobre as notas, uma dúvida... onde fica salvo as anotações, tem como fazer backup delas caso eu queira formatar o pc, reinstalar o navegador ou levar para outro local?


Title: Re: [Script] Notas do Usuário
Post by: TryNinja on September 01, 2022, 01:08:26 AM
Sobre as notas, uma dúvida... onde fica salvo as anotações, tem como fazer backup delas caso eu queira formatar o pc, reinstalar o navegador ou levar para outro local?
No update que postei hoje já dá para exportar e importar localmente (copiando e salvando um texto com suas notas). Vou melhorando o script com o tempo. 8)