Skip to content

Commit cb74160

Browse files
committed
Greasy Fork++ | New Feature
* New Feature in v3.3.0 - Added a setting to filter the comments by recently registered user (registered within N hours). - The default CSS rule is to change the opacity of the comment. Users might do their own css for the class `discussion-item-by-recent-user`.
1 parent ee46111 commit cb74160

File tree

1 file changed

+207
-3
lines changed

1 file changed

+207
-3
lines changed

473830-greasyfork++.user.js

Lines changed: 207 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ==UserScript==
22
// @name Greasy Fork++
33
// @namespace https://github.com/iFelix18
4-
// @version 3.2.62
4+
// @version 3.3.0
55
// @author CY Fung <https://greasyfork.org/users/371179> & Davide <[email protected]>
66
// @icon https://www.google.com/s2/favicons?domain=https://greasyfork.org
77
// @description Adds various features and improves the Greasy Fork experience
@@ -190,6 +190,14 @@ const mWindow = isInIframe || (() => {
190190
default: '',
191191
save: false
192192
},
193+
hideRecentUsersWithin: {
194+
label: 'Hide Recent Users:<br><span>Hide new regeistered users within the last N hours - to avoid seeing comments from spam accounts</span>',
195+
labelPos: 'left',
196+
type: 'text',
197+
title: 'Number only. 0 means disabled. maximum is 168. (Suggested value: 48)',
198+
default: '0',
199+
size: 150
200+
},
193201
logging: {
194202
label: 'Logging',
195203
section: ['Developer options'],
@@ -374,14 +382,16 @@ const mWindow = isInIframe || (() => {
374382
}
375383
#greasyfork-plus_customBlacklist_var[class],
376384
#greasyfork-plus_hiddenList_var[class],
377-
#greasyfork-plus_milestoneNotification_var[class]{
385+
#greasyfork-plus_milestoneNotification_var[class],
386+
#greasyfork-plus_hideRecentUsersWithin_var[class]{
378387
flex-direction:column;
379388
margin-left:21px;
380389
}
381390
382391
#greasyfork-plus_customBlacklist_var[class]::before,
383392
#greasyfork-plus_hiddenList_var[class]::before,
384-
#greasyfork-plus_milestoneNotification_var[class]::before{
393+
#greasyfork-plus_milestoneNotification_var[class]::before,
394+
#greasyfork-plus_hideRecentUsersWithin_var[class]::before{
385395
/* content: "◉"; */
386396
content: "◎";
387397
position: absolute;
@@ -636,6 +646,10 @@ const mWindow = isInIframe || (() => {
636646
min-height: unset;
637647
}
638648
649+
.discussion-item-by-recent-user{
650+
opacity: 0.2;
651+
}
652+
639653
640654
`
641655

@@ -1131,6 +1145,184 @@ inIframeFn() || (async () => {
11311145
await gmc.initialized.then();
11321146
const customBlacklistRE = createRE((gmc.get('customBlacklist') || '').replace(/\s/g, '').split(',').join('|'), 'giu');
11331147

1148+
const valHideRecentUsersWithin_ = Math.floor(+gmc.get('hideRecentUsersWithin'));
1149+
const valHideRecentUsersWithin = valHideRecentUsersWithin_ > 168 ? 168 : valHideRecentUsersWithin_ > 0 ? valHideRecentUsersWithin_ : 0;
1150+
1151+
/**
1152+
* Inserts an element into a sorted array using the given comparator function.
1153+
*
1154+
* @param {Array} arr - The sorted array.
1155+
* @param {*} element - The new element to insert.
1156+
* @param {Function} comparator - A function that takes two arguments (a, b)
1157+
* and returns a negative number if a < b,
1158+
* zero if a === b, or a positive number if a > b.
1159+
*/
1160+
function insertSorted(arr, element, comparator) {
1161+
let left = 0;
1162+
let right = arr.length;
1163+
1164+
// Use binary search to find the correct index for insertion.
1165+
while (left < right) {
1166+
const mid = Math.floor((left + right) / 2);
1167+
if (comparator(element, arr[mid]) < 0) {
1168+
right = mid;
1169+
} else {
1170+
left = mid + 1;
1171+
}
1172+
}
1173+
1174+
// Insert the element at the found index.
1175+
arr.splice(left, 0, element);
1176+
return arr;
1177+
}
1178+
1179+
function findIndexSorted(arr, element, comparator) {
1180+
let left = 0;
1181+
let right = arr.length;
1182+
1183+
// Use binary search to find the correct index for insertion.
1184+
while (left < right) {
1185+
const mid = Math.floor((left + right) / 2);
1186+
if (comparator(element, arr[mid]) < 0) {
1187+
right = mid;
1188+
} else {
1189+
left = mid + 1;
1190+
}
1191+
}
1192+
1193+
return left; // arr_j > target [ arr_(j-1) <= target ]
1194+
1195+
}
1196+
1197+
let targetHiddenRecentDateTime = 0;
1198+
let userCreations = [];
1199+
let recentUserMP = Promise.resolve(0);
1200+
const fetchUserCreations = () => {
1201+
if (sessionStorage.__TMP_userCreations682__) {
1202+
try {
1203+
return JSON.parse(sessionStorage.__TMP_userCreations682__);
1204+
// console.log(388, userCreations);
1205+
} catch (e) {
1206+
console.warn(e);
1207+
}
1208+
}
1209+
return [];
1210+
}
1211+
userCreations = fetchUserCreations();
1212+
const cleanupUserCreations = () => {
1213+
1214+
// in case the record in sessionStorage is modified by other instances as well.
1215+
{
1216+
let storedUserCreations = fetchUserCreations();
1217+
let encodedArrS = storedUserCreations.map(e => e.join(','));
1218+
let encodedArrC = userCreations.map(e => e.join(','));
1219+
let encodedSetC = new Set(encodedArrC);
1220+
let encodedArrSFiltered = encodedArrS.filter(e => !encodedSetC.has(e));
1221+
let elementsMissing = encodedArrSFiltered.map(e => e.split(',').map(d => +d));
1222+
for (const element of elementsMissing) {
1223+
insertSorted(userCreations, element, (a, b) => a[1] - b[1]);
1224+
}
1225+
}
1226+
1227+
// since targetHiddenRecentDateTime is expected monotonic increasing, small values are useless in checking.
1228+
let deleteCount = 0;
1229+
for (let i = 0; i < userCreations.length - 1; i++) {
1230+
if (userCreations[i][1] < targetHiddenRecentDateTime && userCreations[i + 1][1] < targetHiddenRecentDateTime) {
1231+
deleteCount++;
1232+
} else {
1233+
break;
1234+
}
1235+
}
1236+
if (deleteCount > 0) {
1237+
deleteCount === 1 ? userCreations.shift() : userCreations.splice(0, deleteCount);
1238+
}
1239+
1240+
// trim the cache array to "8 + HALF" element size
1241+
while (userCreations.length > 32) {
1242+
// remove idx 8, 10, 12, ... 32, etc.
1243+
// 33 -> 20; 34 -> 21; 35 -> 21 , 36 -> 22, ...
1244+
// len2 = Math.floor(len1 / 2) + 4
1245+
// 58 -> 33 -> 20
1246+
userCreations = userCreations.filter((e, idx) => {
1247+
if (idx < 8) return true;
1248+
return (idx % 2) === 1;
1249+
});
1250+
}
1251+
1252+
sessionStorage.__TMP_userCreations682__ = JSON.stringify(userCreations);
1253+
// console.log(1238, userCreations);
1254+
1255+
};
1256+
const mightHideDiscussionByRecentlyNewUser = async (userId) => {
1257+
1258+
let result = null;
1259+
1260+
const tryBeforeNetworkRequest = () => {
1261+
1262+
let idxJ = findIndexSorted(userCreations, [null, targetHiddenRecentDateTime], (a, b) => a[1] - b[1]);
1263+
1264+
// findIndexSorted's result is arr_j[1] > targetHiddenRecentDateTime; reduce index for equality case
1265+
while (idxJ > 0 && userCreations[idxJ - 1][1] >= targetHiddenRecentDateTime) {
1266+
idxJ--;
1267+
}
1268+
1269+
let newFrom = 0, oldFrom = 0;
1270+
1271+
if (idxJ >= 0 && idxJ < userCreations.length && userCreations[idxJ][1] >= targetHiddenRecentDateTime) {
1272+
newFrom = userCreations[idxJ][0];
1273+
}
1274+
1275+
if (newFrom > 0 && userId >= newFrom) {
1276+
// console.log('newForm -> isRecent', userId, userCreations[idxJ][0], userCreations[idxJ][1], 'target', targetHiddenRecentDateTime)
1277+
return (result = true);
1278+
}
1279+
1280+
if (idxJ > 0 && idxJ - 1 < userCreations.length && userCreations[idxJ - 1][1] < targetHiddenRecentDateTime) {
1281+
oldFrom = userCreations[idxJ - 1][0];
1282+
}
1283+
1284+
if (oldFrom > 0 && userId <= oldFrom) {
1285+
// console.log('oldFrom -> notRecent', userId, userCreations[idxJ-1][0], userCreations[idxJ-1][1], 'target', targetHiddenRecentDateTime)
1286+
return (result = false);
1287+
}
1288+
1289+
return { newFrom, oldFrom };
1290+
1291+
}
1292+
1293+
tryBeforeNetworkRequest();
1294+
if (result !== null) return result;
1295+
1296+
recentUserMP = recentUserMP.then(async () => {
1297+
1298+
try {
1299+
1300+
const { newFrom, oldFrom } = tryBeforeNetworkRequest();
1301+
if (result !== null) return result;
1302+
1303+
// console.log(505, newFrom, oldFrom, userId);
1304+
1305+
const userData = await getUserData(userId, false);
1306+
if (userData.id !== userId) return (result = false);
1307+
const insertData = [userId, +(new Date(userData.created_at))];
1308+
insertSorted(userCreations, insertData, (a, b) => a[1] - b[1]);
1309+
1310+
// console.log('regDate', insertData);
1311+
1312+
result = insertData[1] >= targetHiddenRecentDateTime;
1313+
cleanupUserCreations();
1314+
1315+
} catch (e) {
1316+
console.warn(e);
1317+
}
1318+
1319+
});
1320+
1321+
await recentUserMP.then();
1322+
return result;
1323+
1324+
}
1325+
11341326
if (typeof GM.registerMenuCommand === 'function') {
11351327
GM.registerMenuCommand('Configure', () => gmc.open());
11361328
GM.registerMenuCommand('Reset Everything', () => {
@@ -2217,6 +2409,7 @@ inIframeFn() || (async () => {
22172409
}
22182410

22192411
const foundDiscussionList = (discussionsList) => {
2412+
targetHiddenRecentDateTime = Date.now() - valHideRecentUsersWithin * 3600000;
22202413

22212414
let rid = 0;
22222415
let g = () => {
@@ -2242,6 +2435,17 @@ inIframeFn() || (async () => {
22422435
// if (gmc.get('showInstallButton')) {
22432436
// showInstallButton(scriptID, element)
22442437
// }
2438+
let t;
2439+
if (t = element.querySelector('a.user-link[href*="/users/"]')) {
2440+
const m = /\/users\/(\d+)/.exec(`${t.getAttribute('href')}`);
2441+
if (m) {
2442+
const userId = +m[1];
2443+
mightHideDiscussionByRecentlyNewUser(userId).then((isNewUser)=>{
2444+
element.classList.toggle('discussion-item-by-recent-user', isNewUser);
2445+
});
2446+
}
2447+
}
2448+
22452449
}
22462450

22472451
}

0 commit comments

Comments
 (0)