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.
- }
-}