From 3dc639e30512131ae4e01eca439a3074b576be25 Mon Sep 17 00:00:00 2001 From: everestmcarthur <280397636+everestmcarthur@users.noreply.github.com> Date: Sat, 1 Aug 2026 20:24:05 -0400 Subject: [PATCH] YouBar+: reset to upstream's exact original logic --- plugins/you-bar-plus/manifest.json | 2 +- .../src/patches/youBarButtons.tsx | 140 +++++++----------- plugins/you-bar-plus/src/ui/Settings.tsx | 5 +- shared/lib/forceRerender.ts | 39 ----- 4 files changed, 54 insertions(+), 132 deletions(-) delete mode 100644 shared/lib/forceRerender.ts diff --git a/plugins/you-bar-plus/manifest.json b/plugins/you-bar-plus/manifest.json index d8cef39..dd6a848 100644 --- a/plugins/you-bar-plus/manifest.json +++ b/plugins/you-bar-plus/manifest.json @@ -1,6 +1,6 @@ { "name": "YouBar+", - "description": "Adds up to 2 configurable buttons (Direct Messages, Settings, or none) to the YouBar, in whatever order you pick.", + "description": "Adds Settings and DM buttons to the YouBar. (May require a restart after enabling.)", "authors": [ { "name": "everestmcarthur" }, { "name": "Purple_Ξye™" } diff --git a/plugins/you-bar-plus/src/patches/youBarButtons.tsx b/plugins/you-bar-plus/src/patches/youBarButtons.tsx index 1cc3553..e0e1e7f 100644 --- a/plugins/you-bar-plus/src/patches/youBarButtons.tsx +++ b/plugins/you-bar-plus/src/patches/youBarButtons.tsx @@ -1,107 +1,67 @@ -import { findByProps } from "@vendetta/metro"; +import { findByProps, findByTypeName } from "@vendetta/metro"; import { React } from "@vendetta/metro/common"; import { instead } from "@vendetta/patcher"; import { storage } from "@vendetta/plugin"; import { getAssetIDByName } from "@vendetta/ui/assets"; -import { waitFor } from "@shared/lib/waitFor"; -import { rawFindByTypeName } from "@shared/lib/rawFind"; -import { forceRerender } from "@shared/lib/forceRerender"; -let internalForceUpdate: (() => void) | null = null; -let patchedComponent: any = null; +// Reset to Purple-EyeZ's original upstream logic, verbatim aside from the import path +// (@revenge-mod/metro -> @vendetta/metro, since this repo only has the vendetta-compat alias +// configured) - no retry loop, no raw* passive finder, no forced re-render. Every one of those +// layers added this session was meant to fix "sometimes needs a reload," but on the actual device +// buttons stopped appearing at all, even after ten restarts - worse than upstream's own documented +// "requires a restart" baseline. Resetting to the exact known-quantity upstream version first, to +// find out whether the *added* logic was the actual problem or something else in this port +// (a renamed lookup, a build difference) was always broken and just wasn't visible under the retry +// logic's own try/catch swallowing it. +export let updateYouBar = () => {}; -/** - * Lets the Settings screen force the patched YouBar to re-render right after a toggle changes. - * Prefers the cheap in-tree hook update once our wrapper has actually rendered at least once under - * the patch; falls back to forceRerender's fiber-walk otherwise, since a memoized component that - * hasn't rendered under the patch yet has no hook state to update through (see forceRerender.ts for - * why that fallback is needed at all). - */ -export function updateYouBar(): void { - if (internalForceUpdate) internalForceUpdate(); - else if (patchedComponent) forceRerender(patchedComponent); -} - -/** - * `YouBarNotificationsButton` isn't guaranteed to be registered in Metro yet at the exact moment - * onLoad runs - on a cold app start plugins can load before every screen component has actually - * been required. The original version of this plugin did a single synchronous - * `findByTypeName` lookup here and gave up for the whole session if it came back empty, which is - * the "sometimes the buttons just don't show up, restarting fixes it" bug this port fixes. - * - * Uses rawFindByTypeName (not @vendetta/metro's findByTypeName) specifically because this call - * gets retried by waitFor - Revenge's own findByTypeName permanently caches a "not found" result - * and never rescans, so retrying the cached version would just hit that poisoned cache every time - * instead of ever actually finding the component once it registers. Confirmed by reading Revenge's - * own metro finder source. - */ export default function patchYouBarButtons(): () => void { - let unpatch: () => void = () => {}; - - const handle = waitFor( - () => rawFindByTypeName("YouBarNotificationsButton"), - (YouBarNotificationsButton) => { - unpatch = applyButtonPatch(YouBarNotificationsButton); - patchedComponent = YouBarNotificationsButton; - // Finding the module and patching it doesn't make anything visible on its own - this - // component is React.memo'd with a stable prop, so once mounted it can go the rest of - // the session without ever rendering again on its own (see forceRerender.ts). Force - // one pass right now instead of waiting on luck for the buttons to actually appear. - forceRerender(YouBarNotificationsButton); - } - ); - - return () => { - handle.cancel(); - unpatch(); - internalForceUpdate = null; - patchedComponent = null; - }; -} - -function applyButtonPatch(YouBarNotificationsButton: any): () => void { - // Same lookups the original working version of this plugin used - this was a timing bug - // (see above), not a renamed-API one, so these targets are left exactly as they were. + const YouBarNotificationsButton = findByTypeName("YouBarNotificationsButton"); const userSettingsAction = findByProps("openUserSettings"); const transitionModule = findByProps("transitionToGuild"); - const ChatIcon = getAssetIDByName("ChatIcon"); + const SettingsIcon = getAssetIDByName("SettingsIcon"); + const ChatIcon = getAssetIDByName("ChatIcon"); + + if (!YouBarNotificationsButton) return () => {}; return instead("type", YouBarNotificationsButton, (args: any[], OriginalRender: (...a: any[]) => any) => { - try { - const [, forceUpdate] = React.useReducer((x: number) => ~x, 0); - internalForceUpdate = () => forceUpdate(); + const [, forceUpdate] = React.useReducer((x: number) => ~x, 0); - const res = OriginalRender(...args); - if (!res?.props?.children) return res; + updateYouBar = () => forceUpdate(); - const IconButton = res.props.children.type; - const originalProps = res.props.children.props; + const res = OriginalRender(...args); - return ( - - {storage.showDMButton && ( - transitionModule?.transitionToGuild?.("@me")} - /> - )} - {storage.showSettingsButton && ( - userSettingsAction?.openUserSettings?.()} - /> - )} - {res} - - ); - } catch { - // A shape mismatch here should mean "stock YouBar, no extra buttons" - not a crash. - return OriginalRender(...args); - } + if (!res?.props?.children) return res; + + const IconButton = res.props.children.type; + const originalProps = res.props.children.props; + + return ( + + {storage.showDMButton && ( + { + transitionModule?.transitionToGuild?.("@me"); + }} + /> + )} + + {storage.showSettingsButton && ( + { + userSettingsAction?.openUserSettings?.(); + }} + /> + )} + {res} + + ); }); } diff --git a/plugins/you-bar-plus/src/ui/Settings.tsx b/plugins/you-bar-plus/src/ui/Settings.tsx index 456ee57..3ec69d0 100644 --- a/plugins/you-bar-plus/src/ui/Settings.tsx +++ b/plugins/you-bar-plus/src/ui/Settings.tsx @@ -17,8 +17,9 @@ export default function Settings() { return ( - Fork of Purple-EyeZ's YouBar+, ported to Revenge's Vendetta-compat API with a fix - for a startup race that could silently skip the button patch for an entire session. + Fork of Purple-EyeZ's YouBar+, ported to Revenge's Vendetta-compat API. Like the + original, this patches the YouBar the moment it loads - if you enable it while + Discord's already running, you may need to restart once for the buttons to appear. false; - } - } catch { - // Best effort - a shape mismatch here just means no permanent bailout override. - } -}