// ==UserScript==
// @name GitHub Sophisticated Legacy Feed
// @namespace https://github.com/SeaHOH
// @version 0.1.5
// @description Brings back the legacy feed (but more sophisticated) of GitHub dashboard.
// @author SeaHOH
// @license MIT
// @match https://github.com/
// @match https://github.com/dashboard
// @grant none
// @run-at document-start
// @icon https://raw.githubusercontent.com/SeaHOH/gmscripts/57e76be23ec99ab0354daf3c5ec5da9540b5c5c7/icon/favicon.svg
// @homepageURL https://github.com/SeaHOH/gmscripts
// @downloadURL https://raw.githubusercontent.com/SeaHOH/gmscripts/main/github.com/github-sophisticated-legacy-feed.user.js
// @updateURL https://raw.githubusercontent.com/SeaHOH/gmscripts/main/github.com/github-sophisticated-legacy-feed.user.js
// @description:zh-CN 使 GitHub 的旧式消息订阅 (但更先进) 重返个人面板页面。
// ==/UserScript==
(function () {
'use strict';
function setDashboardStyle() {
const content = document.querySelector('.feed-content');
const main = document.querySelector('.feed-main');
const sidebar = document.querySelector('.feed-right-sidebar');
if (content) content.style.maxWidth = 'unset';
if (main) main.style.maxWidth = '100%';
if (sidebar) {
sidebar.style.maxWidth = 'unset';
sidebar.style.width = '800px';
}
}
async function fetchFeed(signal) {
const response = await fetch('https://github.com/dashboard-feed', {
signal: signal,
headers: {'X-Requested-With': 'XMLHttpRequest'}
});
if (!response.ok) {
throw(`HTTP ${response.status} ${response.statusText}`);
}
const text = await response.text();
const feed = new DOMParser().parseFromString(text, 'text/html')
.querySelector('div[data-hpc]');
if (!feed) {
throw('no "
" element was found');
}
return feed;
}
function replaceFeed({fragment, feed, container}) {
if (fragment) {
// stop feed fragment
const newFragment = document.createElement('div');
newFragment.append(...fragment.children);
fragment.replaceWith(newFragment);
container = newFragment.parentElement.parentElement;
}
else if (feed) {
if (!container)
container = document.querySelector('#dashboard feed-container');
const content = container && container.querySelector(
'div[data-target="feed-container.content"]');
const msg = 'No
element was found'
if (!content) {
if (container.id == 'my-dashboard-feed') throw(msg);
console.log(msg);
return;
}
content.replaceChildren(feed);
if (container.id == 'my-dashboard-feed') return;
} else {
return;
}
// stop functions of
const newContainer = document.createElement('div');
newContainer.id = 'my-dashboard-feed';
newContainer.append(...container.children);
if (/Android|Mobile|Phone|Tablet/i.test(navigator.userAgent)) {
container.replaceWith(newContainer);
} else {
// remove useless fragments for desktop
container.parentElement.replaceChildren(newContainer);
}
newContainer.querySelector('#feed-filter-menu').remove();
return newContainer;
}
function waitFeedContainer(controller) {
let i = 0;
let logged_out = null;
let missed = document.readyState != 'loading';
return new Promise((resolve, reject) => {
new MutationObserver((mutations, observer) => {
const resolved = (fragment) => {
observer.disconnect();
resolve(replaceFeed({fragment: fragment}));
setDashboardStyle();
};
const rejected = (reason) => {
controller.abort(reason);
observer.disconnect();
reject(reason);
};
if (i == 0 && !missed) {
missed = !['head', 'html', 'body'].includes(mutations[0].target.localName);
if (missed) {
const fragment = document.querySelector(
'#dashboard feed-container >' +
'div[data-target="feed-container.content"] > *:first-child'
);
if (fragment) return resolved(fragment);
}
}
if (logged_out === null && (200 < i && i < 300 || missed)) {
let body = document.querySelector('html > body');
if (logged_out = body && body.className.includes('logged-out')) {
return rejected('logged-out');
}
}
if (!missed && i + mutations.length < 1600) return i += mutations.length;
if (2*i + mutations.length < 3500) mutations = mutations.reverse();
for (const {target} of mutations) {
i++;
if (target.localName != 'include-fragment' ||
target.getAttribute('src') != '/conduit/for_you_feed') continue
return resolved(target);
}
if (i >= 1600 && document.querySelector('#ajax-error-message')) {
rejected('No "for you" feed was found');
}
}).observe(document, {childList: true, subtree: true});
})
}
if (document.readyState != 'loading' &&
document.querySelector('body').className.includes('logged-out')) {
return;
}
const controller = new AbortController();
const signal = controller.signal;
const task = [fetchFeed(signal).catch((error) => error)];
if (document.readyState != 'complete') task.push(waitFeedContainer(controller));
Promise.all(task)
.then(([feed, container]) => {
if (!(feed instanceof Element)) throw(feed);
if (container !== false) {
replaceFeed({feed: feed, container: container});
if (!container) setDashboardStyle();
}
})
.catch((error) => {
if (error.toString().includes('logged-out')) return;
console.error('Could not fetch dashboard feed:', error);
const fragment = document.querySelector(
'div[data-target="feed-container.content"] > *:first-child'
);
if (fragment) {
fragment.children[0].hidden = true;
fragment.children[1].hidden = false;
fragment.children[1].querySelector('a').className = '';
}
});
})();