// ==UserScript==
// @name Bookmark Twitter Users (Discrete Follow)
// @namespace Violentmonkey Scripts
// @match https://x.com/*
// @grant none
// @version 1.0
// @author -
// @description 6/2/2024, 12:07:28 AM
// @license MIT
// ==/UserScript==
// Add FontAwesome library
var faLink = document.createElement('link');
faLink.rel = 'stylesheet';
faLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css';
document.head.appendChild(faLink);
// modal vars
const modal = document.createElement('div');
modal.className = 'bookmarkModal';
const modalContent = document.createElement('div');
modalContent.classList.add('bookmarkContent');
const closeModal = document.createElement('button');
closeModal.classList.add('r-37j5jr', 'closeBookmarkModal');
const bookmarkUl = document.createElement('ul');
// Execute addElement function at window load
window.onload = function() {
const bookmarkStyle = document.createElement('style');
// Define your CSS rules
const css = `
.bookmarkHover:hover {
transform: scale(1.05);
filter: brightness(1.5);
}
.bookmarkHover:active {
transform: scale(1);
}
`;
// Set the CSS rules to the style element
bookmarkStyle.innerHTML = css;
// Append the style element to the document body
document.body.appendChild(bookmarkStyle);
// Load bookmarks from local storage
loadBookmarks();
// Use a setInterval to check if the sidebar is available
const checkSidebarInterval = setInterval(() => {
const sidebarNav = document.querySelector('nav[aria-label="Primary"]');
if (sidebarNav) {
clearInterval(checkSidebarInterval); // Stop checking once the element is found
addElement(sidebarNav); // Call the addElement function with the sidebarNav element
}
}, 100); // Check every 100ms
// add bookmark option to user actions
// Use a setInterval to check if the user actions is available
const checkUserActionsInterval = setInterval(() => {
const userActionBtns = document.querySelector('div[class="css-175oi2r r-obd0qt r-18u37iz r-1w6e6rj r-1h0z5md r-dnmrzs"]');
userActionBtns.style.cssText = `align-items: center;`;
if (userActionBtns) {
clearInterval(checkUserActionsInterval); // Stop checking once the element is found
addBookmarkBtn(userActionBtns); // Call the addElement function with the sidebarNav element
}
}, 100); // Check every 100ms
};
// Function to load bookmarks from local storage
function loadBookmarks() {
const bookmarks = JSON.parse(localStorage.getItem('bookmarks')) || [];
bookmarkUl.innerHTML = ''; // Clear the existing bookmarks
bookmarks.forEach((bookmark, index) => {
const newBookmark = document.createElement('li');
newBookmark.innerHTML = `
<p>
<a href="${bookmark.link}">${bookmark.userName}</a>${bookmark.notes ? ` - ${bookmark.notes}` : ''}
<span class="edit-notes bookmarkHover" style="color: yellow; margin-left: 10px;" data-bookmark-index="${index}">[ Edit | </span>
<span class="delete-bookmark bookmarkHover" style="color: red;" data-bookmark-index="${index}"> Delete ]</span>
</p>
`;
bookmarkUl.appendChild(newBookmark);
});
}
// Add event listener for edit notes and delete bookmark
bookmarkUl.addEventListener('click', function(event) {
const target = event.target;
if (target.classList.contains('edit-notes')) {
editNotes(event);
} else if (target.classList.contains('delete-bookmark')) {
deleteBookmark(event);
}
});
// Function to edit notes
function editNotes(event) {
const bookmarkIndex = event.target.dataset.bookmarkIndex;
const bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
const newNotes = prompt("Edit notes:", bookmarks[bookmarkIndex].notes);
if (newNotes !== null) {
bookmarks[bookmarkIndex].notes = newNotes;
localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
loadBookmarks(); // Reload bookmarks to update the UI
}
}
// Function to delete bookmark
function deleteBookmark(event) {
const bookmarkIndex = event.target.dataset.bookmarkIndex;
const bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
bookmarks.splice(bookmarkIndex, 1);
localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
loadBookmarks(); // Reload bookmarks to update the UI
}
// Function to add bookmark button on profiles
function addBookmarkBtn(userActionBtns) {
const bookmarkUserBtn = document.createElement('div');
bookmarkUserBtn.innerHTML = `
<div class="css-175oi2r">
<svg viewBox="0 0 24 24" aria-hidden="true" class="r-4qtqp9 r-yyyyoo r-dnmrzs r-bnwqim r-lrvibr r-m6rgpd r-1nao33i r-lwhw9o r-cnnz9e">
<g><path d="M4 4.5C4 3.12 5.119 2 6.5 2h11C18.881 2 20 3.12 20 4.5v18.44l-8-5.71-8 5.71V4.5zM6.5 4c-.276 0-.5.22-.5.5v14.56l6-4.29 6 4.29V4.5c0-.28-.224-.5-.5-.5h-11z"></path></g>
</svg>
</div>
`;
bookmarkUserBtn.style.cssText = `
background-color: transparent;
border-radius: 100%;
font-family: TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
`;
userActionBtns.appendChild(bookmarkUserBtn);
bookmarkUserBtn.onclick = () => {
addUserToBookmarks();
};
}
function addUserToBookmarks() {
let newBookmarkLink = window.location.href;
let bookmarkUserName = document.querySelector('div[data-testid="UserName"] span[class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"]').textContent;
console.log('NEW BOOKMARK: ' + newBookmarkLink);
populateBookmarks(newBookmarkLink, bookmarkUserName);
}
function populateBookmarks(link, bookmarkUserName) {
let bookmarkModal = document.querySelector('.bookmarkContent');
let notes = prompt("Add additional notes: ");
if (notes !== null && notes !== '') {
notes = notes;
} else {
notes = '';
}
const newBookmark = {
link,
userName: bookmarkUserName,
notes
};
const bookmarks = JSON.parse(localStorage.getItem('bookmarks')) || [];
bookmarks.push(newBookmark);
localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
loadBookmarks(); // Reload bookmarks to update the UI
}
// Function to add the button to the sidebar
function addElement(sidebarNav) {
console.log('NAV ' + sidebarNav);
const bookmarkUserBtn = document.createElement('a');
bookmarkUserBtn.classList.add('css-175oi2r', 'r-6koalj', 'r-eqz5dr', 'r-16y2uox', 'r-1habvwh', 'r-cnw61z', 'r-13qz1uu', 'r-1ny4l3l', 'r-1loqt21', '.r-1hdo0pc', '.r-o7ynqc');
bookmarkUserBtn.innerHTML = `
<div class="css-175oi2r r-sdzlij r-dnmrzs r-1awozwy r-18u37iz r-1777fci r-xyw6el r-o7ynqc r-6416eg">
<div class="css-175oi2r">
<svg viewBox="0 0 24 24" aria-hidden="true" class="r-4qtqp9 r-yyyyoo r-dnmrzs r-bnwqim r-lrvibr r-m6rgpd r-1nao33i r-lwhw9o r-cnnz9e">
<g><path d="M4 4.5C4 3.12 5.119 2 6.5 2h11C18.881 2 20 3.12 20 4.5v18.44l-8-5.71-8 5.71V4.5zM6.5 4c-.276 0-.5.22-.5.5v14.56l6-4.29 6 4.29V4.5c0-.28-.224-.5-.5-.5h-11z"></path></g>
</svg>
</div>
<div dir="ltr" class="css-146c3p1 r-dnmrzs r-1udh08x r-3s2u2q r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-adyw6z r-135wba7 r-16dba41 r-dlybji r-nazi8o" style="text-overflow: unset; color: rgb(231, 233, 234);">
<span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3" style="text-overflow: unset;">Bookmarked Users</span>
</div>
</div>
`;
// Get the 4th to last child and insert the button after it
const children = sidebarNav.children;
const fourthToLastChild = children[children.length - 5];
if (fourthToLastChild) {
fourthToLastChild.insertAdjacentElement('afterend', bookmarkUserBtn);
} else {
// If there are less than 4 children, just append the button to the sidebar
sidebarNav.appendChild(bookmarkUserBtn);
}
bookmarkUserBtn.onclick = () => {
openModal();
};
}
function openModal() {
// create the modal
modal.style.cssText = `
font: inherit;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 100000;
`;
modalContent.style.cssText = `
font: inherit;
position: relative;
width: 40%;
height: 40%;
background-color: #161616;
border: 1px solid rgba(255,255,255,0.5);
border-radius: 3px;
overflow-y: auto;
padding: 20px;
-webkit-box-shadow: 0px 0px 92px 50px rgba(0,0,0,0.55);
-moz-box-shadow: 0px 0px 92px 50px rgba(0,0,0,0.55);
box-shadow: 0px 0px 92px 50px rgba(0,0,0,0.55);
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.2) rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
font-family: TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
place-content: start !important;
`;
closeModal.textContent = 'Close';
closeModal.style.cssText = `
font: inherit;
position: absolute;
top: 10px;
right: 10px;
background-color: #161616;
color: white;
border: rgba(255,255,255,0.5) 1px solid;
border-radius: 3px;
z-index: 1000000;
font-family: TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
`;
bookmarkUl.style.cssText = `
display: flex;
flex-direction: column;
gap: 10px;
`;
closeModal.onclick = () => {
closeModalFunc();
};
modalContent.appendChild(closeModal);
modalContent.appendChild(bookmarkUl);
modal.appendChild(modalContent);
document.body.appendChild(modal);
}
function closeModalFunc() {
modal.style.display = 'none';
modalContent.innerHTML = ''; // Clear the modal content
}
// Close modal when clicking outside the inner modalContent
modal.onclick = (event) => {
if (event.target === modal) {
closeModalFunc();
}
};
// Prevent modal from closing when clicking inside the modalContent
modalContent.onclick = (event) => {
event.stopPropagation();
};