Untitled

 avatar
unknown
plain_text
2 days ago
10 kB
17
No Index
// ==UserScript==
// @name         Custom Reddit notifications UI
// @namespace    https://reddit.com/
// @version      21.8
// @description  Custom Reddit notifications UI
// @author       RaidPrincess
// @match        *://www.reddit.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  const baseUrl = 'https://www.reddit.com';
  let open = false;

  const container = document.createElement('div');
  container.className = 'rnqv-container';
  container.style.display = 'none';
  document.body.appendChild(container);

  const style = document.createElement('style');
  style.textContent = `
    .rnqv-container {
      position: fixed;
      top: 55px;
      right: 10px;
      width: 360px;
      max-height: 420px;
      background: #1a1a1b;
      color: #d7dadc;
      border: 1px solid #343536;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.4);
      z-index: 9999;
      padding: 12px;
      overflow-y: auto;
      font-size: 14px;
    }
    .rnqv-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 10px;
    }
    .rnqv-actions {
      display: flex;
      gap: 8px;
    }
    .rnqv-btn {
      background-color: transparent;
      border: 1px solid #343536;
      border-radius: 4px;
      padding: 4px 10px;
      color: #d7dadc;
      cursor: pointer;
      font-size: 13px;
    }
    .rnqv-btn:hover {
      background-color: #272729;
    }
    .rnqv-container ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    .rnqv-container li {
      margin-bottom: 10px;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .rnqv-container a {
      color: #58a6ff;
      text-decoration: none;
      flex: 1;
    }
    .rnqv-container a:hover {
      text-decoration: underline;
    }
    .rnqv-avatar {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .rnqv-dismiss {
      cursor: pointer;
      font-size: 16px;
      color: #888;
    }
    .rnqv-dismiss:hover {
      color: red;
    }
    .rnqv-fake-ping {
      position: absolute;
      top: 6px;
      right: 6px;
      width: 8px;
      height: 8px;
      background: red;
      border-radius: 50%;
      z-index: 10;
    }
    [data-testid="unread-indicator"],
    #notifications-inbox-button span[class*="icon"] > span {
      opacity: 0 !important;
      pointer-events: none !important;
    }
    #notifications-inbox-button[data-has-unread] {
      data-has-unread: false !important;
    }
  `;
  document.head.appendChild(style);

  function replaceInboxButton() {
    const btn = document.querySelector('#notifications-inbox-button');
    if (!btn || btn.dataset.rnqv === '1') return;

    const clone = btn.cloneNode(true);
    clone.id = 'notifications-inbox-button';
    clone.style.position = 'relative';
    clone.dataset.rnqv = '1';

    clone.addEventListener('click', (e) => {
      e.preventDefault();
      e.stopImmediatePropagation();
      e.stopPropagation();

      open = !open;
      container.style.display = open ? 'block' : 'none';

      if (open) {
        loadNotifications();
        setSeenCookie();
      }
    }, true);

    btn.replaceWith(clone);
  }

  function updateFakePing(shouldShow) {
    const btn = document.querySelector('#notifications-inbox-button');
    if (!btn) return;
    const existing = btn.querySelector('.rnqv-fake-ping');
    if (shouldShow && !existing) {
      const dot = document.createElement('span');
      dot.className = 'rnqv-fake-ping';
      btn.appendChild(dot);
    } else if (!shouldShow && existing) {
      existing.remove();
    }
  }

  function setSeenCookie() {
    document.cookie = `rnqv_seen=true; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT`;
  }

  function getHiddenMessageIDs() {
    const match = document.cookie.match(/(?:^|; )rnqv_hidden=([^;]*)/);
    return match ? decodeURIComponent(match[1]).split(',') : [];
  }

  function setHiddenMessageIDs(ids) {
    const encoded = encodeURIComponent([...new Set(ids)].join(','));
    document.cookie = `rnqv_hidden=${encoded}; path=/; expires=Fri, 31 Dec 9999 23:59:59 GMT`;
  }

  async function markMessageAsRead(id) {
    try {
      await fetch(`${baseUrl}/api/read_message`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: 'id=' + encodeURIComponent(id)
      });
    } catch {}
  }

  async function loadNotifications() {
    container.innerHTML = '';

    const header = document.createElement('div');
    header.className = 'rnqv-header';

    const title = document.createElement('strong');
    title.textContent = 'Notifications';
    header.appendChild(title);

    const fullBtn = document.createElement('button');
    fullBtn.textContent = 'Full';
    fullBtn.className = 'rnqv-btn';
    fullBtn.onclick = () => window.open('https://www.reddit.com/notifications', '_blank');

    const actions = document.createElement('div');
    actions.className = 'rnqv-actions';
    actions.appendChild(fullBtn);
    header.appendChild(actions);
    container.appendChild(header);

    const list = document.createElement('ul');
    container.appendChild(list);

    const hiddenIDs = getHiddenMessageIDs();

    try {
      const res = await fetch(`${baseUrl}/message/unread.json`, { credentials: 'include' });
      const items = (await res.json())?.data?.children?.slice(0, 10) || [];
      const visibleItems = items.filter(i => !hiddenIDs.includes(i.data.name));
      updateFakePing(visibleItems.length > 0);

      if (!visibleItems.length) {
        const li = document.createElement('li');
        li.textContent = 'No new notifications.';
        list.appendChild(li);
        return;
      }

      for (const item of visibleItems) {
        const d = item.data;
        const li = document.createElement('li');

        const avatar = document.createElement('img');
        avatar.className = 'rnqv-avatar';
        avatar.src = 'https://www.redditstatic.com/avatars/avatar_default_02_24A0ED.png';
        li.appendChild(avatar);

        const a = document.createElement('a');
        a.textContent = d.was_comment
          ? `u/${d.author}: ${(d.body || '').replace(/\s+/g, ' ').trim().slice(0, 80)}`
          : `${d.subject || '(no subject)'} (from u/${d.author})`;
        a.href = d.context ? baseUrl + d.context : baseUrl + '/message/messages/' + d.id;
        a.target = '_blank';
        li.appendChild(a);

        const dismiss = document.createElement('span');
        dismiss.className = 'rnqv-dismiss';
        dismiss.textContent = '🅧';
        dismiss.title = 'Dismiss & mark as read';
        dismiss.onclick = async () => {
          li.remove();
          hiddenIDs.push(d.name);
          setHiddenMessageIDs(hiddenIDs);
          await markMessageAsRead(d.name);
          const stillVisible = container.querySelectorAll('ul li').length - 1;
          updateFakePing(stillVisible > 0);
        };
        li.appendChild(dismiss);

        list.appendChild(li);

        fetch(`${baseUrl}/user/${d.author}/about.json`, { credentials: 'include' })
          .then(r => r.json())
          .then(user => {
            const icon = user.data?.icon_img?.split('?')[0];
            if (icon) avatar.src = icon;
          }).catch(() => {});
      }
    } catch {
      const li = document.createElement('li');
      li.textContent = 'Error loading notifications.';
      list.appendChild(li);
    }
  }

  new MutationObserver(() => replaceInboxButton()).observe(document.body, { childList: true, subtree: true });

  document.addEventListener('click', (e) => {
    if (open && !container.contains(e.target) && !e.target.closest('#notifications-inbox-button')) {
      container.style.display = 'none';
      open = false;
    }
  });

  document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape' && open) {
      container.style.display = 'none';
      open = false;
    }
  });

  container.addEventListener('click', (e) => e.stopPropagation());

  // Initial red dot check
  (async () => {
    const hiddenIDs = getHiddenMessageIDs();
    try {
      const res = await fetch(`${baseUrl}/message/unread.json`, { credentials: 'include' });
      const items = (await res.json())?.data?.children || [];
      const visibleItems = items.filter(i => !hiddenIDs.includes(i.data.name));
      updateFakePing(visibleItems.length > 0);
    } catch {}
  })();

  //  Red dot polling every 30s
  setInterval(async () => {
    const hiddenIDs = getHiddenMessageIDs();
    try {
      const res = await fetch(`${baseUrl}/message/unread.json`, { credentials: 'include' });
      const items = (await res.json())?.data?.children || [];
      const visibleItems = items.filter(i => !hiddenIDs.includes(i.data.name));
      updateFakePing(visibleItems.length > 0);
    } catch (err) {
      console.warn('Polling error:', err);
    }
  }, 30000);

  //  Kill Reddit's dynamic-badge
  function removeDynamicBadge() {
    const badge = document.querySelector('dynamic-badge[data-id="notification-count-element"]');
    if (badge) {
      badge.remove();
      console.log('Dynamic badge removed.');
    }
  }

  removeDynamicBadge();

  new MutationObserver((mutationsList) => {
    for (const mutation of mutationsList) {
      if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
        removeDynamicBadge();
      }
    }
  }).observe(document.body, { childList: true, subtree: true });

  setInterval(removeDynamicBadge, 5000);
})();
// ==UserScript==
// @name        New script discord.com
// @namespace   Violentmonkey Scripts
// @match       https://discord.com/channels/@me/609570647718494209*
// @grant       none
// @version     1.0
// @author      -
// @description 4/25/2025, 9:47:27 PM
// ==/UserScript==// ==UserScript==
// @name        New script reddit.com
// @namespace   Violentmonkey Scripts
// @match       https://www.reddit.com/*
// @grant       none
// @version     1.0
// @author      -
// @description 4/25/2025, 9:53:59 PM
// ==/UserScript==
Editor is loading...
Leave a Comment