-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.user.js
77 lines (64 loc) · 2.4 KB
/
main.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// ==UserScript==
// @name Sort channels by viewcount
// @namespace https://github.com/tomasz13nocon
// @version 1.3
// @description Reorders the followed channels list in the sidebar based on viewcount.
// @author Tomasz Nocoń
// @match https://www.twitch.tv/*
// @grant none
// @homepageURL https://github.com/tomasz13nocon/twitch-sort-by-viewcount
// @license MIT
// ==/UserScript==
(async function() {
'use strict';
// Returns the react element from a dom node
function findReact(dom) {
return dom[Object.keys(dom).find(a=>a.startsWith("__reactInternalInstance$"))].return.stateNode;
}
// Returns viewcount of a given element, or undefined if offline.
// element has to be the grandparent of a side-nav-card element. That's the react component.
function viewcount(element) {
let component = findReact(element);
// If "stream" property doesn't exist (optional chaining below) then the stream is offline.
return component
.props.children
.props.children
.props.stream?.content.viewersCount;
}
let sidebar;
// Delay the script until the element loads
while ((sidebar = document.getElementsByClassName("side-bar-contents")[0]) === undefined) {
await new Promise(r => setTimeout(r, 500));
}
// We're mutating DOM inside a DOM mutation event callback. This flag prevents infinite loop.
let weMutatedDom = false;
new MutationObserver((mutations) => {
if (weMutatedDom) {
weMutatedDom = false;
return;
}
// We're only interested in "new nodes added" and "text changed" mutations.
let relevantMutation = false;
for (let mutation of mutations) {
if (mutation.addedNodes[0] || mutation.type === "characterData") {
relevantMutation = true;
break;
}
}
if (!relevantMutation)
return;
let followedSection = sidebar.getElementsByClassName("side-nav-section")[0];
// Mapping to 2 parents up, as that's the outermost element for a single channel
let streams = [...followedSection.querySelectorAll("div.side-nav-card")].map(el => el.parentNode.parentNode);
streams.sort((a, b) => {
let av = viewcount(a), bv = viewcount(b);
if (av > bv)
return -1;
if (av < bv)
return 1;
return 0;
});
weMutatedDom = true;
streams[0].parentNode.append(...streams);
}).observe(sidebar, { childList: true, subtree: true, characterData: true });
})();