fix RoleColorEverywhere, StickerPaste, BetterRoleContext (#4641)

Co-authored-by: V <vendicated@riseup.net>
This commit is contained in:
sadan 2026-10-03 09:41:29 -04:00 • committed by GitHub
parent b52ed365cf
commit 445edc7586
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 78 additions and 34 deletions

View file

@ -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<void>;
}
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<void>;

View file

@ -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]),"
}
}
],

View file

@ -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 (
<span style={{
color: role?.colorString,
fontWeight: "unset",
letterSpacing: ".05em"
}}>
{title ?? label} &mdash; {count}
</span>
);
}, { noop: true })
getRoleColor(props: any) {
try {
return GuildRoleStore.getRole(props?.guildId, props?.id)?.colorString;
} catch (e) { }
}
});

View file

@ -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;$&",
}
}

View file

@ -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",
]
});
}

View file

@ -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");