From a4f4993084c0bdea97a8af085a5b4bbea64c416e Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Mon, 28 Aug 2023 15:57:46 -0700 Subject: [PATCH 1/8] Plaintext patch to include aria-controls on all TabBar items --- .../coremods/utilityClasses/plaintextPatches.ts | 14 ++++++++++++++ src/renderer/managers/coremods.ts | 2 ++ 2 files changed, 16 insertions(+) create mode 100644 src/renderer/coremods/utilityClasses/plaintextPatches.ts diff --git a/src/renderer/coremods/utilityClasses/plaintextPatches.ts b/src/renderer/coremods/utilityClasses/plaintextPatches.ts new file mode 100644 index 000000000..106301db6 --- /dev/null +++ b/src/renderer/coremods/utilityClasses/plaintextPatches.ts @@ -0,0 +1,14 @@ +import { PlaintextPatch } from "src/types"; + +export default [ + { + // re-add aria-controls to all TabBar.Item components, even when it's not currently selected + find: /"aria-controls":.+\?.+\(""\.concat\(.+\)\):void 0,/, + replacements: [ + { + match: /"aria-controls":.+\?(.+\(""\.concat\(.+\)\)):void 0,/, + replace: (_, tabName) => `"aria-controls":${tabName},`, + }, + ], + }, +] as PlaintextPatch[]; diff --git a/src/renderer/managers/coremods.ts b/src/renderer/managers/coremods.ts index 39771892a..a1129c278 100644 --- a/src/renderer/managers/coremods.ts +++ b/src/renderer/managers/coremods.ts @@ -9,6 +9,7 @@ import { default as messagePopover } from "../coremods/messagePopover/plaintextP import { default as notices } from "../coremods/notices/plaintextPatches"; import { default as contextMenu } from "../coremods/contextMenu/plaintextPatches"; import { default as languagePlaintext } from "../coremods/language/plaintextPatches"; +import { default as utilityClassesPlaintext } from "../coremods/utilityClasses/plaintextPatches"; import { Logger } from "../modules/logger"; const logger = Logger.api("Coremods"); @@ -79,5 +80,6 @@ export function runPlaintextPatches(): void { notices, contextMenu, languagePlaintext, + utilityClassesPlaintext, ].forEach(patchPlaintext); } From 9b45e2af8ad4f63cd7c24905232786477fb2bc5f Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Mon, 28 Aug 2023 19:23:33 -0700 Subject: [PATCH 2/8] include aria-controls using monkey patch instead --- src/renderer/coremods/utilityClasses/index.ts | 32 +++++++++++++++++++ .../utilityClasses/plaintextPatches.ts | 14 -------- src/renderer/managers/coremods.ts | 4 +-- 3 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 src/renderer/coremods/utilityClasses/index.ts delete mode 100644 src/renderer/coremods/utilityClasses/plaintextPatches.ts diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts new file mode 100644 index 000000000..875480c5c --- /dev/null +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -0,0 +1,32 @@ +import { Injector } from "@replugged"; +import type React from "react"; +import { getByProps } from "src/renderer/modules/webpack"; + +const injector = new Injector(); + +interface TabBarItemProps { + id: string; + "aria-controls"?: string; +} + +interface TabBarItemType extends React.Component { + render(): React.ReactElement; +} + +export function start(): void { + const mod = getByProps<{ TabBar: { Item: { prototype: TabBarItemType } } }>("TabBar"); + if (!mod) { + throw new Error("Failed to find TabBar module!"); + } + + injector.after(mod.TabBar.Item.prototype, "render", function (this: TabBarItemType, _, res) { + if (typeof this.props.id === "string") { + res.props["aria-controls"] = `${this.props.id.replace(/\s+/g, "-").toLowerCase()}-tab`; + } + return res; + }); +} + +export function stop(): void { + injector.uninjectAll(); +} diff --git a/src/renderer/coremods/utilityClasses/plaintextPatches.ts b/src/renderer/coremods/utilityClasses/plaintextPatches.ts deleted file mode 100644 index 106301db6..000000000 --- a/src/renderer/coremods/utilityClasses/plaintextPatches.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PlaintextPatch } from "src/types"; - -export default [ - { - // re-add aria-controls to all TabBar.Item components, even when it's not currently selected - find: /"aria-controls":.+\?.+\(""\.concat\(.+\)\):void 0,/, - replacements: [ - { - match: /"aria-controls":.+\?(.+\(""\.concat\(.+\)\)):void 0,/, - replace: (_, tabName) => `"aria-controls":${tabName},`, - }, - ], - }, -] as PlaintextPatch[]; diff --git a/src/renderer/managers/coremods.ts b/src/renderer/managers/coremods.ts index a1129c278..81d4aab74 100644 --- a/src/renderer/managers/coremods.ts +++ b/src/renderer/managers/coremods.ts @@ -9,7 +9,6 @@ import { default as messagePopover } from "../coremods/messagePopover/plaintextP import { default as notices } from "../coremods/notices/plaintextPatches"; import { default as contextMenu } from "../coremods/contextMenu/plaintextPatches"; import { default as languagePlaintext } from "../coremods/language/plaintextPatches"; -import { default as utilityClassesPlaintext } from "../coremods/utilityClasses/plaintextPatches"; import { Logger } from "../modules/logger"; const logger = Logger.api("Coremods"); @@ -33,6 +32,7 @@ export namespace coremods { export let rpc: Coremod; export let watcher: Coremod; export let welcome: Coremod; + export let utilityClasses: Coremod; } export async function start(name: keyof typeof coremods): Promise { @@ -55,6 +55,7 @@ export async function startAll(): Promise { coremods.rpc = await import("../coremods/rpc"); coremods.watcher = await import("../coremods/watcher"); coremods.welcome = await import("../coremods/welcome"); + coremods.utilityClasses = await import("../coremods/utilityClasses"); await Promise.all( Object.entries(coremods).map(async ([name, mod]) => { try { @@ -80,6 +81,5 @@ export function runPlaintextPatches(): void { notices, contextMenu, languagePlaintext, - utilityClassesPlaintext, ].forEach(patchPlaintext); } From 4305002b29c412d81d070050377d12dc549ef79e Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Thu, 31 Aug 2023 17:16:25 -0700 Subject: [PATCH 3/8] add attribute for current nitro theme to html --- src/renderer/coremods/utilityClasses/index.ts | 69 ++++++++++++++++--- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts index 875480c5c..6d2402f0d 100644 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -1,8 +1,11 @@ -import { Injector } from "@replugged"; +import { Injector /*, Logger*/ } from "@replugged"; import type React from "react"; -import { getByProps } from "src/renderer/modules/webpack"; +import type { Store } from "src/renderer/modules/common/flux"; +import { getByProps, getByStoreName } from "src/renderer/modules/webpack"; const injector = new Injector(); +//const logger = Logger.coremod("UtilityClasses"); +const html = document.documentElement; interface TabBarItemProps { id: string; @@ -13,18 +16,64 @@ interface TabBarItemType extends React.Component { render(): React.ReactElement; } -export function start(): void { - const mod = getByProps<{ TabBar: { Item: { prototype: TabBarItemType } } }>("TabBar"); - if (!mod) { +// Re-adds the tab bar item's ID as an always-present attribute +function tabBarItemId(): void { + const TabBarModule = getByProps<{ TabBar: { Item: { prototype: TabBarItemType } } }>("TabBar"); + if (!TabBarModule) { throw new Error("Failed to find TabBar module!"); } - injector.after(mod.TabBar.Item.prototype, "render", function (this: TabBarItemType, _, res) { - if (typeof this.props.id === "string") { - res.props["aria-controls"] = `${this.props.id.replace(/\s+/g, "-").toLowerCase()}-tab`; - } - return res; + injector.after( + TabBarModule.TabBar.Item.prototype, + "render", + function (this: TabBarItemType, _, res) { + if (typeof this.props.id === "string") { + res.props["aria-controls"] = `${this.props.id.replace(/\s+/g, "-").toLowerCase()}-tab`; + } + return res; + }, + ); +} + +interface ClientThemesBackgroundStore extends Store { + gradientPreset: { + id: number; + }; +} +type ThemeIDMap = Record & Record; + +function onNitroThemeChange(store: ClientThemesBackgroundStore, ThemeIDMap: ThemeIDMap): void { + if (!store.gradientPreset) { + html.removeAttribute("data-nitro-theme"); + } else { + const theme = ThemeIDMap[store.gradientPreset.id]; + html.setAttribute("data-nitro-theme", theme); + } +} + +// Adds the currently active nitro theme as a class on the html element +function nitroThemeClass(): void { + const ClientThemesBackgroundStore = getByStoreName( + "ClientThemesBackgroundStore", + ); + if (!ClientThemesBackgroundStore) { + throw new Error("Failed to find ClientThemesBackgroundStore!"); + } + const ThemeIDMap = getByProps("MINT_APPLE"); + if (!ThemeIDMap) { + throw new Error("Failed to find ThemeIDs module!"); + } + + // update theme attribute when theme changes + ClientThemesBackgroundStore.addChangeListener(() => { + onNitroThemeChange(ClientThemesBackgroundStore, ThemeIDMap); }); + onNitroThemeChange(ClientThemesBackgroundStore, ThemeIDMap); +} + +export function start(): void { + tabBarItemId(); + nitroThemeClass(); } export function stop(): void { From afd448928a425f190bcca9f4fafd14a877bcdb2e Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Wed, 20 Sep 2023 16:21:49 -0700 Subject: [PATCH 4/8] Message data attributes --- src/renderer/coremods/utilityClasses/index.ts | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts index 6d2402f0d..ae82fe1aa 100644 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -1,9 +1,11 @@ -import { Injector /*, Logger*/ } from "@replugged"; +import { Injector, Logger } from "@replugged"; +import { filters, getByProps, getByStoreName, waitForModule } from "src/renderer/modules/webpack"; +import { users } from "@common"; import type React from "react"; import type { Store } from "src/renderer/modules/common/flux"; -import { getByProps, getByStoreName } from "src/renderer/modules/webpack"; +import type { Message } from "discord-types/general"; -const injector = new Injector(); +const inject = new Injector(); //const logger = Logger.coremod("UtilityClasses"); const html = document.documentElement; @@ -23,7 +25,7 @@ function tabBarItemId(): void { throw new Error("Failed to find TabBar module!"); } - injector.after( + inject.after( TabBarModule.TabBar.Item.prototype, "render", function (this: TabBarItemType, _, res) { @@ -71,11 +73,57 @@ function nitroThemeClass(): void { onNitroThemeChange(ClientThemesBackgroundStore, ThemeIDMap); } -export function start(): void { +interface MessageComponent { + props: { id: string }; + type: { + type: (msg: { message: Message }) => React.ReactElement; + }; +} + +async function messageDataAttributes(): Promise { + const MessagesComponent = await waitForModule<{ + type: () => React.ReactElement; + }>(filters.bySource(".content.id)")); + + const uninjectMessagesComponent = inject.after(MessagesComponent, "type", (_, res) => { + uninjectMessagesComponent(); + + const MessageListComponent = res.props.children.type; + const uninjectMessageList = inject.after(MessageListComponent, "type", (_, res) => { + const messagesArray = res.props.children.props.children[1].props.children[1].props + .children[1] as MessageComponent[]; + const messageElement = messagesArray.find((e) => e.props.id !== undefined); + + // messageElement isn't found when first loading the message list. + if (messageElement) { + uninjectMessageList(); + // found a message component, inject into it + inject.after(messageElement.type, "type", ([{ message }], res) => { + const { props } = res.props.children.props.children; + props["data-is-author-self"] = message.author.id === users.getCurrentUser().id; + props["data-is-author-bot"] = message.author.bot; + if (message.author.bot) { + props["data-is-author-webhook"] = message.webhookId !== null; + } + props["data-author-id"] = message.author.id; + props["data-message-type"] = message.type; + if (message.blocked) props["data-is-blocked"] = "true"; + return res; + }); + } + + return res; + }); + return res; + }); +} + +export async function start(): Promise { tabBarItemId(); nitroThemeClass(); + await messageDataAttributes(); } export function stop(): void { - injector.uninjectAll(); + inject.uninjectAll(); } From 88d9f5c20a2d79b452b34ef397b9cdc82f771739 Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Wed, 20 Sep 2023 16:42:24 -0700 Subject: [PATCH 5/8] add replugged class to html --- src/renderer/coremods/utilityClasses/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts index ae82fe1aa..fc4b0f278 100644 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -118,10 +118,20 @@ async function messageDataAttributes(): Promise { }); } +function addHtmlClasses(): void { + if (!html.classList.contains("replugged")) { + html.classList.add("replugged"); + } +} + export async function start(): Promise { tabBarItemId(); nitroThemeClass(); await messageDataAttributes(); + + // generic stuff + const observer = new MutationObserver(addHtmlClasses); + observer.observe(html, { attributeFilter: ["class"] }); } export function stop(): void { From 46674b5342d48b8088348d44fdd8a7d4febef717 Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Sat, 14 Oct 2023 12:10:50 -0700 Subject: [PATCH 6/8] fix linting & some comments --- src/renderer/coremods/utilityClasses/index.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts index fc4b0f278..fc35d3186 100644 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -1,4 +1,4 @@ -import { Injector, Logger } from "@replugged"; +import { Injector } from "@replugged"; import { filters, getByProps, getByStoreName, waitForModule } from "src/renderer/modules/webpack"; import { users } from "@common"; import type React from "react"; @@ -6,7 +6,6 @@ import type { Store } from "src/renderer/modules/common/flux"; import type { Message } from "discord-types/general"; const inject = new Injector(); -//const logger = Logger.coremod("UtilityClasses"); const html = document.documentElement; interface TabBarItemProps { @@ -38,9 +37,7 @@ function tabBarItemId(): void { } interface ClientThemesBackgroundStore extends Store { - gradientPreset: { - id: number; - }; + gradientPreset: { id: number } | undefined; // undefined when no nitro theme is selected } type ThemeIDMap = Record & Record; @@ -74,7 +71,7 @@ function nitroThemeClass(): void { } interface MessageComponent { - props: { id: string }; + props: { id: string | undefined }; // may not be there for non-message components type: { type: (msg: { message: Message }) => React.ReactElement; }; @@ -85,6 +82,7 @@ async function messageDataAttributes(): Promise { type: () => React.ReactElement; }>(filters.bySource(".content.id)")); + // the Message component isn't exported, so it must be extracted like this const uninjectMessagesComponent = inject.after(MessagesComponent, "type", (_, res) => { uninjectMessagesComponent(); @@ -102,11 +100,12 @@ async function messageDataAttributes(): Promise { const { props } = res.props.children.props.children; props["data-is-author-self"] = message.author.id === users.getCurrentUser().id; props["data-is-author-bot"] = message.author.bot; + // webhooks are also considered bots if (message.author.bot) { - props["data-is-author-webhook"] = message.webhookId !== null; + props["data-is-author-webhook"] = Boolean(message.webhookId); } props["data-author-id"] = message.author.id; - props["data-message-type"] = message.type; + props["data-message-type"] = message.type; // raw enum value, seems consistent if (message.blocked) props["data-is-blocked"] = "true"; return res; }); From 9fa077605e7920fa3021d12f2b3ac6c10477cad2 Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Fri, 3 Nov 2023 01:14:33 -0700 Subject: [PATCH 7/8] implement requested changes & fix for discord update --- src/renderer/coremods/utilityClasses/index.ts | 74 ++++++++----------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts index fc35d3186..6c64bf3f6 100644 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -1,5 +1,5 @@ import { Injector } from "@replugged"; -import { filters, getByProps, getByStoreName, waitForModule } from "src/renderer/modules/webpack"; +import { getByProps, getByStoreName } from "src/renderer/modules/webpack"; import { users } from "@common"; import type React from "react"; import type { Store } from "src/renderer/modules/common/flux"; @@ -70,49 +70,28 @@ function nitroThemeClass(): void { onNitroThemeChange(ClientThemesBackgroundStore, ThemeIDMap); } -interface MessageComponent { - props: { id: string | undefined }; // may not be there for non-message components - type: { - type: (msg: { message: Message }) => React.ReactElement; - }; -} +function messageDataAttributes(): void { + const Message = getByProps<{ + default: { type: (msg: { message: Message }) => React.ReactElement }; + getElementFromMessage: unknown; + }>("getElementFromMessage"); -async function messageDataAttributes(): Promise { - const MessagesComponent = await waitForModule<{ - type: () => React.ReactElement; - }>(filters.bySource(".content.id)")); - - // the Message component isn't exported, so it must be extracted like this - const uninjectMessagesComponent = inject.after(MessagesComponent, "type", (_, res) => { - uninjectMessagesComponent(); - - const MessageListComponent = res.props.children.type; - const uninjectMessageList = inject.after(MessageListComponent, "type", (_, res) => { - const messagesArray = res.props.children.props.children[1].props.children[1].props - .children[1] as MessageComponent[]; - const messageElement = messagesArray.find((e) => e.props.id !== undefined); - - // messageElement isn't found when first loading the message list. - if (messageElement) { - uninjectMessageList(); - // found a message component, inject into it - inject.after(messageElement.type, "type", ([{ message }], res) => { - const { props } = res.props.children.props.children; - props["data-is-author-self"] = message.author.id === users.getCurrentUser().id; - props["data-is-author-bot"] = message.author.bot; - // webhooks are also considered bots - if (message.author.bot) { - props["data-is-author-webhook"] = Boolean(message.webhookId); - } - props["data-author-id"] = message.author.id; - props["data-message-type"] = message.type; // raw enum value, seems consistent - if (message.blocked) props["data-is-blocked"] = "true"; - return res; - }); - } + if (!Message) { + throw new Error("Failed to find Message module!"); + } - return res; - }); + inject.after(Message.default, "type", ([{ message }], res) => { + const props = res.props?.children?.props?.children?.props; + if (!props) return; + props["data-is-author-self"] = message.author.id === users.getCurrentUser().id; + props["data-is-author-bot"] = message.author.bot; + // webhooks are also considered bots + if (message.author.bot) { + props["data-is-author-webhook"] = Boolean(message.webhookId); + } + props["data-author-id"] = message.author.id; + props["data-message-type"] = message.type; // raw enum value, seems consistent enough to be useful + if (message.blocked) props["data-is-blocked"] = "true"; return res; }); } @@ -123,16 +102,21 @@ function addHtmlClasses(): void { } } -export async function start(): Promise { +let observer: MutationObserver; + +export function start(): void { tabBarItemId(); nitroThemeClass(); - await messageDataAttributes(); + messageDataAttributes(); // generic stuff - const observer = new MutationObserver(addHtmlClasses); + observer = new MutationObserver(addHtmlClasses); observer.observe(html, { attributeFilter: ["class"] }); } export function stop(): void { inject.uninjectAll(); + observer.disconnect(); + html.classList.remove("replugged"); + html.removeAttribute("data-nitro-theme"); } From cfb0d34db525ec5163e97be9fa8b19d541037552 Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Sat, 11 Jan 2025 22:00:48 -0800 Subject: [PATCH 8/8] update Message module search the coremod now loads asynchronously & will not log an error if the message module isn't found (it will just never load) --- src/renderer/coremods/utilityClasses/index.ts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) mode change 100644 => 100755 src/renderer/coremods/utilityClasses/index.ts diff --git a/src/renderer/coremods/utilityClasses/index.ts b/src/renderer/coremods/utilityClasses/index.ts old mode 100644 new mode 100755 index 6c64bf3f6..3e9114dc9 --- a/src/renderer/coremods/utilityClasses/index.ts +++ b/src/renderer/coremods/utilityClasses/index.ts @@ -1,5 +1,5 @@ import { Injector } from "@replugged"; -import { getByProps, getByStoreName } from "src/renderer/modules/webpack"; +import { filters, getByProps, getByStoreName, waitForModule } from "src/renderer/modules/webpack"; import { users } from "@common"; import type React from "react"; import type { Store } from "src/renderer/modules/common/flux"; @@ -70,19 +70,14 @@ function nitroThemeClass(): void { onNitroThemeChange(ClientThemesBackgroundStore, ThemeIDMap); } -function messageDataAttributes(): void { - const Message = getByProps<{ - default: { type: (msg: { message: Message }) => React.ReactElement }; - getElementFromMessage: unknown; - }>("getElementFromMessage"); +async function messageDataAttributes(): Promise { + const Message = await waitForModule<{ + ZP: { type: (msg: { message: Message }) => React.ReactElement }; + }>(filters.bySource(/channel:{id:\w},compact:\w=!1,/i)); - if (!Message) { - throw new Error("Failed to find Message module!"); - } - - inject.after(Message.default, "type", ([{ message }], res) => { - const props = res.props?.children?.props?.children?.props; - if (!props) return; + inject.after(Message.ZP, "type", ([{ message }], res) => { + const props = res.props?.children?.props?.children?.[1]?.props; + if (!props) return res; props["data-is-author-self"] = message.author.id === users.getCurrentUser().id; props["data-is-author-bot"] = message.author.bot; // webhooks are also considered bots @@ -104,10 +99,10 @@ function addHtmlClasses(): void { let observer: MutationObserver; -export function start(): void { +export async function start(): Promise { tabBarItemId(); nitroThemeClass(); - messageDataAttributes(); + await messageDataAttributes(); // generic stuff observer = new MutationObserver(addHtmlClasses);