tkrkt
6/21/2016 - 1:07 AM

[userscript] [Feedly] Add "Sort by engagement" button to Feedly

[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));