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...).
Installation- Install
Tampermonkey (Chrome, Brave...) or
Greasymonkey (Firefox).
- Add a new script and paste the 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);
}
})();