From 445edc7586d4241dfe9cfc9e7251f9586f5d1f77 Mon Sep 17 00:00:00 2001 From: sadan <117494111+sadan4@users.noreply.github.com> Date: Sat, 3 Oct 2026 09:41:29 -0400 Subject: [PATCH] fix RoleColorEverywhere, StickerPaste, BetterRoleContext (#4641) Co-authored-by: V --- packages/discord-types/src/utils.d.ts | 43 +++++++++++++++++++++++ src/plugins/betterRoleContext/index.tsx | 12 +++++-- src/plugins/roleColorEverywhere/index.tsx | 32 +++++++---------- src/plugins/stickerPaste/index.ts | 6 ++-- src/utils/discord.tsx | 12 +++---- src/webpack/common/utils.ts | 7 ++-- 6 files changed, 78 insertions(+), 34 deletions(-) diff --git a/packages/discord-types/src/utils.d.ts b/packages/discord-types/src/utils.d.ts index b0a6ee343..b6eecc342 100644 --- a/packages/discord-types/src/utils.d.ts +++ b/packages/discord-types/src/utils.d.ts @@ -415,3 +415,46 @@ export interface EmojiUtils { triggerFullscreenAnimation(emoji: any, node: HTMLElement): void; applyPlatformToThemedEmojiColorPalette(colors: any): any; } + +export interface OpenUserSettingsOptions { + /** + * a *sub*section of settings to navigate to + * + * to navigate to a top-level section, use the navigationTarget parameter of {@link SettingsRouter.openUserSettings} instead + * + * discord has an enum with all the possible values (go dig in the {@link SettingsRouter.openUserSettings} source) + */ + path?: string; + /** + * modal stacking behavior, defaults to "replaceAll" + */ + stackingBehavior?: "replaceAll" | "stack" | "replace"; + // ... misc analytics options +} + +export interface SettingsRouter { + openUserSettings(navigationTarget?: string, opts?: OpenUserSettingsOptions, onOpen?: () => void): Promise; +} + +export interface OpenUserProfileModalOptions { + userId: string; + guildId?: string | null; + originGuildId?: string | null; + channelId?: string | null; + messageId?: string | null; + roleId?: string | null; + sessionId?: unknown | null; + joinRequestId?: unknown; + tabSection?: unknown; + scrollTarget?: unknown; + hideRestrictedProfile?: boolean; + sourceAnalyticsLocations?: string[]; + appContext?: unknown; + /** + * defaults to null + */ + customStatusPrompt?: unknown; + onModalOpen?: unknown; +} + +export type OpenUserProfileModal = (opts: OpenUserProfileModalOptions) => Promise; diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index 9b5653aef..561b8a332 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -206,14 +206,20 @@ export default definePlugin({ settings, openRoleContextMenu, patches: [ - // Conflicts with RoleColorEverywhere which changes the code at the end of our match. (and also uses same find & similar match) - // However, BetterRoleContext applies first (alphabetic order), so it's not an issue { find: 'tutorialId:"whos-online', replacement: { - match: /(?<=#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.{0,200}?"aria-hidden":!0,)children:.{0,200}?(?:—|\\u2014) ",\i\]\}\)\]/, + match: /(?<=#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.{0,400}?)children:(?=.{0,20}?(?:—|\\u2014) ",\i\])/, replace: "onContextMenu:e=>$self.openRoleContextMenu(e,arguments[0]),$&" } + }, + // member list role headers + { + find: "?null:new Intl.NumberFormat", + replacement: { + match: /(?<=#{intl::CHANNEL_MEMBERS_A11Y_LABEL},\{title:\i,count:\i\}\)\}\),\(0,\i\.jsxs\)\("div",\{)/, + replace: "onContextMenu:e=>$self.openRoleContextMenu(e,arguments[0])," + } } ], diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index d50596398..7ff6cf1fb 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -17,7 +17,6 @@ */ import { definePluginSettings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { makeRange, OptionType } from "@utils/types"; @@ -102,23 +101,24 @@ export default definePlugin({ ], predicate: () => settings.store.chatMentions }, - // Member List Role Headers + // Member List Role Headers (in threads) { find: 'tutorialId:"whos-online', replacement: [ { - match: /(#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.+}\):null,).{0,100}?(?:—|\\u2014) ",\i\]\}\)\]/, - replace: "$1$self.RoleGroupColor(arguments[0])]" + match: /(?<=#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.{0,400}?)children:(?=.{0,20}?(?:—|\\u2014) ",\i\])/, + replace: "style:{color:$self.getRoleColor(arguments[0])},$&" }, ], predicate: () => settings.store.memberList }, + // Member List Role Headers { - find: "#{intl::THREAD_BROWSER_PRIVATE}", + find: "?null:new Intl.NumberFormat", replacement: [ { - match: /children:\[\i," (?:—|\\u2014) ",\i\]/, - replace: "children:[$self.RoleGroupColor(arguments[0])]" + match: /(?<=#{intl::CHANNEL_MEMBERS_A11Y_LABEL},\{title:\i,count:\i\}\)\}\),\(0,\i\.jsxs\)\("div",\{)/, + replace: "style:{color:$self.getRoleColor(arguments[0])}," }, ], predicate: () => settings.store.memberList @@ -213,17 +213,9 @@ export default definePlugin({ return null; }, - RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => { - const role = GuildRoleStore.getRole(guildId, id); - - return ( - - {title ?? label} — {count} - - ); - }, { noop: true }) + getRoleColor(props: any) { + try { + return GuildRoleStore.getRole(props?.guildId, props?.id)?.colorString; + } catch (e) { } + } }); diff --git a/src/plugins/stickerPaste/index.ts b/src/plugins/stickerPaste/index.ts index 0dd0bd2b3..4d216d6ba 100644 --- a/src/plugins/stickerPaste/index.ts +++ b/src/plugins/stickerPaste/index.ts @@ -15,9 +15,11 @@ export default definePlugin({ patches: [ { - find: ".stickers,previewSticker:", + // This function is a util func from another module + // but it only has once use here so it's inlined + find: '.STICKER_PICKER:return""', replacement: { - match: /if\(\i\.\i\.getUploadCount/, + match: /(?<=\){)if\(\i\.\i\.getUploadCount/, replace: "return true;$&", } } diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 47aea0cc4..6821839fe 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -18,7 +18,7 @@ import type { MessageObject } from "@api/MessageEvents"; import type { Channel, CloudUpload, Guild, GuildFeatures, MediaModalItem, MediaModalProps, Message, User } from "@vencord/discord-types"; -import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, openMediaModal, RestAPI, SelectedChannelStore, SelectedGuildStore, showToast, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; +import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, openMediaModal, openUserProfileModal, RestAPI, SelectedChannelStore, SelectedGuildStore, showToast, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Except } from "type-fest"; import { copyToClipboard } from "./clipboard"; @@ -178,14 +178,14 @@ export async function openUserProfile(id: string) { if (!user) throw new Error("No such user: " + id); const guildId = SelectedGuildStore.getGuildId(); - UserProfileActions.openUserProfileModal({ + openUserProfileModal({ userId: id, guildId, channelId: SelectedChannelStore.getChannelId(), - analyticsLocation: { - page: guildId ? "Guild Channel" : "DM Channel", - section: "Profile Popout" - } + sourceAnalyticsLocations: [ + "username", + "user profile popout", + ] }); } diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 47d23bf8a..b6749b521 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -126,8 +126,9 @@ export const ChannelRouter: t.ChannelRouter = mapMangledModuleLazy('"Thread must transitionToThread: filters.byCode('"Thread must have a parent ID."') }); -export let SettingsRouter: any; -waitFor(["openUserSettings", "USER_SETTINGS_MODAL_KEY"], m => SettingsRouter = m); +export const SettingsRouter: t.SettingsRouter = mapMangledModuleLazy('type:"USER_SETTINGS_MODAL_OPEN"', { + openUserSettings: filters.byCode('type:"USER_SETTINGS_MODAL_OPEN"') +}); export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); @@ -141,7 +142,7 @@ export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); -export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); +export const openUserProfileModal: t.OpenUserProfileModal = findByCodeLazy('type:"USER_PROFILE_MODAL_OPEN"'); export const InviteActions = findByPropsLazy("resolveInvite"); export const ChannelActionCreators = findByPropsLazy("openPrivateChannel");