Bookmark Twitter Users (Discrete Follow)

6/2/2024, 12:07:28 AM

当前为 2024-06-02 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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