nodebb-plugin-ezoic-infinite 1.8.16 → 1.8.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/library.js +3 -30
- package/package.json +1 -1
- package/public/client.js +10 -56
package/library.js
CHANGED
|
@@ -21,34 +21,21 @@ function parseBool(v, def = false) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async function getAllGroups() {
|
|
24
|
-
const now = Date.now();
|
|
25
|
-
if (_groupsCache && (now - _groupsCacheAt) < GROUPS_TTL) return _groupsCache;
|
|
26
|
-
|
|
27
24
|
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
28
25
|
if (!names || !names.length) {
|
|
29
26
|
names = await db.getSortedSetRange('groups:visible:createtime', 0, -1);
|
|
30
27
|
}
|
|
31
28
|
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
32
29
|
const data = await groups.getGroupsData(filtered);
|
|
33
|
-
|
|
30
|
+
// Filter out nulls (groups deleted between the sorted-set read and getGroupsData)
|
|
31
|
+
const valid = data.filter(g => g && g.name);
|
|
34
32
|
valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
35
|
-
|
|
36
|
-
_groupsCacheAt = now;
|
|
37
|
-
_groupsCache = valid;
|
|
38
33
|
return valid;
|
|
39
34
|
}
|
|
40
|
-
|
|
41
35
|
let _settingsCache = null;
|
|
42
36
|
let _settingsCacheAt = 0;
|
|
43
37
|
const SETTINGS_TTL = 30000; // 30s
|
|
44
38
|
|
|
45
|
-
let _groupsCache = null;
|
|
46
|
-
let _groupsCacheAt = 0;
|
|
47
|
-
const GROUPS_TTL = 5 * 60 * 1000; // 5min
|
|
48
|
-
|
|
49
|
-
const _excludedCache = new Map(); // uid -> { at, excluded }
|
|
50
|
-
const EXCLUDED_TTL = 30 * 1000; // 30s
|
|
51
|
-
|
|
52
39
|
async function getSettings() {
|
|
53
40
|
const now = Date.now();
|
|
54
41
|
if (_settingsCache && (now - _settingsCacheAt) < SETTINGS_TTL) return _settingsCache;
|
|
@@ -80,31 +67,17 @@ async function getSettings() {
|
|
|
80
67
|
|
|
81
68
|
async function isUserExcluded(uid, excludedGroups) {
|
|
82
69
|
if (!uid || !excludedGroups.length) return false;
|
|
83
|
-
|
|
84
|
-
const now = Date.now();
|
|
85
|
-
const cached = _excludedCache.get(uid);
|
|
86
|
-
if (cached && (now - cached.at) < EXCLUDED_TTL) return cached.excluded;
|
|
87
|
-
|
|
88
70
|
const userGroups = await groups.getUserGroups([uid]);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
_excludedCache.set(uid, { at: now, excluded });
|
|
92
|
-
// petite hygiene : éviter une map qui grossit sans limite
|
|
93
|
-
if (_excludedCache.size > 5000) _excludedCache.clear();
|
|
94
|
-
|
|
95
|
-
return excluded;
|
|
71
|
+
return (userGroups[0] || []).some(g => excludedGroups.includes(g.name));
|
|
96
72
|
}
|
|
97
73
|
|
|
98
|
-
|
|
99
74
|
plugin.onSettingsSet = function (data) {
|
|
100
75
|
// Invalider le cache dès que les settings de ce plugin sont sauvegardés via l'ACP
|
|
101
76
|
if (data && data.hash === SETTINGS_KEY) {
|
|
102
77
|
_settingsCache = null;
|
|
103
|
-
_excludedCache.clear();
|
|
104
78
|
}
|
|
105
79
|
};
|
|
106
80
|
|
|
107
|
-
|
|
108
81
|
plugin.addAdminNavigation = async (header) => {
|
|
109
82
|
header.plugins = header.plugins || [];
|
|
110
83
|
header.plugins.push({
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
const S = {
|
|
81
81
|
pageKey: null,
|
|
82
82
|
cfg: null,
|
|
83
|
-
poolSig: null,
|
|
84
83
|
|
|
85
84
|
pools: { topics: [], posts: [], categories: [] },
|
|
86
85
|
cursors: { topics: 0, posts: 0, categories: 0 },
|
|
@@ -88,7 +87,6 @@
|
|
|
88
87
|
lastShow: new Map(), // id → timestamp dernier show
|
|
89
88
|
|
|
90
89
|
io: null,
|
|
91
|
-
ioMargin: null,
|
|
92
90
|
domObs: null,
|
|
93
91
|
mutGuard: 0, // compteur internalMutation
|
|
94
92
|
|
|
@@ -124,31 +122,14 @@
|
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
function initPools(cfg) {
|
|
127
|
-
// (Perf) Ne reparse les pools que si les strings ont changé.
|
|
128
|
-
const sig = `${cfg.placeholderIds || ''}§${cfg.messagePlaceholderIds || ''}§${cfg.categoryPlaceholderIds || ''}`;
|
|
129
|
-
if (S.poolSig === sig) return;
|
|
130
|
-
S.poolSig = sig;
|
|
131
|
-
|
|
132
125
|
S.pools.topics = parseIds(cfg.placeholderIds);
|
|
133
126
|
S.pools.posts = parseIds(cfg.messagePlaceholderIds);
|
|
134
127
|
S.pools.categories = parseIds(cfg.categoryPlaceholderIds);
|
|
135
|
-
|
|
136
|
-
// Réinitialise les curseurs si une pool change
|
|
137
|
-
S.cursors.topics = 0;
|
|
138
|
-
S.cursors.posts = 0;
|
|
139
|
-
S.cursors.categories = 0;
|
|
140
128
|
}
|
|
141
129
|
|
|
142
130
|
function parseIds(raw) {
|
|
143
|
-
// Accepte : un ID par ligne, ou séparés par virgules/espaces (ACP le mentionne).
|
|
144
131
|
const out = [], seen = new Set();
|
|
145
|
-
const
|
|
146
|
-
.replace(/\r/g, '\n')
|
|
147
|
-
.split(/[\n\s,]+/)
|
|
148
|
-
.map(s => s.trim())
|
|
149
|
-
.filter(Boolean);
|
|
150
|
-
|
|
151
|
-
for (const v of parts) {
|
|
132
|
+
for (const v of String(raw || '').split(/\r?\n/).map(s => s.trim()).filter(Boolean)) {
|
|
152
133
|
const n = parseInt(v, 10);
|
|
153
134
|
if (n > 0 && Number.isFinite(n) && !seen.has(n)) { seen.add(n); out.push(n); }
|
|
154
135
|
}
|
|
@@ -312,26 +293,23 @@
|
|
|
312
293
|
const meta = KIND[klass];
|
|
313
294
|
if (!meta) return;
|
|
314
295
|
|
|
315
|
-
|
|
316
|
-
const present = new Set();
|
|
317
|
-
try {
|
|
318
|
-
document.querySelectorAll(meta.sel).forEach(el => {
|
|
319
|
-
const v = el.getAttribute(meta.anchorAttr);
|
|
320
|
-
if (v !== null && v !== '') present.add(String(v));
|
|
321
|
-
});
|
|
322
|
-
} catch (_) {}
|
|
296
|
+
const baseTag = meta.sel.split('[')[0]; // ex: "li", "[component=..." → "" (géré)
|
|
323
297
|
|
|
324
298
|
document.querySelectorAll(`.${WRAP_CLASS}.${klass}`).forEach(w => {
|
|
325
299
|
if (ts() - parseInt(w.getAttribute(A_CREATED) || '0', 10) < MIN_PRUNE_AGE_MS) return;
|
|
326
300
|
|
|
327
301
|
const key = w.getAttribute(A_ANCHOR) ?? '';
|
|
328
|
-
const sid = key.slice(klass.length + 1); // après "kindClass:"
|
|
302
|
+
const sid = key.slice(klass.length + 1); // extrait la partie après "kindClass:"
|
|
329
303
|
if (!sid) { mutate(() => dropWrap(w)); return; }
|
|
330
304
|
|
|
331
|
-
|
|
305
|
+
const anchorEl = document.querySelector(
|
|
306
|
+
`${baseTag}[${meta.anchorAttr}="${sid.replace(/"/g, '\\"')}"]`
|
|
307
|
+
);
|
|
308
|
+
if (!anchorEl || !anchorEl.isConnected) mutate(() => dropWrap(w));
|
|
332
309
|
});
|
|
333
310
|
}
|
|
334
|
-
|
|
311
|
+
|
|
312
|
+
// ── Decluster ──────────────────────────────────────────────────────────────
|
|
335
313
|
|
|
336
314
|
/**
|
|
337
315
|
* Deux wraps adjacents = situation anormale → supprimer le moins prioritaire.
|
|
@@ -411,14 +389,8 @@
|
|
|
411
389
|
// ── IntersectionObserver & Show ────────────────────────────────────────────
|
|
412
390
|
|
|
413
391
|
function getIO() {
|
|
392
|
+
if (S.io) return S.io;
|
|
414
393
|
const margin = isMobile() ? IO_MARGIN_MOBILE : IO_MARGIN_DESKTOP;
|
|
415
|
-
if (S.io && S.ioMargin === margin) return S.io;
|
|
416
|
-
// Si la marge doit changer (resize/orientation), on recrée l'observer.
|
|
417
|
-
if (S.io && S.ioMargin !== margin) {
|
|
418
|
-
try { S.io.disconnect(); } catch (_) {}
|
|
419
|
-
S.io = null;
|
|
420
|
-
}
|
|
421
|
-
S.ioMargin = margin;
|
|
422
394
|
try {
|
|
423
395
|
S.io = new IntersectionObserver(entries => {
|
|
424
396
|
for (const e of entries) {
|
|
@@ -622,7 +594,6 @@
|
|
|
622
594
|
blockedUntil = ts() + 1500;
|
|
623
595
|
mutate(() => document.querySelectorAll(`.${WRAP_CLASS}`).forEach(dropWrap));
|
|
624
596
|
S.cfg = null;
|
|
625
|
-
S.poolSig = null;
|
|
626
597
|
S.pools = { topics: [], posts: [], categories: [] };
|
|
627
598
|
S.cursors = { topics: 0, posts: 0, categories: 0 };
|
|
628
599
|
S.mountedIds.clear();
|
|
@@ -756,22 +727,6 @@
|
|
|
756
727
|
} catch (_) {}
|
|
757
728
|
}
|
|
758
729
|
|
|
759
|
-
|
|
760
|
-
function bindResize() {
|
|
761
|
-
let t = null;
|
|
762
|
-
window.addEventListener('resize', () => {
|
|
763
|
-
clearTimeout(t);
|
|
764
|
-
t = setTimeout(() => {
|
|
765
|
-
try { getIO(); } catch (_) {}
|
|
766
|
-
// Ré-observer les placeholders existants (si IO recréé)
|
|
767
|
-
try {
|
|
768
|
-
document.querySelectorAll(`.${WRAP_CLASS} [id^="${PH_PREFIX}"]`).forEach(ph => {
|
|
769
|
-
if (ph instanceof Element) { try { S.io?.observe(ph); } catch (_) {} }
|
|
770
|
-
});
|
|
771
|
-
} catch (_) {}
|
|
772
|
-
}, 200);
|
|
773
|
-
}, { passive: true });
|
|
774
|
-
}
|
|
775
730
|
function bindScroll() {
|
|
776
731
|
let ticking = false;
|
|
777
732
|
window.addEventListener('scroll', () => {
|
|
@@ -791,7 +746,6 @@
|
|
|
791
746
|
getIO();
|
|
792
747
|
ensureDomObserver();
|
|
793
748
|
bindNodeBB();
|
|
794
|
-
bindResize();
|
|
795
749
|
bindScroll();
|
|
796
750
|
blockedUntil = 0;
|
|
797
751
|
requestBurst();
|