您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhances the GitHub Notifications page, making it more productive and less noisy.
当前为
- // ==UserScript==
- // @name Refined GitHub Notifications
- // @namespace https://greasyfork.org/en/scripts/461320-refined-github-notifications
- // @version 0.1.4
- // @description Enhances the GitHub Notifications page, making it more productive and less noisy.
- // @author Anthony Fu (https://github.com/antfu)
- // @license MIT
- // @homepageURL https://github.com/antfu/refined-github-notifications
- // @supportURL https://github.com/antfu/refined-github-notifications
- // @match https://github.com/**
- // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
- // @grant none
- // ==/UserScript==
- /* eslint-disable no-console */
- (function () {
- 'use strict'
- // Fix the archive link
- if (location.pathname === '/notifications/beta/archive')
- location.pathname = '/notifications'
- const TIMEOUT = 60_000
- const NAME = 'Refined GitHub Notifications'
- let lastUpdate = Date.now()
- let bc
- let bcInitTime = 0
- function injectStyle() {
- const style = document.createElement('style')
- style.innerHTML = `
- /* Hide blue dot on notification icon */
- .mail-status.unread {
- display: none !important;
- }
- `
- document.head.appendChild(style)
- }
- function initBroadcastChannel() {
- bcInitTime = Date.now()
- bc = new BroadcastChannel('refined-github-notifications')
- bc.onmessage = ({ data }) => {
- console.log(`[${NAME}]`, 'Received message', data)
- if (data.type === 'check-dedupe') {
- // If the new tab is opened after the current tab, close the current tab
- if (data.time > bcInitTime) {
- // TODO: close the tab
- try {
- window.close()
- }
- catch (e) {}
- location.href = 'about:blank'
- }
- }
- }
- }
- function dedupeTab() {
- if (!bc)
- return
- bc.postMessage({ type: 'check-dedupe', time: bcInitTime, url: location.href })
- }
- function externalize() {
- document.querySelectorAll('a')
- .forEach((r) => {
- if (r.href.startsWith('https://github.com/notifications'))
- return
- r.target = '_blank'
- r.rel = 'noopener noreferrer'
- const url = new URL(r.href)
- // Remove notification_referrer_id
- if (url.searchParams.get('notification_referrer_id')) {
- url.searchParams.delete('notification_referrer_id')
- r.href = url.toString()
- }
- })
- }
- function initIdleListener() {
- // Auto refresh page on idle
- document.addEventListener('focus', () => {
- if (Date.now() - lastUpdate > TIMEOUT)
- setTimeout(() => refresh(), 100)
- lastUpdate = Date.now()
- })
- }
- function getIssues() {
- return [...document.querySelectorAll('.notifications-list-item')]
- .map((el) => {
- const url = el.querySelector('a.notification-list-item-link').href
- const status = el.querySelector('.color-fg-open')
- ? 'open'
- : el.querySelector('.color-fg-done')
- ? 'done'
- : el.querySelector('.color-fg-closed')
- ? 'closed'
- : el.querySelector('.color-fg-muted')
- ? 'muted'
- : 'unknown'
- const notificationTypeEl = el.querySelector('.AvatarStack').nextElementSibling
- const notificationType = notificationTypeEl.textContent.trim()
- // Colorize notification type
- if (notificationType === 'mention')
- notificationTypeEl.classList.add('color-fg-open')
- else if (notificationType === 'subscribed')
- notificationTypeEl.classList.add('color-fg-muted')
- else if (notificationType === 'review requested')
- notificationTypeEl.classList.add('color-fg-done')
- const item = {
- title: el.querySelector('.markdown-title').textContent.trim(),
- el,
- url,
- read: el.classList.contains('notification-read'),
- starred: el.classList.contains('notification-starred'),
- type: notificationType,
- status,
- isClosed: ['closed', 'done', 'muted'].includes(status),
- markDone: () => {
- console.log(`[${NAME}]`, 'Mark notifications done', item)
- el.querySelector('button[type=submit] .octicon-check').parentElement.parentElement.click()
- },
- }
- return item
- })
- }
- function getReasonMarkedDone(item) {
- if (item.isClosed && (item.read || item.type === 'subscribed'))
- return 'Closed / merged'
- if (item.title.startsWith('chore(deps): update ') && (item.read || item.type === 'subscribed'))
- return 'Renovate bot'
- if (item.url.match('/pull/[0-9]+/files/'))
- return 'New commit pushed to PR'
- if (item.type === 'ci activity' && /workflow run cancell?ed/.test(item.title))
- return 'GH PR Audit Action workflow run cancelled, probably due to another run taking precedence'
- }
- function isInboxView() {
- const query = new URLSearchParams(window.location.search).get('query')
- if (!query)
- return true
- const conditions = query.split(' ')
- return ['is:done', 'is:saved'].every(condition => !conditions.includes(condition))
- }
- function autoMarkDone() {
- // Only mark on "Inbox" view
- if (!isInboxView())
- return
- const items = getIssues()
- console.log(items)
- let count = 0
- const done = []
- items.forEach((i) => {
- // skip bookmarked notifications
- if (i.starred)
- return
- const reason = getReasonMarkedDone(i)
- if (!reason)
- return
- count++
- i.markDone()
- done.push({
- title: i.title,
- reason,
- link: i.link,
- })
- })
- if (done.length) {
- console.log(`[${NAME}]`, `${count} notifications marked done`)
- console.table(done)
- }
- // Refresh page after marking done (expand the pagination)
- if (count >= 5)
- setTimeout(() => refresh(), 200)
- }
- function removeBotAvatars() {
- document.querySelectorAll('.AvatarStack-body > a')
- .forEach((r) => {
- if (r.href.startsWith('/apps/') || r.href.startsWith('https://github.com/apps/'))
- r.remove()
- })
- }
- /**
- * The "x new notifications" badge
- */
- function hasNewNotifications() {
- return !!document.querySelector('.js-updatable-content a[href="/notifications?query="]')
- }
- // Click the notification tab to do soft refresh
- function refresh() {
- if (!isInNotificationPage())
- return
- document.querySelector('.filter-list a[href="/notifications"]').click()
- lastUpdate = Date.now()
- }
- function isInNotificationPage() {
- return location.href.startsWith('https://github.com/notifications')
- }
- ////////////////////////////////////////
- let initialized = false
- function run() {
- if (isInNotificationPage()) {
- // Run only once
- if (!initialized) {
- initIdleListener()
- initBroadcastChannel()
- initialized = true
- setInterval(() => {
- if (hasNewNotifications())
- refresh()
- }, 2000)
- }
- // Run every render
- dedupeTab()
- externalize()
- removeBotAvatars()
- autoMarkDone()
- }
- }
- injectStyle()
- run()
- // listen to github page loaded event
- document.addEventListener('pjax:end', () => run())
- document.addEventListener('turbo:render', () => run())
- })()