「Feedly」Less Items

Feedly 分次标记已读

目前为 2023-02-06 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 「Feedly」Less Items
  3. // @namespace https://www.wdssmq.com/
  4. // @version 1.0.0
  5. // @author 沉冰浮水
  6. // @description Feedly 分次标记已读
  7. // @license MIT
  8. // @null ----------------------------
  9. // @contributionURL https://github.com/wdssmq#%E4%BA%8C%E7%BB%B4%E7%A0%81
  10. // @contributionAmount 5.93
  11. // @null ----------------------------
  12. // @link https://github.com/wdssmq/userscript
  13. // @link https://afdian.net/@wdssmq
  14. // @link https://greasyfork.org/zh-CN/users/6865-wdssmq
  15. // @null ----------------------------
  16. // @noframes
  17. // @run-at document-end
  18. // @match https://feedly.com/i/subscription/feed%2Fhttps%3A%2F%2F*
  19. // @match https://feedly.com/i/my
  20. // @match https://feedly.com/i/saved
  21. // @grant none
  22. // ==/UserScript==
  23.  
  24. /* jshint esversion: 6 */
  25. /* eslint-disable */
  26.  
  27. (function () {
  28. 'use strict';
  29.  
  30. const gm_name = "LessItems";
  31.  
  32. const curDate = new Date();
  33.  
  34. // -------------------------------------
  35.  
  36. const _curUrl = () => { return window.location.href };
  37. const _sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  38.  
  39. // -------------------------------------
  40.  
  41. const _log = (...args) => console.log(`[${gm_name}]\n`, ...args);
  42.  
  43. // -------------------------------------
  44.  
  45. // const $ = window.$ || unsafeWindow.$;
  46. function $n(e) {
  47. return document.querySelector(e);
  48. }
  49. function $na(e) {
  50. return document.querySelectorAll(e);
  51. }
  52.  
  53. // -------------------------------------
  54.  
  55. // 添加内容到指定元素后面
  56. function fnAfter($ne, e) {
  57. const $e = typeof e === "string" ? $n(e) : e;
  58. $e.parentNode.insertBefore($ne, $e.nextSibling);
  59. }
  60.  
  61. const objDataSet = {
  62. getDataSet(el, key, def) {
  63. return el.dataset[key] || def;
  64. },
  65. setDataSet(el, key, val) {
  66. el.dataset[key] = val;
  67. },
  68. };
  69.  
  70. const gob = {
  71. intBlocks: 0,
  72. maxBlocks: 4,
  73. intUnread: 0,
  74. minUnread: 37,
  75. bolStopScroll: false,
  76. curTimeMS: curDate.getTime(),
  77. stopScroll(preCaB = () => { }) {
  78. if (!gob.bolStopScroll) {
  79. preCaB();
  80. gob.bolStopScroll = true;
  81. gob.setDataSet();
  82. }
  83. },
  84. reset() {
  85. gob.intBlocks = 0;
  86. gob.bolStopScroll = false;
  87. },
  88. setDataSet() {
  89. objDataSet.setDataSet($n("#header-title"), "stopScroll", "true");
  90. },
  91. getDataSet() {
  92. return objDataSet.getDataSet($n("#header-title"), "stopScroll", "false");
  93. },
  94. };
  95.  
  96. function fnGetItems($baseEL = "body") {
  97. let $items = $na(".list-entries article");
  98. if ($baseEL !== "body") {
  99. $items = $baseEL.querySelectorAll("article");
  100. }
  101. return $items;
  102. }
  103.  
  104. async function fnAutoScroll($items, $blocks) {
  105. const $h2End = $n(".list-entries > h2");
  106. const $endItem = $items[$items.length - 1];
  107.  
  108. // // 阻止向下滚动
  109. // if (gob.intAutoScroll > 4 && !$h2End) {
  110. // $blocks[$blocks.length - 1].scrollIntoView();
  111. // }
  112.  
  113. if (!gob.bolStopScroll && $h2End) {
  114. $h2End.scrollIntoView();
  115. }
  116. if (gob.intBlocks > gob.maxBlocks || $h2End || !$endItem) {
  117. gob.stopScroll(
  118. () => {
  119. _log("fnAutoScroll", "自动滚动停止");
  120. _log("fnAutoScroll", gob);
  121. },
  122. );
  123. return;
  124. }
  125. // dateset 不存在时,执行
  126. if ($endItem.dataset.scrollIntoView !== "done") {
  127. $endItem.scrollIntoView();
  128. gob.intBlocks = $blocks.length;
  129. $endItem.dataset.scrollIntoView = "done";
  130. }
  131. await _sleep(1000);
  132.  
  133. // // 隐藏最新的四个区块
  134. // if (gob.intBlocks <= gob.maxBlocks && !$h2End) {
  135. // [].forEach.call($blocks, ($e, i) => {
  136. // // 隐藏
  137. // // $e.remove();
  138. // $e.style.display = "none";
  139. // $e.classList.add("hidden");
  140. // });
  141. // }
  142.  
  143. fnLessItems();
  144. }
  145.  
  146. // 构建侧边栏
  147. function fnBuildSideBar($block) {
  148. let $el = $n(".list.list-feed");
  149. if (!$el) {
  150. const $cols = $na(".row > div");
  151. [].forEach.call($cols, ($e, i) => {
  152. // 获取 $e 的类名
  153. $e.className;
  154. const text = $e.innerText;
  155. if (text === "") {
  156. $e.innerHTML = "<div><div class=\"list list-feed\"></div>";
  157. $el = $n(".list.list-feed");
  158. }
  159. });
  160. }
  161. // 设置 fixed 定位
  162. $el.parentNode.style.position = "fixed";
  163. // 获取 $block 类名
  164. const strClassBlock = $block.className.replace("EntryList__chunk", "").trim();
  165. const strClassBtn = "btn-" + strClassBlock;
  166. // _log(strClassBlock, `.${strClassBtn}`);
  167. // _log("fnBuildSideBar", $n(`.${strClassBtn}`));
  168. // 判断是否隐藏
  169. const isHidden = $block.classList.contains("hidden");
  170. if (!isHidden && !$n(`.${strClassBtn}`) && $na(".btn-LessItem").length < 4) {
  171. // 追加元素 a
  172. const $a = document.createElement("a");
  173. $a.href = "javascript:void(0);";
  174. $a.className = strClassBtn;
  175. $a.classList.add("btn-LessItem");
  176. $a.innerHTML = strClassBlock;
  177. $a.style.display = "block";
  178. // 边框和内边距
  179. $a.style.border = "1px solid rgba(0,0,0,0.15)";
  180. $a.style.padding = "3px 10px";
  181. // 圆角和外边距
  182. $a.style.borderRadius = "3px";
  183. $a.style.margin = "3px 0";
  184. // 点击事件
  185. $a.addEventListener("click", function () {
  186. $n("#feedlyPageFX").style.maxWidth = "1024px";
  187. const $items = fnGetItems($block);
  188. const intPer = Math.floor($items.length / 4);
  189. const intClick = parseInt($block.dataset.click) || 0;
  190. const curOffset = intClick * intPer;
  191. // _log({
  192. // intPer,
  193. // intClick,
  194. // curOffset,
  195. // });
  196. $items[curOffset].scrollIntoView();
  197. // $items[curOffset].style.borderTop = "4px solid #444";
  198. const $btnList = [];
  199. let strAlert = "";
  200. for (let i = 0; i < intPer; i++) {
  201. const element = $items[i + curOffset];
  202. if (!element) {
  203. continue;
  204. }
  205. // 调整样式以减少页面占用
  206. element.querySelector(".MagazineEntry").style.marginBottom = 0;
  207. element.querySelector(".MagazineEntry").style.maxWidth = "720px";
  208. // 获取标记已读按钮
  209. const $btn = element.querySelector(
  210. ".EntryMarkAsReadButton",
  211. );
  212. $btnList.push($btn);
  213. const $a = element.querySelector(".MagazineEntry__content > a");
  214. strAlert += $a.textContent + "\n\n";
  215. }
  216. $block.dataset.click = intClick + 1;
  217. setTimeout(() => {
  218. // 确认对话框
  219. // if (confirm(strAlert)) {
  220. $btnList.forEach(($btn) => {
  221. $btn.click();
  222. });
  223. if (curOffset + intPer >= $items.length) {
  224. $a.style.display = "none";
  225. return;
  226. }
  227. // }
  228. }, 1000);
  229. // alert(strAlert);
  230. // _log("fnBuildSideBar", $items);
  231. // _log("fnBuildSideBar", $btnList);
  232. });
  233. // 添加到 $el 后边
  234. fnAfter($a, $el);
  235. }
  236. }
  237.  
  238. function fnLessItems() {
  239. // 判断页面地址
  240. if (_curUrl().indexOf("subscription/") === -1) {
  241. return;
  242. }
  243. // 获取未读数
  244. gob.intUnread = parseInt($n("span.MarkAsReadButton__unread-count").textContent);
  245. // _log("fnLessItems", intUnread);
  246. // 判断未读数
  247. if (gob.intUnread < gob.minUnread) {
  248. return;
  249. }
  250. if (gob.bolStopScroll) {
  251. if (gob.getDataSet() == "false") {
  252. gob.reset();
  253. } else {
  254. return;
  255. }
  256. }
  257. const $items = fnGetItems();
  258. const $blocks = $na(".list-entries .EntryList__chunk");
  259. fnAutoScroll($items, $blocks);
  260. [].forEach.call($blocks, function ($e, i) {
  261. // 设置下边框
  262. $e.style.borderBottom = "11px solid #444";
  263. // 设置下边距
  264. $e.style.marginBottom = "11px";
  265. // 分配一个不重复的 class
  266. $e.classList.add("LessItem" + i);
  267. // $e.classList.add("LessItem");
  268. fnBuildSideBar($e);
  269. });
  270. }
  271.  
  272. // 加载完成后执行
  273. window.onload = function () {
  274. fnOnLoad();
  275. };
  276.  
  277. async function fnOnLoad() {
  278. await _sleep(1000);
  279.  
  280. // 判断加载完成
  281. if (!$n("#feedlyFrame")) {
  282. fnOnLoad();
  283. _log("fnOnLoad", "页面加载中");
  284. return;
  285. }
  286.  
  287. // 滚动条滚动时触发
  288. if ($n("#feedlyFrame") && $n("#feedlyFrame").dataset.LessItem !== "done") {
  289. $n("#feedlyFrame").dataset.LessItem = "done";
  290. // $n("#feedlyFrame").addEventListener("mouseover", fnLessItems);
  291. $n("#feedlyFrame").addEventListener("scroll", fnLessItems);
  292. _log("fnOnLoad", "列表滚动监听");
  293. }
  294. }
  295.  
  296. })();