39 lines
2.8 KiB
TypeScript
39 lines
2.8 KiB
TypeScript
// The actual reason YouBar+'s buttons only ever showed up after a full reload: YouBar's own
|
|
// button is wrapped in React.memo with the default shallow-prop comparator, and it only ever
|
|
// receives one stable boolean prop. Once it mounts, React's own updateMemoComponent bailout means
|
|
// it will *never* run its render function again - regardless of how often anything around it
|
|
// re-renders - unless something upstream schedules real work that reaches this exact fiber. That
|
|
// makes patching `.type` alone invisible until pure luck (an unrelated future re-render) happens
|
|
// to touch it, which on a live device can be "eventually" or "never in this session" - matching
|
|
// exactly what looked like "needs 2 reloads."
|
|
//
|
|
// Two earlier versions of this file tried to actively force that render pass:
|
|
// - Walking React's DevTools fiber-root registry (__REACT_DEVTOOLS_GLOBAL_HOOK__). Confirmed via
|
|
// Key Inspector on a real device that hook doesn't exist on this Discord build at all - that
|
|
// whole approach was silently doing nothing.
|
|
// - Calling setParams() on the current route via getRootNavigationRef(), to force a genuine
|
|
// react-navigation state update. This ran unconditionally, synchronously, the moment the button
|
|
// module first initializes - which on a cold boot can be very early, before navigation has
|
|
// fully settled. After shipping it, buttons stopped appearing even after a full restart - a
|
|
// real regression, not just "still needs a reload." Poking live navigation state as an automatic
|
|
// side effect of applying a UI patch is a meaningfully bigger risk than anything else in this
|
|
// fix, and it's the prime suspect, so it's been pulled entirely rather than debugged further
|
|
// blind. (It also used the cached findByProps instead of this repo's raw* variants, so on a
|
|
// build where that lookup wasn't registered yet, it would have permanently poisoned
|
|
// getRootNavigationRef for the rest of the session - a second, independent reason to drop it.)
|
|
//
|
|
// What's left is the one part that's unconditionally safe: if `target` is a memo wrapper,
|
|
// permanently override its `compare` so that whenever a future render pass *does* reach this fiber
|
|
// for any reason, it can never bail out again. This doesn't make buttons appear instantly on a hot
|
|
// toggle - that still needs a real trigger, which is worth another attempt, but only ever tested
|
|
// manually first via Key Inspector's Eval tool where its effect can actually be observed before
|
|
// it's wired back into anything that runs automatically.
|
|
export function forceRerender(target: any): void {
|
|
try {
|
|
if (target && typeof target === "object" && "compare" in target) {
|
|
target.compare = () => false;
|
|
}
|
|
} catch {
|
|
// Best effort - a shape mismatch here just means no permanent bailout override.
|
|
}
|
|
}
|