[userscript] [Feedly] Add "Sort by engagement" button to Feedly
// ==UserScript==
// @name [Feedly] Sort by engagement
// @namespace https://gist.github.com/tkrkt
// @description Add "Sort by engagement" button to header
// @include https://feedly.com/*
// @version 7
// @grant none
// ==/UserScript==
function watch(callback) {
setTimeout(function () {
if (document.querySelector('#searchBarFX .search-bar-right-col')) {
callback();
} else {
watch(callback);
}
}, 1000);
}
function parseEngagement(str) {
str = str.trim();
const num = Number.parseInt(str, 10);
if (!str) return 0;
if (str.endsWith('K')) return num * 1000;
if (str.endsWith('M')) return num * 1000 * 1000;
return num;
}
function sort() {
const container = document.querySelector('#feedlyPageFX .list-entries');
const sortedMarkerClass = 'sorted-marker';
const existingMarker = container.querySelector('h4.sorted-marker');
if (existingMarker) {
existingMarker.parentElement.removeChild(existingMarker);
}
const endOfFeed = container.querySelector('h4');
const entries = Array.from(container.querySelectorAll('.entry'));
!endOfFeed && entries.pop();
const sorted = entries.filter(elem => elem.classList.contains('unread'));
const targets = (sorted.length ? sorted : entries).filter((elem) => {
const ago = elem.querySelector('.ago');
if (ago && ago.textContent.trim() === 'Sponsored') {
elem.style.display = 'none';
return false;
} else {
return true;
}
});
const articleNumElement = document.querySelector('.mark-as-read-button span');
let articleNum;
if (articleNumElement) {
articleNum = +articleNumElement.textContent;
}
if (!endOfFeed && articleNum) {
const marker = document.createElement('h4');
marker.textContent = `Sorted ${targets.length} of ${articleNum} unread articles`;
marker.classList.add('sorted-marker');
Object.assign(marker.style, {
marginTop: '1rem',
marginBottom: '2rem'
});
container.insertBefore(marker, container.firstElementChild);
}
targets.map(elem => {
const counter = elem.querySelector('.engagement');
if (counter) {
return [elem, parseEngagement(counter.textContent)];
} else {
return [elem, 0];
}
}).sort(([,a], [,b]) => a - b).forEach(([elem]) => {
container.insertBefore(elem, container.firstElementChild);
});
}
function addButton(callback) {
const controls = document.querySelector('#searchBarFX .search-bar-right-col');
const button = document.createElement('button');
button.textContent = 'Sort by engagement';
button.title = 'Sort';
button.classList.add('secondary');
button.addEventListener('click', callback, false);
Object.assign(button.style, {
textTransform: 'none',
height: '32px',
lineHeight: '32px',
padding: '0 10px'
});
controls.insertBefore(button, controls.firstChild);
}
watch(() => addButton(sort));