infiniteScroll.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // @license http://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
  2. // SPDX-License-Identifier: AGPL-3.0-only
  3. function insertBeforeLast(node, elem) {
  4. node.insertBefore(elem, node.childNodes[node.childNodes.length - 2]);
  5. }
  6. function getLoadMore(doc) {
  7. return doc.querySelector(".show-more:not(.timeline-item)");
  8. }
  9. function isDuplicate(item, itemClass) {
  10. const tweet = item.querySelector(".tweet-link");
  11. if (tweet == null) return false;
  12. const href = tweet.getAttribute("href");
  13. return document.querySelector(itemClass + " .tweet-link[href='" + href + "']") != null;
  14. }
  15. window.onload = function () {
  16. const url = window.location.pathname;
  17. const isTweet = url.indexOf("/status/") !== -1;
  18. const containerClass = isTweet ? ".replies" : ".timeline";
  19. const itemClass = containerClass + " > div:not(.top-ref)";
  20. var html = document.querySelector("html");
  21. var container = document.querySelector(containerClass);
  22. var loading = false;
  23. function handleScroll(failed) {
  24. if (loading) return;
  25. if (html.scrollTop + html.clientHeight >= html.scrollHeight - 3000) {
  26. loading = true;
  27. var loadMore = getLoadMore(document);
  28. if (loadMore == null) return;
  29. loadMore.children[0].text = "Loading...";
  30. var url = new URL(loadMore.children[0].href);
  31. url.searchParams.append("scroll", "true");
  32. fetch(url.toString()).then(function (response) {
  33. if (response.status === 404) throw "error";
  34. return response.text();
  35. }).then(function (html) {
  36. var parser = new DOMParser();
  37. var doc = parser.parseFromString(html, "text/html");
  38. loadMore.remove();
  39. for (var item of doc.querySelectorAll(itemClass)) {
  40. if (item.className == "timeline-item show-more") continue;
  41. if (isDuplicate(item, itemClass)) continue;
  42. if (isTweet) container.appendChild(item);
  43. else insertBeforeLast(container, item);
  44. }
  45. loading = false;
  46. const newLoadMore = getLoadMore(doc);
  47. if (newLoadMore == null) return;
  48. if (isTweet) container.appendChild(newLoadMore);
  49. else insertBeforeLast(container, newLoadMore);
  50. }).catch(function (err) {
  51. console.warn("Something went wrong.", err);
  52. if (failed > 3) {
  53. loadMore.children[0].text = "Error";
  54. return;
  55. }
  56. loading = false;
  57. handleScroll((failed || 0) + 1);
  58. });
  59. }
  60. }
  61. window.addEventListener("scroll", () => handleScroll());
  62. };
  63. // @license-end