fix: resolve React DOM insertBefore reconciliation crash on launcher start
This commit is contained in:
parent
945302a3cb
commit
4e19e6f54e
2 changed files with 67 additions and 108 deletions
|
|
@ -28,7 +28,6 @@
|
|||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
const AdBlocker = {
|
||||
_observer: null,
|
||||
_style: null,
|
||||
|
||||
init() {
|
||||
|
|
@ -44,32 +43,26 @@
|
|||
.subscribe-link,
|
||||
.subscribe-link-small-resolution,
|
||||
.subscribe-link-large-resolution,
|
||||
.promo-banner {
|
||||
.promo-banner,
|
||||
owadview,
|
||||
[class*="ad-container"],
|
||||
[class*="ad-banner"],
|
||||
[class*="AdView"],
|
||||
[class*="high-impact"],
|
||||
[data-ad],
|
||||
[data-ad-slot] {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 0 !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
pointer-events: none !important;
|
||||
position: absolute !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
owadview {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
position: absolute !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
[class*="ad-container"],
|
||||
[class*="ad-banner"],
|
||||
[class*="high-impact-ad"],
|
||||
[data-ad],
|
||||
[data-ad-slot] {
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
[style*="grid-template"][style*="ads"] {
|
||||
|
|
@ -79,66 +72,13 @@
|
|||
`;
|
||||
(document.head || document.documentElement).appendChild(this._style);
|
||||
|
||||
this._observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
for (const node of mutation.addedNodes) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
||||
this._removeAds(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._observer.observe(document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
setTimeout(() => this._sweepAds(), 1000);
|
||||
setTimeout(() => this._sweepAds(), 3000);
|
||||
setTimeout(() => this._sweepAds(), 6000);
|
||||
},
|
||||
|
||||
_removeAds(node) {
|
||||
if (node.tagName?.toLowerCase() === 'owadview') {
|
||||
const parent = node.parentElement;
|
||||
node.remove();
|
||||
if (parent && parent.children.length === 0) {
|
||||
parent.style.display = 'none';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const classes = node.className?.toString() || '';
|
||||
if (/\b(ad-container|ad-banner|ads-wrapper|AdView|high-impact|display-ad)\b/i.test(classes)) {
|
||||
node.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
const adChildren = node.querySelectorAll?.('owadview, [class*="ad-container"], [class*="AdView"], [class*="high-impact"]');
|
||||
if (adChildren) {
|
||||
for (const child of adChildren) {
|
||||
child.remove();
|
||||
}
|
||||
}
|
||||
// Safe cleanup of ad text wrappers without removing DOM nodes (avoids breaking React Fiber)
|
||||
setTimeout(() => this._sweepAds(), 1500);
|
||||
setTimeout(() => this._sweepAds(), 4000);
|
||||
},
|
||||
|
||||
_sweepAds() {
|
||||
const adSelectors = [
|
||||
'.curseforge-ad',
|
||||
'.ad-inner',
|
||||
'.overwolf-ad',
|
||||
'.affiliate-banner',
|
||||
'.subscribe-link',
|
||||
'.subscribe-link-small-resolution',
|
||||
'.subscribe-link-large-resolution',
|
||||
'.promo-banner',
|
||||
'owadview',
|
||||
];
|
||||
const els = document.querySelectorAll(adSelectors.join(', '));
|
||||
for (const el of els) {
|
||||
el.remove();
|
||||
}
|
||||
|
||||
try {
|
||||
const walker = document.createTreeWalker(document.body || document.documentElement, NodeFilter.SHOW_TEXT, null);
|
||||
while (walker.nextNode()) {
|
||||
const text = walker.currentNode.textContent?.trim();
|
||||
|
|
@ -153,6 +93,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -515,6 +456,8 @@
|
|||
|
||||
try {
|
||||
await renderCustomThemesSection(view);
|
||||
} catch (err) {
|
||||
console.warn('[BetterCF] Themes section rendering failed:', err);
|
||||
} finally {
|
||||
isInjecting = false;
|
||||
}
|
||||
|
|
@ -592,8 +535,12 @@
|
|||
lastRow.after(section);
|
||||
} else {
|
||||
const upsell = view.querySelector('.upsell') || view.querySelector('[class*="upsell"]');
|
||||
if (upsell) {
|
||||
view.insertBefore(section, upsell);
|
||||
if (upsell && upsell.parentNode) {
|
||||
try {
|
||||
upsell.before(section);
|
||||
} catch {
|
||||
view.appendChild(section);
|
||||
}
|
||||
} else {
|
||||
view.appendChild(section);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import hashlib
|
|||
import winreg
|
||||
import time
|
||||
import subprocess
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
|
||||
|
|
@ -401,35 +402,46 @@ def patch_curseforge(log_callback=print) -> bool:
|
|||
return False
|
||||
|
||||
try:
|
||||
# 1. Zero-Ad & Themes Patch in desktop.js
|
||||
# 1. Themes Patch in desktop.js (Ads are fully handled by desktop.css & network blocking)
|
||||
desktop_js = temp_extract / "dist" / "desktop" / "desktop.js"
|
||||
if desktop_js.exists():
|
||||
content = desktop_js.read_text(encoding="utf-8")
|
||||
# Clean up any legacy or duplicate __orig_eV replacements
|
||||
# Restore clean eV component if previously patched (in modern CurseForge, eV is the modpack card author component, not ads)
|
||||
if "/*BCF_AD_OFF*/eV=e=>{return null;let" in content:
|
||||
content = content.replace("/*BCF_AD_OFF*/eV=e=>{return null;let", "eV=e=>{let")
|
||||
log_callback("[+] Restored intact modpack card component (eV)")
|
||||
while "let __orig_eV=e=>null;" in content:
|
||||
content = content.replace("let __orig_eV=e=>null;", "")
|
||||
if "eV=e=>null;let __orig_eV=e=>{" in content:
|
||||
content = content.replace("eV=e=>null;let __orig_eV=e=>{", "eV=e=>{")
|
||||
|
||||
# Idempotently disable React ad component (eV -> return null)
|
||||
if "/*BCF_AD_OFF*/" not in content and "eV=e=>{let" in content:
|
||||
content = content.replace("eV=e=>{let", "/*BCF_AD_OFF*/eV=e=>{return null;let", 1)
|
||||
log_callback("[+] Disabled React ad component (eV -> return null)")
|
||||
if "showSwapAdsButton:T" in content:
|
||||
content = content.replace("showSwapAdsButton:T", "showSwapAdsButton:!1")
|
||||
log_callback("[+] Disabled swap ads button")
|
||||
if "R=e=>!!e.forceUnlock||!e.isPremium||!!M" in content:
|
||||
content = content.replace("R=e=>!!e.forceUnlock||!e.isPremium||!!M", "R=e=>!0")
|
||||
log_callback("[+] Unlocked all native premium themes (Settings route)")
|
||||
if "P=e=>!!e.forceUnlock||!e.isPremium||f" in content:
|
||||
content = content.replace("P=e=>!!e.forceUnlock||!e.isPremium||f", "P=e=>!0")
|
||||
log_callback("[+] Unlocked all native premium themes (Popup route)")
|
||||
if "return!r?.forceUnlock&&r?.isPremium&&!t?u.Sx.Classic:n" in content:
|
||||
content = content.replace("return!r?.forceUnlock&&r?.isPremium&&!t?u.Sx.Classic:n", "return n")
|
||||
# Unlock all native premium themes (Settings & Popup routes) across versions
|
||||
content, c1 = re.subn(
|
||||
r'([a-zA-Z0-9_$]+)=e=>!!e\.forceUnlock\|\|!e\.isPremium\|\|[^,;]+',
|
||||
r'\1=e=>!0',
|
||||
content
|
||||
)
|
||||
if c1 > 0:
|
||||
log_callback(f"[+] Unlocked native premium themes ({c1} route checks)")
|
||||
|
||||
# Unlock premium theme state validation
|
||||
content, c2 = re.subn(
|
||||
r'return!r\?\.forceUnlock&&r\?\.isPremium&&!t\?[a-zA-Z0-9_$.]+:n',
|
||||
r'return n',
|
||||
content
|
||||
)
|
||||
if c2 > 0:
|
||||
log_callback("[+] Unlocked premium theme state validation")
|
||||
if "function iy(e){return e.subscription.plan?.active}" in content:
|
||||
content = content.replace("function iy(e){return e.subscription.plan?.active}", "function iy(e){return !0}")
|
||||
log_callback("[+] Unlocked subscription plan status for themes")
|
||||
|
||||
# Unlock subscription plan status for themes
|
||||
content, c3 = re.subn(
|
||||
r'function ([a-zA-Z0-9_$]+)\(e\)\{return e\.subscription\.plan\?\.active\}',
|
||||
r'function \1(e){return !0}',
|
||||
content
|
||||
)
|
||||
if c3 > 0:
|
||||
log_callback(f"[+] Unlocked subscription plan status for themes ({c3} checks)")
|
||||
|
||||
desktop_js.write_text(content, encoding="utf-8")
|
||||
|
||||
# 2. Zero-Ad & Settings Themes CSS in desktop.css
|
||||
|
|
|
|||
Loading…
Reference in a new issue