Greasy Fork is available in English.

V2Block

在 V 站屏蔽某个帖子

// ==UserScript==
// @name         V2Block
// @namespace    http://tampermonkey.net/
// @version      0.1.2
// @description  在 V 站屏蔽某个帖子
// @author       hudidit
// @match        https://v2ex.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
  'use strict';

  console.log('[v2block]');

  // ********** 初始化逻辑 **********

  const STORAGE_LINKS = 'v2_menu_block_links';
  let blockedLinks = getLink().links;
  hideListItem();

  // 菜单
  const menu = document.createElement('div');
  menu.id = 'v2-menu-container';
  document.body.appendChild(menu);
  menu.innerHTML = `
    <a class="v2-menu-item" id="v2-menu-block">不看这个页面</a>
    <a class="v2-menu-item" id="v2-menu-unblock">恢复全部页面</a>
    <a class="v2-menu-item" id="v2-menu-new-tab" target="_blank">在新标签页打开</a>
  `;
  const menuState = {
    href: '',
    left: -999,
    top: -999,
    linkPath: '',
    linkText: '',
    visible: false,
  };

  // 蒙层
  const overlay = document.createElement('div');
  overlay.id = 'v2-menu-overlay';
  document.body.appendChild(overlay);

  appendStyle();
  resetState();
  console.log('[v2block] blocked links:', blockedLinks);

  // ********** 监听事件 **********

  document.addEventListener('contextmenu', function(e) {
    const el = e.target;
    if (el.tagName.toUpperCase() === 'A' && el.className === 'topic-link') {
      e.preventDefault();
      const href = el.getAttribute('href');
      const linkPath = getPathFormHref(href);
      updateState({
        visible: true,
        href,
        left: e.clientX,
        top: e.clientY,
        linkPath,
        linkText: el.textContent,
      });
    }
  });

  overlay.addEventListener('click', function() {
    resetState();
  });

  document.querySelector('#v2-menu-block').addEventListener('click', function() {
    const {
      linkPath,
      linkText,
    } = menuState;
    saveLink({
      path: linkPath,
      text: linkText,
    });
    hideListItem();
    resetState();
  });

  document.querySelector('#v2-menu-unblock').addEventListener('click', function() {
    clearLink();
    // TODO: 刷新页面,比较粗暴,需要优化
    location.reload();
  });

  // ********** 定义函数 **********

  function resetState() {
    updateState({
      visible: false,
      href: '',
      linkText: '',
    });
  }

  function appendStyle() {
    const styleEl = document.createElement('style');
    styleEl.innerText = `
      #v2-menu-overlay {
        position: fixed;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        z-index: 1;
        display: none;
      }
      #v2-menu-container {
        position: fixed;
        z-index: 2;
        min-width: 200px;
        background: #fff;
        border: 1px solid #c0c0c0;
        box-shadow: 0 0 5px rgba(136, 136, 136, 0.3);
      }
      #v2-menu-container .v2-menu-item {
        display: block;
        font-size: 14px;
        line-height: 30px;
        padding: 0 10px;
        border-bottom: 1px solid #e0e0e0;
        color: #333;
        text-decoration: none;
        cursor: pointer;
      }
      #v2-menu-container .v2-menu-item:hover {
        background: #e0e0e0;
      }
      #v2-menu-container .v2-menu-item:last-of-type {
        border-bottom: none;
      }
    `;
    document.querySelector('head').appendChild(styleEl);
  }

  /**
   * 更新状态
   * @param {object} state 需要更新的状态字段
   */
  function updateState(state = {}) {
    Object.assign(menuState, state);

    const {
      visible,
      href = '',
      left,
      top,
    } = menuState;

    if (!visible) {
      hideMenu();
      return;
    }

    showMenu();

    document.querySelector('#v2-menu-new-tab').href = href;
    menu.style.left = `${left}px`;
    menu.style.top = `${top}px`;
  }

  function showMenu(state = {}) {
    overlay.style.display = 'block';
    menu.style.display = 'block';
  }

  function hideMenu() {
    overlay.style.display = 'none';
    menu.style.display = 'none';
  }

  function hideListItem() {
    const items = document.querySelectorAll('#Main .cell.item');
    for (let item of items) {
      const topic = item.querySelector('.topic-link');
      if (blockedLinks.find(link => link.path === getPathFormHref(topic.getAttribute('href')))) {
        item.remove();
      }
    }
  }

  function saveLink(link = { path: '', text: '' }) {
    const data = JSON.parse(GM_getValue(STORAGE_LINKS, '{}'));
    data.links = data.links || [];
    const links = data.links;
    const isAlreadyIn = links.findIndex(item => item.path === link.path) > -1;
    if (isAlreadyIn) return;
    links.push(link);
    blockedLinks = data.links;
    GM_setValue(STORAGE_LINKS, JSON.stringify(data));
  }

  function getLink() {
    const data = JSON.parse(GM_getValue(STORAGE_LINKS, '{}'));
    data.links = data.links || [];
    return data;
  }

  function clearLink() {
    const data = {};
    data.links = [];
    blockedLinks = data.links;
    GM_setValue(STORAGE_LINKS, JSON.stringify(data));
  }

  function getPathFormHref(href = '') {
    return href.split('#')[0];
  }

})();