perfectionist-plugins 2.4.7 → 2.5.0

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/auto-read.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const PLUGIN_ID = "auto-read";
2
- const version = "1.2.0";
2
+ const version = "1.3.0";
3
3
 
4
4
  const SWEEP_DEBOUNCE_MS = 500;
5
5
  const ITEM_COOLDOWN_MS = 10 * 60 * 1000;
@@ -46,14 +46,35 @@ const LEVELUP_PREFS_URI = "/lol-settings/v2/account/LCUPreferences/lol-challenge
46
46
  const TFT_BATTLE_PASS_URI = "/lol-tft-pass/v1/battle-pass";
47
47
  const TFT_HOME_HUB_URI = "/lol-tft/v1/tft/homeHub";
48
48
  const TFT_PREFS_URI = "/lol-settings/v2/account/LCUPreferences/lol-tft";
49
- const PIP_STYLE_ID = "auto-read-pips-style";
50
- const PIP_CSS = ".activity-center__tab_label_notification { display: none !important; }";
49
+ // The pips on the home screen's activity tabs. The client shows one for every tab that is not in
50
+ // tabsViewed, and on the patch notes tab until lastPatchNotesViewed matches the current major.minor build.
51
+ const ACTIVITY_PREFS_URI = "/lol-settings/v2/account/LCUPreferences/activity-center";
52
+ const ACTIVITY_NAV_URI = "/lol-activity-center/v1/content/client-nav";
53
+ const BUILD_URI = "/system/v1/builds";
54
+ const PATCH_NOTES_TAB = "lol-patch-notes";
55
+ const INFO_HUB_TAB = "info-hub";
56
+ // The client reads the activity-center setting once at startup, so a pip it already drew stays until the next
57
+ // restart even after it was marked seen. Only those already-seen pips are hidden (they are read at the source).
58
+ const SEEN_PIPS_STYLE_ID = "auto-read-seen-pips";
59
+ const SEEN_PIPS_CSS = ".activity-center__tab_label_notification_pip { display: none !important; }";
60
+
61
+ function hideSeenPips(hide) {
62
+ const existing = document.getElementById(SEEN_PIPS_STYLE_ID);
63
+ if (hide && !existing) {
64
+ const style = document.createElement("style");
65
+ style.id = SEEN_PIPS_STYLE_ID;
66
+ style.textContent = SEEN_PIPS_CSS;
67
+ document.head.appendChild(style);
68
+ } else if (!hide && existing) {
69
+ existing.remove();
70
+ }
71
+ }
51
72
 
52
73
  let api = null;
53
74
  let active = false;
54
75
  let sweepTimer = null;
55
76
  let sweepRunning = false;
56
- let sweepQueued = false;
77
+ const pendingCategories = new Set();
57
78
  let unsubscribes = [];
58
79
  const cooldowns = new Map();
59
80
  const markedGrants = new Set();
@@ -267,49 +288,92 @@ async function sweepTft() {
267
288
  await sendJson("PATCH", TFT_PREFS_URI, { data: { seenOfferIds: target }, schemaVersion: 1 }).catch(() => {});
268
289
  }
269
290
 
270
- function syncPips() {
271
- const wanted = active && on("pips");
272
- let style = document.getElementById(PIP_STYLE_ID);
273
- if (wanted && !style) {
274
- style = document.createElement("style");
275
- style.id = PIP_STYLE_ID;
276
- style.textContent = PIP_CSS;
277
- document.head.appendChild(style);
278
- } else if (!wanted && style) {
279
- style.remove();
291
+ async function sweepPips() {
292
+ const [prefs, nav, build] = await Promise.all([
293
+ getJson(ACTIVITY_PREFS_URI).catch(() => null),
294
+ getJson(ACTIVITY_NAV_URI).catch(() => null),
295
+ getJson(BUILD_URI).catch(() => null),
296
+ ]);
297
+ const tabs = (Array.isArray(nav?.data) ? nav.data : []).map((tab) => tab?.navigationItemID).filter((id) => typeof id === "string" && id);
298
+ if (!tabs.length) return;
299
+ let viewed = {};
300
+ try {
301
+ viewed = JSON.parse(prefs?.data?.tabsViewed ?? "{}") || {};
302
+ } catch {}
303
+ const patch = String(build?.version ?? "").split(".").slice(0, 2).join(".");
304
+ const missing = [...tabs, INFO_HUB_TAB].filter((id) => id !== PATCH_NOTES_TAB && viewed[id] !== true);
305
+ const patchUnseen = Boolean(patch) && tabs.includes(PATCH_NOTES_TAB) && prefs?.data?.lastPatchNotesViewed !== patch;
306
+ if (!missing.length && !patchUnseen) {
307
+ hideSeenPips(true);
308
+ return;
280
309
  }
310
+ if (!ready("activity-pips", BULK_COOLDOWN_MS)) return;
311
+ stamp("activity-pips");
312
+ const next = { ...viewed };
313
+ for (const id of missing) next[id] = true;
314
+ const data = { tabsViewed: JSON.stringify(next) };
315
+ if (patchUnseen) data.lastPatchNotesViewed = patch;
316
+ const saved = await sendJson("PATCH", ACTIVITY_PREFS_URI, { data, schemaVersion: 1 }).then(() => true, () => false);
317
+ if (saved && active) hideSeenPips(true);
281
318
  }
282
319
 
283
- function scheduleSweep() {
320
+ const SWEEPS = {
321
+ quests: () => sweepQuests(),
322
+ loot: () => sweepLootMarks(),
323
+ rewards: async () => {
324
+ await sweepRewards().catch(() => {});
325
+ await sweepIdentity().catch(() => {});
326
+ },
327
+ tft: () => sweepTft(),
328
+ pips: () => sweepPips(),
329
+ clash: () => sweepClash(),
330
+ ranked: () => sweepRanked(),
331
+ notices: () => sweepNotices(),
332
+ };
333
+ const ALL_CATEGORIES = Object.keys(SWEEPS);
334
+
335
+ // Which category a pushed URI belongs to, so one change only re-checks what it can affect.
336
+ const URI_CATEGORIES = new Map([
337
+ [MISSIONS_URI, ["quests"]],
338
+ [LOOT_URI, ["loot"]],
339
+ [CLASH_FLAGS_URI, ["clash"]],
340
+ [REWARDS_GRANTS_URI, ["rewards"]],
341
+ [LOOT_NOTIF_URI, ["rewards"]],
342
+ [MASTERY_NOTIF_URI, ["rewards"]],
343
+ ...HONOR_NOTICES.map((notice) => [notice.uri, ["rewards"]]),
344
+ ...IDENTITY_NOTICE_TYPES.map((type) => [`/lol-inventory/v1/notifications/${type}`, ["rewards"]]),
345
+ [TOKENS_PREFS_URI, ["rewards"]],
346
+ [LEVELUP_PREFS_URI, ["rewards"]],
347
+ [RANKED_NOTIF_URI, ["ranked"]],
348
+ [EOS_NOTIF_URI, ["ranked"]],
349
+ ...NOTICE_LIST_URIS.map((uri) => [uri, ["notices"]]),
350
+ [TFT_BATTLE_PASS_URI, ["tft"]],
351
+ [TFT_HOME_HUB_URI, ["tft"]],
352
+ [TFT_PREFS_URI, ["tft"]],
353
+ [ACTIVITY_PREFS_URI, ["pips"]],
354
+ [ACTIVITY_NAV_URI, ["pips"]],
355
+ ]);
356
+
357
+ function scheduleSweep(categories = ALL_CATEGORIES) {
284
358
  if (!active) return;
359
+ for (const category of categories) pendingCategories.add(category);
285
360
  clearTimeout(sweepTimer);
286
361
  sweepTimer = setTimeout(runSweep, SWEEP_DEBOUNCE_MS);
287
362
  }
288
363
 
289
364
  async function runSweep() {
290
- if (!active) return;
291
- if (sweepRunning) {
292
- sweepQueued = true;
293
- return;
294
- }
365
+ if (!active || sweepRunning) return;
295
366
  sweepRunning = true;
296
367
  try {
297
- if (on("quests")) await sweepQuests().catch(() => {});
298
- if (on("loot")) await sweepLootMarks().catch(() => {});
299
- if (on("rewards")) {
300
- await sweepRewards().catch(() => {});
301
- await sweepIdentity().catch(() => {});
368
+ while (active && pendingCategories.size) {
369
+ const batch = [...pendingCategories];
370
+ pendingCategories.clear();
371
+ for (const category of batch) {
372
+ if (on(category)) await Promise.resolve(SWEEPS[category]()).catch(() => {});
373
+ }
302
374
  }
303
- if (on("tft")) await sweepTft().catch(() => {});
304
- if (on("clash")) await sweepClash().catch(() => {});
305
- if (on("ranked")) await sweepRanked().catch(() => {});
306
- if (on("notices")) await sweepNotices().catch(() => {});
307
375
  } finally {
308
376
  sweepRunning = false;
309
- if (sweepQueued) {
310
- sweepQueued = false;
311
- scheduleSweep();
312
- }
313
377
  }
314
378
  }
315
379
 
@@ -317,14 +381,14 @@ function onEnable(utils) {
317
381
  api = utils;
318
382
  active = true;
319
383
  cooldowns.clear();
320
- for (const uri of [MISSIONS_URI, LOOT_URI, CLASH_FLAGS_URI, REWARDS_GRANTS_URI, LOOT_NOTIF_URI, MASTERY_NOTIF_URI, ...HONOR_NOTICES.map((n) => n.uri), RANKED_NOTIF_URI, EOS_NOTIF_URI, ...NOTICE_LIST_URIS, ...IDENTITY_NOTICE_TYPES.map((type) => `/lol-inventory/v1/notifications/${type}`), TOKENS_PREFS_URI, LEVELUP_PREFS_URI, TFT_BATTLE_PASS_URI, TFT_HOME_HUB_URI, TFT_PREFS_URI]) {
384
+ for (const uri of [MISSIONS_URI, LOOT_URI, CLASH_FLAGS_URI, REWARDS_GRANTS_URI, LOOT_NOTIF_URI, MASTERY_NOTIF_URI, ...HONOR_NOTICES.map((n) => n.uri), RANKED_NOTIF_URI, EOS_NOTIF_URI, ...NOTICE_LIST_URIS, ...IDENTITY_NOTICE_TYPES.map((type) => `/lol-inventory/v1/notifications/${type}`), TOKENS_PREFS_URI, LEVELUP_PREFS_URI, TFT_BATTLE_PASS_URI, TFT_HOME_HUB_URI, TFT_PREFS_URI, ACTIVITY_PREFS_URI, ACTIVITY_NAV_URI]) {
321
385
  try {
322
- const off = api.subscribe(uri, scheduleSweep);
386
+ const categories = URI_CATEGORIES.get(uri) ?? ALL_CATEGORIES;
387
+ const off = api.subscribe(uri, () => scheduleSweep(categories));
323
388
  if (off) unsubscribes.push(off);
324
389
  } catch {}
325
390
  }
326
391
  scheduleSweep();
327
- syncPips();
328
392
  }
329
393
 
330
394
  function onDisable() {
@@ -332,7 +396,7 @@ function onDisable() {
332
396
  clearTimeout(sweepTimer);
333
397
  sweepTimer = null;
334
398
  sweepRunning = false;
335
- sweepQueued = false;
399
+ pendingCategories.clear();
336
400
  for (const off of unsubscribes) {
337
401
  try {
338
402
  off();
@@ -341,14 +405,15 @@ function onDisable() {
341
405
  unsubscribes = [];
342
406
  cooldowns.clear();
343
407
  markedGrants.clear();
408
+ hideSeenPips(false);
344
409
  api = null;
345
- syncPips();
346
410
  }
347
411
 
348
- function onOptionChange() {
412
+ function onOptionChange(key, value) {
349
413
  if (!active) return;
350
- scheduleSweep();
351
- syncPips();
414
+ const category = String(key).replace(/^read./, "");
415
+ if (value && SWEEPS[category]) scheduleSweep([category]);
416
+ if (category === "pips" && !value) hideSeenPips(false);
352
417
  }
353
418
 
354
419
  export function init() {
@@ -372,7 +437,7 @@ export function init() {
372
437
  { id: "clash", label: "Clash", description: "Clash tab flags and Clash notifications.", default: true },
373
438
  { id: "ranked", label: "Ranked", description: "Ranked, end-of-season and champion mastery notices.", default: true },
374
439
  { id: "notices", label: "Notices", description: "Patch, patcher, leaver buster, lobby, behavior and gameflow notices.", default: true },
375
- { id: "pips", label: "Tab pips", description: "Hides the new pips on activity tabs.", default: true },
440
+ { id: "pips", label: "Tab pips", description: "Marks the home screen's activity tabs and patch notes as seen, so their new pips go away.", default: true },
376
441
  ],
377
442
  },
378
443
  ],