Inoreader Article Sort By Popularity Button

Add a button to sort all articles by their popularity score in descending order.

  1. // ==UserScript==
  2. // @name Inoreader Article Sort By Popularity Button
  3. // @version 20241028
  4. // @description Add a button to sort all articles by their popularity score in descending order.
  5. // @author jamesdeluk
  6. // @match https://www.inoreader.com/*
  7. // @grant none
  8. // @namespace https://greasyfork.org/users/242246
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. function parseScore(scoreText) {
  15. var score = 0;
  16. if (scoreText.includes('k')) {
  17. score = parseFloat(scoreText.replace('k', '')) * 1000;
  18. } else if (scoreText.includes('M')) {
  19. score = parseFloat(scoreText.replace('M', '')) * 1000000;
  20. } else {
  21. score = parseFloat(scoreText);
  22. }
  23. return score;
  24. }
  25.  
  26. function sortArticlesByScore() {
  27. // Select the container that holds all article headers
  28. var articlesContainer = document.querySelector('#reader_pane'); // Adjust the selector if necessary
  29.  
  30. if (!articlesContainer) return;
  31.  
  32. // Select all article headers
  33. var articleHeaders = Array.from(articlesContainer.querySelectorAll('.ar'));
  34.  
  35. // Sort the article headers by their popularity score in descending order
  36. articleHeaders.sort((a, b) => {
  37. var scoreA = parseScore(a.querySelector('.popularity_score_span')?.innerText || '0');
  38. var scoreB = parseScore(b.querySelector('.popularity_score_span')?.innerText || '0');
  39. return scoreB - scoreA;
  40. });
  41.  
  42. // Remove all article headers from the container
  43. articleHeaders.forEach(header => articlesContainer.removeChild(header));
  44.  
  45. // Append the sorted article headers back to the container
  46. articleHeaders.forEach(header => articlesContainer.appendChild(header));
  47.  
  48. // Ensure #no_more_div is the last element
  49. ensureNoMoreDivLast();
  50. }
  51.  
  52. function addSortButton() {
  53. const sortButton = document.createElement('button');
  54. sortButton.id = 'sb_rp_sort';
  55. sortButton.textContent = 'Sort';
  56. sortButton.className = 'btn btn-sm btn-outline-text';
  57. sortButton.title = 'Sort';
  58.  
  59. // // Add click event to the sort button
  60. sortButton.addEventListener('click', sortArticlesByScore);
  61.  
  62. return sortButton;
  63.  
  64. }
  65.  
  66. function ensureSortButton() {
  67. const toolbar = document.querySelector('.nav.nav-toolbar.mx-0.justify-content-end');
  68. if (toolbar && !document.getElementById('sb_rp_sort')) {
  69. const sortButton = addSortButton();
  70. const listItem = document.createElement('li');
  71. listItem.className = 'nav-item ml-2';
  72. listItem.appendChild(sortButton);
  73. toolbar.insertBefore(listItem, toolbar.children[1]);
  74. }
  75. }
  76.  
  77. function ensureNoMoreDivLast() {
  78. var readerPane = document.getElementById('reader_pane');
  79. var noMoreDiv = document.getElementById('no_more_div');
  80. if (readerPane && noMoreDiv && noMoreDiv.nextSibling) {
  81. readerPane.appendChild(noMoreDiv);
  82. }
  83. }
  84.  
  85. function init() {
  86. ensureSortButton();
  87. ensureNoMoreDivLast();
  88.  
  89. // Use MutationObserver to ensure the button persists and #no_more_div stays last
  90. var observer = new MutationObserver(() => {
  91. ensureSortButton();
  92. ensureNoMoreDivLast();
  93. });
  94.  
  95. observer.observe(document.body, { childList: true, subtree: true });
  96. }
  97.  
  98. // Run the function to add the sort button and ensure #no_more_div is last on page load
  99. window.addEventListener('load', init);
  100. })();