YouBar+: createElement interception, notification center rework
This commit is contained in:
parent
35972b836d
commit
ba0b6b6bfd
1 changed files with 47 additions and 54 deletions
|
|
@ -1,21 +1,18 @@
|
|||
import { findByProps, findByName } from "@vendetta/metro";
|
||||
import { findByProps, findByTypeName, findByName } 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 { registerTypeDetector, registerIntercept, registerPropsTransform, patchCreateElement } from "@shared/lib/createElementIntercept";
|
||||
import { registerPropsTransform, patchCreateElement } from "@shared/lib/createElementIntercept";
|
||||
import NotificationCenter from "../ui/NotificationCenter";
|
||||
|
||||
// Retry logic lives one level up in index.ts, calling this repeatedly until it succeeds - keep
|
||||
// this function itself simple.
|
||||
export let updateYouBar = () => {};
|
||||
|
||||
// findByTypeName + instead("type", ...) used to patch this, but that mutates whatever object
|
||||
// Metro's search hands back - which isn't reliably the same reference Discord's own render call
|
||||
// site reads from in this bundle (confirmed live: the property write didn't stick, and a fresh
|
||||
// findByTypeName call moments later returned undefined). registerTypeDetector instead captures
|
||||
// the exact type value at the actual createElement/jsx call site, so there's no reference
|
||||
// mismatch to go stale.
|
||||
export default function patchYouBarButtons(): () => void {
|
||||
/** Returns null (not undefined) when YouBarNotificationsButton isn't registered in Metro yet, so a caller can tell "not ready" apart from "patched, here's the unpatch fn". */
|
||||
export default function patchYouBarButtons(): (() => void) | null {
|
||||
const YouBarNotificationsButton = findByTypeName("YouBarNotificationsButton");
|
||||
const userSettingsAction = findByProps("openUserSettings");
|
||||
const transitionModule = findByProps("transitionToGuild");
|
||||
|
||||
|
|
@ -28,6 +25,8 @@ export default function patchYouBarButtons(): () => void {
|
|||
const SettingsIcon = getAssetIDByName("SettingsIcon");
|
||||
const ChatIcon = getAssetIDByName("ChatIcon");
|
||||
|
||||
if (!YouBarNotificationsButton) return null;
|
||||
|
||||
const openInbox = () => {
|
||||
if (!Navigator || !Navigation?.push) {
|
||||
console.error("[YouBar+] Inbox: Navigator not available");
|
||||
|
|
@ -48,65 +47,59 @@ export default function patchYouBarButtons(): () => void {
|
|||
));
|
||||
};
|
||||
|
||||
const cleanups: (() => void)[] = [];
|
||||
patchCreateElement(cleanups);
|
||||
|
||||
// The native bell is one of four sibling buttons YouBarNotificationsButton renders - the
|
||||
// other three are unrelated icon buttons with raw numeric asset ids. accessibilityLabel is
|
||||
// the only reliable way to pick the real bell out (confirmed live via the fiber tree).
|
||||
const elementCleanups: (() => void)[] = [];
|
||||
patchCreateElement(elementCleanups);
|
||||
registerPropsTransform(
|
||||
(props) => !!storage.showInboxButton && props?.accessibilityLabel === "Notifications",
|
||||
(props) => ({ ...props, onPress: openInbox }),
|
||||
);
|
||||
|
||||
registerTypeDetector(
|
||||
"you-bar-plus-notifications-button",
|
||||
(type) => typeof type === "function" && type.name === "YouBarNotificationsButton",
|
||||
(YouBarNotificationsButton: any) => {
|
||||
const PatchedYouBarNotificationsButton = (props: any) => {
|
||||
const [, forceUpdate] = React.useReducer((x: number) => ~x, 0);
|
||||
updateYouBar = () => forceUpdate();
|
||||
const unpatchType = instead("type", YouBarNotificationsButton, (args: any[], OriginalRender: (...a: any[]) => any) => {
|
||||
const [, forceUpdate] = React.useReducer((x: number) => ~x, 0);
|
||||
|
||||
const res = YouBarNotificationsButton(props);
|
||||
updateYouBar = () => forceUpdate();
|
||||
|
||||
if (!res?.props?.children) return res;
|
||||
const res = OriginalRender(...args);
|
||||
|
||||
const IconButton = res.props.children.type;
|
||||
const originalProps = res.props.children.props;
|
||||
if (!res?.props?.children) return res;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{storage.showDMButton && (
|
||||
<IconButton
|
||||
variant={originalProps?.variant || "tertiary"}
|
||||
size={originalProps?.size || "sm"}
|
||||
icon={ChatIcon}
|
||||
onPress={() => {
|
||||
transitionModule?.transitionToGuild?.("@me");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
const IconButton = res.props.children.type;
|
||||
const originalProps = res.props.children.props;
|
||||
|
||||
{storage.showSettingsButton && (
|
||||
<IconButton
|
||||
variant={originalProps?.variant || "tertiary"}
|
||||
size={originalProps?.size || "sm"}
|
||||
icon={SettingsIcon}
|
||||
onPress={() => {
|
||||
userSettingsAction?.openUserSettings?.();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<React.Fragment>
|
||||
{storage.showDMButton && (
|
||||
<IconButton
|
||||
variant={originalProps?.variant || "tertiary"}
|
||||
size={originalProps?.size || "sm"}
|
||||
icon={ChatIcon}
|
||||
onPress={() => {
|
||||
transitionModule?.transitionToGuild?.("@me");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{res}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
{storage.showSettingsButton && (
|
||||
<IconButton
|
||||
variant={originalProps?.variant || "tertiary"}
|
||||
size={originalProps?.size || "sm"}
|
||||
icon={SettingsIcon}
|
||||
onPress={() => {
|
||||
userSettingsAction?.openUserSettings?.();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
registerIntercept(YouBarNotificationsButton, PatchedYouBarNotificationsButton);
|
||||
},
|
||||
{ persistent: true },
|
||||
);
|
||||
{res}
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
return () => cleanups.forEach((fn) => fn());
|
||||
return () => {
|
||||
unpatchType();
|
||||
elementCleanups.forEach((fn) => fn());
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue