Manhuagui helper

Improve Manhuagui

  1. // ==UserScript==
  2. // @name Manhuagui helper
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Improve Manhuagui
  6. // @author You
  7. // @match https://www.manhuagui.com/update/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=manhuagui.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const appState = loadAppState();
  16. const appStateWatchers = [];
  17. var appStateIsDirty = false;
  18. var dirtyAppStateKeys = {};
  19.  
  20. var scrollIsDirty = false;
  21.  
  22. function loadAppState() {
  23. try {
  24. const value = localStorage.getItem('appState');
  25. return value ? JSON.parse(value) : {};
  26. } catch (e) {
  27. console.error(e);
  28. return {};
  29. }
  30. }
  31.  
  32. function saveAppState() {
  33. if (!appStateIsDirty) return;
  34.  
  35. localStorage.setItem('appState', JSON.stringify(appState));
  36. appStateIsDirty = false;
  37. }
  38.  
  39. function getAppState(key) {
  40. return Array.isArray(key) ? key.map(k => getAppState(k)) : appState[key];
  41. }
  42.  
  43. function setAppState(key, value) {
  44. if (value !== undefined) {
  45. appState[key] = value;
  46. } else {
  47. delete appState[key];
  48. }
  49.  
  50. appStateIsDirty = true;
  51. requestAnimationFrame(() => saveAppState());
  52.  
  53. dirtyAppStateKeys[key] = true;
  54. requestAnimationFrame(() => notifyAppStateWatchers());
  55. }
  56.  
  57. function watchAppState(key, callback) {
  58. appStateWatchers.push({ key, callback });
  59. callback(getAppState(key));
  60. }
  61.  
  62. function notifyAppStateWatchers() {
  63. if (!Object.entries(dirtyAppStateKeys).length) return;
  64.  
  65. for (const watcher of appStateWatchers) {
  66. const isDirty = Array.isArray(watcher.key) ? watcher.key.some(k => dirtyAppStateKeys[k]) : dirtyAppStateKeys[watcher.key];
  67. if (!isDirty) continue;
  68.  
  69. watcher.callback(getAppState(watcher.key));
  70. }
  71.  
  72. for (const watcher of appStateGlobalWatchers) {
  73. watcher.callback();
  74. }
  75.  
  76. dirtyAppStateKeys = {};
  77. }
  78.  
  79. function notifyScroll() {
  80. if (!scrollIsDirty) return;
  81.  
  82. window.dispatchEvent(new Event('scroll'));
  83. scrollIsDirty = false;
  84. }
  85.  
  86. function scheduleNotifyScroll() {
  87. scrollIsDirty = true;
  88. requestAnimationFrame(() => notifyScroll());
  89. }
  90.  
  91. function createStyle() {
  92. const style = document.createElement('style');
  93. style.textContent = `
  94. .mhgh-item-menu button {
  95. padding: 0px 2px;
  96. }
  97.  
  98. .mhgh-global-menu {
  99. padding: 2px;
  100. background: #fff;
  101. }
  102.  
  103. .mhgh-global-menu button {
  104. padding: 0px 2px;
  105. }
  106. `;
  107. document.head.appendChild(style);
  108. }
  109.  
  110. function createShowButton(container, title) {
  111. const button = document.createElement('button');
  112. button.type = 'button';
  113. button.textContent = 'Show';
  114. button.onclick = () => setAppState(`item.${title}.state`, undefined);
  115. container.appendChild(button);
  116.  
  117. watchAppState(`item.${title}.state`, state => {
  118. button.style.display = state == 'hidden' ? '' : 'none';
  119. });
  120. }
  121.  
  122. function createHideButton(container, title) {
  123. const button = document.createElement('button');
  124. button.type = 'button';
  125. button.textContent = 'Hide';
  126. button.onclick = () => setAppState(`item.${title}.state`, 'hidden');
  127. container.appendChild(button);
  128.  
  129. watchAppState(`item.${title}.state`, state => {
  130. button.style.display = state === undefined || state == 'like' ? '' : 'none';
  131. });
  132. }
  133.  
  134. function createLikeButton(container, title) {
  135. const button = document.createElement('button');
  136. button.type = 'button';
  137. button.textContent = 'Like';
  138. button.onclick = () => setAppState(`item.${title}.state`, 'like');
  139. container.appendChild(button);
  140.  
  141. watchAppState(`item.${title}.state`, state => {
  142. button.style.display = state != 'like' ? '' : 'none';
  143. });
  144. }
  145.  
  146. function createItemMenu(container, title) {
  147. const div = document.createElement('div');
  148. div.className = 'mhgh-item-menu';
  149. div.style.position = 'absolute';
  150. div.style.top = 0;
  151. div.style.right = '26px';
  152. div.style.zIndex = 10;
  153. container.appendChild(div);
  154.  
  155. createShowButton(div, title);
  156. createHideButton(div, title);
  157. createLikeButton(div, title);
  158. }
  159.  
  160. function createItem(container) {
  161. const title = container.querySelector('a.cover').getAttribute('title');
  162.  
  163. container.style.position = 'relative';
  164.  
  165. createItemMenu(container, title);
  166.  
  167. watchAppState([`item.${title}.state`, 'showAll'], ([state, showAll]) => {
  168. const hidden = state == 'hidden';
  169. const oldDisplay = container.style.display;
  170. container.style.display = showAll || !hidden ? '' : 'none';
  171. container.style.opacity = showAll && hidden ? .5 : '';
  172.  
  173. if (container.style.display != oldDisplay) {
  174. scheduleNotifyScroll();
  175. }
  176. });
  177. }
  178.  
  179. function createShowAllButton(container) {
  180. const button = document.createElement('button');
  181. button.type = 'button';
  182. button.textContent = 'Show all';
  183. button.onclick = () => setAppState(`showAll`, '1');
  184. container.appendChild(button);
  185.  
  186. watchAppState('showAll', showAll => {
  187. button.style.display = showAll ? 'none' : '';
  188. });
  189. }
  190.  
  191. function createHideUnwantedButton(container) {
  192. const button = document.createElement('button');
  193. button.type = 'button';
  194. button.textContent = 'Hide unwanted';
  195. button.onclick = () => setAppState(`showAll`, undefined);
  196. container.appendChild(button);
  197.  
  198. watchAppState('showAll', showAll => {
  199. button.style.display = showAll ? '' : 'none';
  200. });
  201. }
  202.  
  203. function createGlobalMenu(container) {
  204. const div = document.createElement('div');
  205. div.className = 'mhgh-global-menu';
  206. div.style.position = 'fixed';
  207. div.style.bottom = 0;
  208. div.style.right = '10px';
  209. div.style.zIndex = 100;
  210. container.appendChild(div);
  211.  
  212. createShowAllButton(div);
  213. createHideUnwantedButton(div);
  214. }
  215.  
  216. createStyle();
  217.  
  218. document.querySelectorAll('.latest-list li').forEach(e => createItem(e));
  219. createGlobalMenu(document.body);
  220. })();