Twitter media-only filter toggle.

Toggle non-media tweets on and off on the home timeline, for the power-viewer!

目前為 2024-09-06 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Twitter media-only filter toggle.
  3. // @version 0.20
  4. // @description Toggle non-media tweets on and off on the home timeline, for the power-viewer!
  5. // @author Cro
  6. // @match https://*.twitter.com/*
  7. // @match https://*.x.com/*
  8. // @run-at document-idle
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @namespace https://greasyfork.org/users/10865
  12. // @icon https://www.google.com/s2/favicons?domain=twitter.com
  13. // @license MIT
  14. // ==/UserScript==
  15. /* jshint esversion: 6 */
  16.  
  17. (function() {
  18. 'use strict';
  19. let storage_key = "cro-media-toggle";
  20. let show_all = GM_getValue(storage_key);
  21.  
  22. let create_ui = function(target)
  23. {
  24. let button = document.createElement("button");
  25. button.innerText = show_all ? "Showing all home tweets" : "Showing only media home tweets";
  26. button.onclick = function(event)
  27. {
  28. show_all = !show_all;
  29. GM_setValue(storage_key, show_all);
  30. location.reload();
  31. };
  32. target.prepend(button);
  33. };
  34.  
  35. let walk_objects = function*(obj)
  36. {
  37. let stack = Object.entries(obj);
  38. while (stack.length > 0)
  39. {
  40. let entry = stack.pop();
  41. yield entry;
  42. if (entry[1] != null && typeof(entry[1]) == 'object')
  43. {
  44. stack = stack.concat(Object.entries(entry[1]));
  45. }
  46. }
  47. }
  48.  
  49. let find_objects_at_keys = (obj, keys) => Array.from(walk_objects(obj)).filter(e => keys.includes(e[0])).map(e => e[1]);
  50. let any_key_in_obj = (obj, keys) => Array.from(walk_objects(obj)).some(e => keys.includes(e[0]));
  51. let has_media = (obj) => obj.entryId.includes("cursor-") || any_key_in_obj(obj, ['media', 'card']);
  52.  
  53. let update_data = function(data)
  54. {
  55. if (show_all || location.pathname != '/home')
  56. {
  57. return;
  58. }
  59. for (let obj of find_objects_at_keys(data, ['instructions']))
  60. {
  61. for (let subobj of obj)
  62. {
  63. if (subobj.hasOwnProperty('entries'))
  64. {
  65. subobj.entries = subobj.entries.filter(has_media);
  66. }
  67. }
  68. }
  69. };
  70.  
  71. let old_parse = JSON.parse;
  72. let unsafe_window_parse = unsafeWindow.JSON.parse;
  73. let new_unsafe_window_parse = function(string)
  74. {
  75. let data = old_parse(string);
  76. try
  77. {
  78. if (data != null)
  79. {
  80. update_data(data);
  81. }
  82. }
  83. catch(error)
  84. {
  85. console.log(error);
  86. }
  87. return unsafe_window_parse(JSON.stringify(data));;
  88. };
  89. exportFunction(new_unsafe_window_parse, unsafeWindow.JSON, { defineAs: "parse" });
  90.  
  91. // Wait for twitter's react crap finish loading things.
  92. let scan_interval = setInterval(function()
  93. {
  94. let target = document.body.querySelector("nav[role='navigation']");
  95. if (target)
  96. {
  97. clearInterval(scan_interval);
  98. create_ui(target);
  99. }
  100. }, 10);
  101. })();