diff --git a/package.json b/package.json
index 22b99f8bc..65d97f2e0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
- "version": "1.9.7",
+ "version": "1.9.8",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {
diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts
index d3484bd9a..73a89504f 100644
--- a/src/debug/loadLazyChunks.ts
+++ b/src/debug/loadLazyChunks.ts
@@ -134,7 +134,7 @@ export async function loadLazyChunks() {
const allChunks = [] as number[];
// Matches "id" or id:
- for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)")|(?:([\deE]+?):)/g)) {
+ for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
const id = currentMatch[1] ?? currentMatch[2];
if (id == null) continue;
diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx
index 89a992ac3..cf00a0e29 100644
--- a/src/plugins/_api/badges/index.tsx
+++ b/src/plugins/_api/badges/index.tsx
@@ -62,34 +62,6 @@ export default definePlugin({
authors: [Devs.Megu, Devs.Ven, Devs.TheSun],
required: true,
patches: [
- /* Patch the badge list component on user profiles */
- {
- find: 'id:"premium",',
- replacement: [
- {
- match: /&&(\i)\.push\(\{id:"premium".+?\}\);/,
- replace: "$&$1.unshift(...$self.getBadges(arguments[0]));",
- },
- {
- // alt: "", aria-hidden: false, src: originalSrc
- match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/,
- // ...badge.props, ..., src: badge.image ?? ...
- replace: "...$1.props,$& $1.image??"
- },
- // replace their component with ours if applicable
- {
- match: /(?<=text:(\i)\.description,spacing:12,.{0,50})children:/,
- replace: "children:$1.component ? () => $self.renderBadgeComponent($1) :"
- },
- // conditionally override their onClick with badge.onClick if it exists
- {
- match: /href:(\i)\.link/,
- replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&"
- }
- ]
- },
-
- /* new profiles */
{
find: ".FULL_SIZE]:26",
replacement: {
diff --git a/src/plugins/automodContext/README.md b/src/plugins/automodContext/README.md
deleted file mode 100644
index f70d71d90..000000000
--- a/src/plugins/automodContext/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# AutomodContext
-
-Allows you to jump to the messages surrounding an automod hit
-
-![Visualization](https://github.com/Vendicated/Vencord/assets/61953774/d13740c8-2062-4553-b975-82fd3d6cc08b)
diff --git a/src/plugins/automodContext/index.tsx b/src/plugins/automodContext/index.tsx
deleted file mode 100644
index 5425c5526..000000000
--- a/src/plugins/automodContext/index.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Vencord, a Discord client mod
- * Copyright (c) 2024 Vendicated and contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-import ErrorBoundary from "@components/ErrorBoundary";
-import { Devs } from "@utils/constants";
-import definePlugin from "@utils/types";
-import { findByPropsLazy } from "@webpack";
-import { Button, ChannelStore, Text } from "@webpack/common";
-
-const { selectChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel");
-
-function jumpToMessage(channelId: string, messageId: string) {
- const guildId = ChannelStore.getChannel(channelId)?.guild_id;
-
- selectChannel({
- guildId,
- channelId,
- messageId,
- jumpType: "INSTANT"
- });
-}
-
-function findChannelId(message: any): string | null {
- const { embeds: [embed] } = message;
- const channelField = embed.fields.find(({ rawName }) => rawName === "channel_id");
-
- if (!channelField) {
- return null;
- }
-
- return channelField.rawValue;
-}
-
-export default definePlugin({
- name: "AutomodContext",
- description: "Allows you to jump to the messages surrounding an automod hit.",
- authors: [Devs.JohnyTheCarrot],
-
- patches: [
- {
- find: ".Messages.GUILD_AUTOMOD_REPORT_ISSUES",
- replacement: {
- match: /\.Messages\.ACTIONS.+?}\)(?=,(\(0.{0,40}\.dot.*?}\)),)/,
- replace: (m, dot) => `${m},${dot},$self.renderJumpButton({message:arguments[0].message})`
- }
- }
- ],
-
- renderJumpButton: ErrorBoundary.wrap(({ message }: { message: any; }) => {
- const channelId = findChannelId(message);
-
- if (!channelId) {
- return null;
- }
-
- return (
-
- );
- }, { noop: true })
-});
diff --git a/src/plugins/betterNotes/index.tsx b/src/plugins/betterNotes/index.tsx
index b97076bf4..63fcf6477 100644
--- a/src/plugins/betterNotes/index.tsx
+++ b/src/plugins/betterNotes/index.tsx
@@ -17,13 +17,9 @@
*/
import { definePluginSettings, Settings } from "@api/Settings";
-import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types";
-import { findByPropsLazy } from "@webpack";
-
-const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
const settings = definePluginSettings({
hide: {
@@ -72,23 +68,9 @@ export default definePlugin({
match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!$self.noSpellCheck,"
}
- },
- {
- find: ".popularApplicationCommandIds,",
- replacement: {
- match: /lastSection:(!?\i)}\),/,
- replace: "$&$self.patchPadding({lastSection:$1}),"
- }
}
],
- patchPadding: ErrorBoundary.wrap(({ lastSection }) => {
- if (!lastSection) return null;
- return (
-
- );
- }),
-
get noSpellCheck() {
return settings.store.noSpellCheck;
}
diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx
index ab240837b..708c961aa 100644
--- a/src/plugins/fakeProfileThemes/index.tsx
+++ b/src/plugins/fakeProfileThemes/index.tsx
@@ -121,7 +121,7 @@ export default definePlugin({
{
find: "UserProfileStore",
replacement: {
- match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/,
+ match: /(?<=getUserProfile\(\i\){return )(.+?)(?=})/,
replace: "$self.colorDecodeHook($1)"
}
},
diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx
index 717bd754c..0399a2f72 100644
--- a/src/plugins/friendsSince/index.tsx
+++ b/src/plugins/friendsSince/index.tsx
@@ -16,7 +16,6 @@ const containerWrapper = findByPropsLazy("memberSinceWrapper");
const container = findByPropsLazy("memberSince");
const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"');
const locale = findByPropsLazy("getLocale");
-const lastSection = findByPropsLazy("lastSection");
const section = findLazy((m: any) => m.section !== void 0 && m.heading !== void 0 && Object.values(m).length === 2);
export default definePlugin({
@@ -24,31 +23,7 @@ export default definePlugin({
description: "Shows when you became friends with someone in the user popout",
authors: [Devs.Elvyra, Devs.Antti],
patches: [
- // User popup - old layout
- {
- find: ".USER_PROFILE}};return",
- replacement: {
- match: /,{userId:(\i.id).{0,30}}\)/,
- replace: "$&,$self.friendsSinceOld({ userId: $1 })"
- }
- },
- // DM User Sidebar - old layout
- {
- find: ".PROFILE_PANEL,",
- replacement: {
- match: /,{userId:([^,]+?)}\)/,
- replace: "$&,$self.friendsSinceOld({ userId: $1 })"
- }
- },
- // User Profile Modal - old layout
- {
- find: ".userInfoSectionHeader,",
- replacement: {
- match: /(\.Messages\.USER_PROFILE_MEMBER_SINCE.+?userId:(.+?),textClassName:)(\i\.userInfoText)}\)/,
- replace: (_, rest, userId, textClassName) => `${rest}!$self.getFriendSince(${userId}) ? ${textClassName} : void 0 }), $self.friendsSinceOld({ userId: ${userId}, textClassName: ${textClassName} })`
- }
- },
- // DM User Sidebar - new layout
+ // DM User Sidebar
{
find: ".PANEL}),nicknameIcons",
replacement: {
@@ -56,7 +31,7 @@ export default definePlugin({
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:true})"
}
},
- // User Profile Modal - new layout
+ // User Profile Modal
{
find: "action:\"PRESS_APP_CONNECTION\"",
replacement: {
@@ -77,39 +52,6 @@ export default definePlugin({
}
},
- friendsSinceOld: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
- if (!RelationshipStore.isFriend(userId)) return null;
-
- const friendsSince = RelationshipStore.getSince(userId);
- if (!friendsSince) return null;
-
- return (
-
-
- Friends Since
-
-
-
- {!!getCurrentChannel()?.guild_id && (
-
- )}
-
- {getCreatedAtDate(friendsSince, locale.getLocale())}
-
-
-
- );
- }, { noop: true }),
-
friendsSinceNew: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
if (!RelationshipStore.isFriend(userId)) return null;
diff --git a/src/plugins/memberCount/MemberCount.tsx b/src/plugins/memberCount/MemberCount.tsx
index 3231d01ce..084e7ecc4 100644
--- a/src/plugins/memberCount/MemberCount.tsx
+++ b/src/plugins/memberCount/MemberCount.tsx
@@ -5,9 +5,10 @@
*/
import { getCurrentChannel } from "@utils/discord";
+import { isObjectEmpty } from "@utils/misc";
import { SelectedChannelStore, Tooltip, useEffect, useStateFromStores } from "@webpack/common";
-import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat } from ".";
+import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat, ThreadMemberListStore } from ".";
import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) {
@@ -30,10 +31,19 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
() => ChannelMemberStore.getProps(guildId, currentChannel?.id)
);
+ const threadGroups = useStateFromStores(
+ [ThreadMemberListStore],
+ () => ThreadMemberListStore.getMemberListSections(currentChannel.id)
+ );
+
if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) {
onlineCount = groups.reduce((total, curr) => total + (curr.id === "offline" ? 0 : curr.count), 0);
}
+ if (!isTooltip && threadGroups && !isObjectEmpty(threadGroups)) {
+ onlineCount = Object.values(threadGroups).reduce((total, curr) => total + (curr.sectionId === "offline" ? 0 : curr.userIds.length), 0);
+ }
+
useEffect(() => {
OnlineMemberCountStore.ensureCount(guildId);
}, [guildId]);
diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx
index 28ecb9db7..7e591357d 100644
--- a/src/plugins/memberCount/index.tsx
+++ b/src/plugins/memberCount/index.tsx
@@ -32,6 +32,10 @@ export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as F
export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
};
+export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as FluxStore & {
+ getMemberListSections(channelId: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
+};
+
const settings = definePluginSettings({
toolTip: {
diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx
index 45538fb66..4802f04a1 100644
--- a/src/plugins/moreUserTags/index.tsx
+++ b/src/plugins/moreUserTags/index.tsx
@@ -247,9 +247,9 @@ export default definePlugin({
}
},
{
- find: 'copyMetaData:"User Tag"',
+ find: ".Messages.USER_PROFILE_PRONOUNS",
replacement: {
- match: /(?=,botClass:)/,
+ match: /(?=,hideBotTag:!0)/,
replace: ",moreTags_channelId:arguments[0].moreTags_channelId"
}
},
diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx
index 7c71e1edb..a1e73cabf 100644
--- a/src/plugins/mutualGroupDMs/index.tsx
+++ b/src/plugins/mutualGroupDMs/index.tsx
@@ -58,20 +58,6 @@ export default definePlugin({
authors: [Devs.amia],
patches: [
- {
- find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Note: the module is lazy-loaded
- replacement: {
- match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
- replace: '$self.isBotOrSelf(arguments[0].user)?null:$1"MUTUAL_GDMS",children:$self.getMutualGDMCountText(arguments[0].user)}),'
- }
- },
- {
- find: ".USER_INFO_CONNECTIONS:case",
- replacement: {
- match: /(?<={user:(\i),onClose:(\i)}\);)(?=case \i\.\i\.MUTUAL_FRIENDS)/,
- replace: "case \"MUTUAL_GDMS\":return $self.renderMutualGDMs({user: $1, onClose: $2});"
- }
- },
{
find: ".MUTUAL_FRIENDS?(",
replacement: [
@@ -87,9 +73,6 @@ export default definePlugin({
}
],
- isBotOrSelf,
- getMutualGDMCountText,
-
pushSection(sections: any[], user: User) {
if (isBotOrSelf(user) || sections[IS_PATCHED]) return;
diff --git a/src/plugins/noMaskedUrlPaste/README.md b/src/plugins/noMaskedUrlPaste/README.md
new file mode 100644
index 000000000..36707ac93
--- /dev/null
+++ b/src/plugins/noMaskedUrlPaste/README.md
@@ -0,0 +1,3 @@
+# NoMaskedUrlPaste
+
+Pasting a link while you have text selected will NOT paste your link as a masked link.
diff --git a/src/plugins/noMaskedUrlPaste/index.ts b/src/plugins/noMaskedUrlPaste/index.ts
new file mode 100644
index 000000000..64f522cae
--- /dev/null
+++ b/src/plugins/noMaskedUrlPaste/index.ts
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2023 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Devs } from "@utils/constants.js";
+import definePlugin from "@utils/types";
+
+export default definePlugin({
+ name: "NoMaskedUrlPaste",
+ authors: [Devs.CatNoir],
+ description: "Pasting a link while having text selected will not paste as masked URL",
+ patches: [
+ {
+ find: ".selection,preventEmojiSurrogates:",
+ replacement: {
+ match: /if\(null!=\i.selection&&\i.\i.isExpanded\(\i.selection\)\)/,
+ replace: "if(false)"
+ }
+ }
+ ],
+});
diff --git a/src/plugins/noProfileThemes/index.ts b/src/plugins/noProfileThemes/index.ts
index e2b9327e8..7c440df80 100644
--- a/src/plugins/noProfileThemes/index.ts
+++ b/src/plugins/noProfileThemes/index.ts
@@ -18,36 +18,21 @@
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
+import { UserStore } from "@webpack/common";
export default definePlugin({
name: "NoProfileThemes",
- description: "Completely removes Nitro profile themes",
+ description: "Completely removes Nitro profile themes from everyone but yourself",
authors: [Devs.TheKodeToad],
patches: [
- {
- find: ".NITRO_BANNER,",
- replacement: {
- // = isPremiumAtLeast(user.premiumType, TIER_2)
- match: /=(?=\i\.\i\.isPremiumAtLeast\(null==(\i))/,
- // = user.banner && isPremiumAtLeast(user.premiumType, TIER_2)
- replace: "=(arguments[0]?.bannerSrc||$1?.banner)&&"
- }
- },
- {
- find: ".avatarPositionPremiumNoBanner,default:",
- replacement: {
- // premiumUserWithoutBanner: foo().avatarPositionPremiumNoBanner, default: foo().avatarPositionNormal
- match: /\.avatarPositionPremiumNoBanner(?=,default:\i\.(\i))/,
- // premiumUserWithoutBanner: foo().avatarPositionNormal...
- replace: ".$1"
- }
- },
{
find: "hasThemeColors(){",
replacement: {
match: /get canUsePremiumProfileCustomization\(\){return /,
- replace: "$&false &&"
+ replace: "$&$self.isCurrentUser(this.userId)&&"
}
- }
- ]
+ },
+ ],
+
+ isCurrentUser: (userId: string) => userId === UserStore.getCurrentUser()?.id,
});
diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx
index 49770bbe1..f53f93b6d 100644
--- a/src/plugins/permissionsViewer/components/UserPermissions.tsx
+++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx
@@ -35,15 +35,17 @@ interface UserPermission {
type UserPermissions = Array;
-const Classes = proxyLazyWebpack(() =>
- Object.assign({}, ...findBulk(
- filters.byProps("roles", "rolePill", "rolePillBorder"),
- filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"),
- filters.byProps("roleNameOverflow", "root", "roleName", "roleRemoveButton")
- ))
-) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>;
+const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() => {
+ const [RoleRootClasses, RoleClasses, RoleBorderClasses] = findBulk(
+ filters.byProps("root", "showMoreButton", "collapseButton"),
+ filters.byProps("role", "roleCircle", "roleName"),
+ filters.byProps("roleCircle", "dot", "dotBorderColor")
+ ) as Record[];
-function UserPermissionsComponent({ guild, guildMember, showBorder, forceOpen = false }: { guild: Guild; guildMember: GuildMember; showBorder: boolean; forceOpen?: boolean; }) {
+ return { RoleRootClasses, RoleClasses, RoleBorderClasses };
+});
+
+function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { guild: Guild; guildMember: GuildMember; forceOpen?: boolean; }) {
const stns = settings.use(["permissionsSortOrder"]);
const [rolePermissions, userPermissions] = useMemo(() => {
@@ -91,8 +93,6 @@ function UserPermissionsComponent({ guild, guildMember, showBorder, forceOpen =
return [rolePermissions, userPermissions];
}, [stns.permissionsSortOrder]);
- const { root, role, roleRemoveButton, roleNameOverflow, roles, rolePill, rolePillBorder, roleCircle, roleName } = Classes;
-
return (
)
]}>
{userPermissions.length > 0 && (
-
+
{userPermissions.map(({ permission, roleColor }) => (
-
-
+
+
-
+
{permission}
diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx
index 6a503f2da..5039d04f8 100644
--- a/src/plugins/permissionsViewer/index.tsx
+++ b/src/plugins/permissionsViewer/index.tsx
@@ -169,13 +169,6 @@ export default definePlugin({
settings,
patches: [
- {
- find: ".popularApplicationCommandIds,",
- replacement: {
- match: /showBorder:(.{0,60})}\),(?<=guild:(\i),guildMember:(\i),.+?)/,
- replace: (m, showBoder, guild, guildMember) => `${m}$self.UserPermissions(${guild},${guildMember},${showBoder}),`
- }
- },
{
find: ".VIEW_ALL_ROLES,",
replacement: {
@@ -185,16 +178,13 @@ export default definePlugin({
}
],
- UserPermissions: (guild: Guild, guildMember: GuildMember | undefined, showBorder: boolean) =>
- !!guildMember && ,
-
ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => (
(
)}
>
diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts
index a5891d2e8..b0c5bfe6f 100644
--- a/src/plugins/pronoundb/index.ts
+++ b/src/plugins/pronoundb/index.ts
@@ -26,11 +26,6 @@ import { CompactPronounsChatComponentWrapper, PronounsChatComponentWrapper } fro
import { useProfilePronouns } from "./pronoundbUtils";
import { settings } from "./settings";
-const PRONOUN_TOOLTIP_PATCH = {
- match: /text:(.{0,10}.Messages\.USER_PROFILE_PRONOUNS)(?=,)/,
- replace: '$& + (typeof vcPronounSource !== "undefined" ? ` (${vcPronounSource})` : "")'
-};
-
export default definePlugin({
name: "PronounDB",
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven, Devs.Elvyra],
@@ -51,26 +46,23 @@ export default definePlugin({
}
]
},
- // Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
+
{
- find: ".pronouns,children",
+ find: ".Messages.USER_PROFILE_PRONOUNS",
+ group: true,
replacement: [
{
- match: /{user:(\i),[^}]*,pronouns:(\i),[^}]*}=\i.*?;(?=return)/,
- replace: "$&let vcPronounSource;[$2,vcPronounSource]=$self.useProfilePronouns($1.id);"
+ match: /\.PANEL},/,
+ replace: "$&[vcPronoun,vcPronounSource,vcHasPendingPronouns]=$self.useProfilePronouns(arguments[0].user?.id),"
},
- PRONOUN_TOOLTIP_PATCH
- ]
- },
- // Patch the profile modal username header to use our pronoun hook instead of Discord's pronouns
- {
- find: ".nameTagSmall)",
- replacement: [
{
- match: /\.getName\(\i\);(?<=displayProfile.{0,200})/,
- replace: "$&const [vcPronounce,vcPronounSource]=$self.useProfilePronouns(arguments[0].user.id,true);if(arguments[0].displayProfile&&vcPronounce)arguments[0].displayProfile.pronouns=vcPronounce;"
+ match: /text:\i\.\i.Messages.USER_PROFILE_PRONOUNS/,
+ replace: '$&+vcHasPendingPronouns?"":` (${vcPronounSource})`'
},
- PRONOUN_TOOLTIP_PATCH
+ {
+ match: /(\.pronounsText.+?children:)(\i)/,
+ replace: "$1vcHasPendingPronouns?$2:vcPronoun"
+ }
]
}
],
diff --git a/src/plugins/pronoundb/pronoundbUtils.ts b/src/plugins/pronoundb/pronoundbUtils.ts
index d4fdb09d3..991e9031a 100644
--- a/src/plugins/pronoundb/pronoundbUtils.ts
+++ b/src/plugins/pronoundb/pronoundbUtils.ts
@@ -21,13 +21,16 @@ import { debounce } from "@shared/debounce";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { getCurrentChannel } from "@utils/discord";
import { useAwaiter } from "@utils/react";
+import { findStoreLazy } from "@webpack";
import { UserProfileStore, UserStore } from "@webpack/common";
import { settings } from "./settings";
import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types";
-type PronounsWithSource = [string | null, string];
-const EmptyPronouns: PronounsWithSource = [null, ""];
+const UserSettingsAccountStore = findStoreLazy("UserSettingsAccountStore");
+
+type PronounsWithSource = [pronouns: string | null, source: string, hasPendingPronouns: boolean];
+const EmptyPronouns: PronounsWithSource = [null, "", false];
export const enum PronounsFormat {
Lowercase = "LOWERCASE",
@@ -75,13 +78,15 @@ export function useFormattedPronouns(id: string, useGlobalProfile: boolean = fal
onError: e => console.error("Fetching pronouns failed: ", e)
});
+ const hasPendingPronouns = UserSettingsAccountStore.getPendingPronouns() != null;
+
if (settings.store.pronounSource === PronounSource.PreferDiscord && discordPronouns)
- return [discordPronouns, "Discord"];
+ return [discordPronouns, "Discord", hasPendingPronouns];
if (result && result !== PronounMapping.unspecified)
- return [result, "PronounDB"];
+ return [result, "PronounDB", hasPendingPronouns];
- return [discordPronouns, "Discord"];
+ return [discordPronouns, "Discord", hasPendingPronouns];
}
export function useProfilePronouns(id: string, useGlobalProfile: boolean = false): PronounsWithSource {
@@ -147,7 +152,7 @@ async function bulkFetchPronouns(ids: string[]): Promise {
}
}
-export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[] }): string {
+export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[]; }): string {
if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified;
// PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}.
const pronouns = pronounSet.en;
diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx
index 456e15a57..caf9bacba 100644
--- a/src/plugins/reviewDB/index.tsx
+++ b/src/plugins/reviewDB/index.tsx
@@ -20,18 +20,16 @@ import "./style.css";
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import ErrorBoundary from "@components/ErrorBoundary";
-import { ExpandableHeader } from "@components/ExpandableHeader";
import { NotesIcon, OpenExternalIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { classes } from "@utils/misc";
import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack";
-import { Alerts, Button, Menu, Parser, TooltipContainer, useState } from "@webpack/common";
+import { Alerts, Button, Menu, Parser, TooltipContainer } from "@webpack/common";
import { Guild, User } from "discord-types/general";
import { Auth, initAuth, updateAuth } from "./auth";
import { openReviewsModal } from "./components/ReviewModal";
-import ReviewsView from "./components/ReviewsView";
import { NotificationType } from "./entities";
import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
import { settings } from "./settings";
@@ -79,17 +77,24 @@ export default definePlugin({
patches: [
{
- find: "showBorder:null",
+ find: ".BITE_SIZE,user:",
replacement: {
- match: /user:(\i),setNote:\i,canDM.+?\}\)/,
- replace: "$&,$self.getReviewsComponent($1)"
+ match: /{profileType:\i\.\i\.BITE_SIZE,children:\[/,
+ replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user}),"
}
},
{
- find: ".BITE_SIZE,user:",
+ find: ".FULL_SIZE,user:",
replacement: {
- match: /(?<=\.BITE_SIZE,children:\[)\(0,\i\.jsx\)\(\i\.\i,\{user:(\i),/,
- replace: "$self.BiteSizeReviewsButton({user:$1}),$&"
+ match: /{profileType:\i\.\i\.FULL_SIZE,children:\[/,
+ replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user}),"
+ }
+ },
+ {
+ find: ".PANEL,isInteractionSource:",
+ replacement: {
+ match: /{profileType:\i\.\i\.PANEL,children:\[/,
+ replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user}),"
}
}
],
@@ -148,31 +153,6 @@ export default definePlugin({
}, 4000);
},
- getReviewsComponent: ErrorBoundary.wrap((user: User) => {
- const [reviewCount, setReviewCount] = useState();
-
- return (
- openReviewsModal(user.id, user.username)}
- moreTooltipText={
- reviewCount && reviewCount > 50
- ? `View all ${reviewCount} reviews`
- : "Open Review Modal"
- }
- onDropDownClick={state => settings.store.reviewsDropdownState = !state}
- defaultState={settings.store.reviewsDropdownState}
- >
- setReviewCount(r.reviewCount)}
- showInput
- />
-
- );
- }, { message: "Failed to render Reviews" }),
-
BiteSizeReviewsButton: ErrorBoundary.wrap(({ user }: { user: User; }) => {
return (
diff --git a/src/plugins/showAllRoles/README.md b/src/plugins/showAllRoles/README.md
deleted file mode 100644
index d5d99c794..000000000
--- a/src/plugins/showAllRoles/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# ShowAllRoles
-
-Display all roles on the new profiles instead of limiting them to the default two rows.
-
-![image](https://github.com/Vendicated/Vencord/assets/71079641/3f021f03-c6f9-4fe5-83ac-a1891b5e4b37)
-
diff --git a/src/plugins/showAllRoles/index.ts b/src/plugins/showAllRoles/index.ts
deleted file mode 100644
index 97f0181fa..000000000
--- a/src/plugins/showAllRoles/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Vencord, a Discord client mod
- * Copyright (c) 2024 Vendicated and contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-import { Devs } from "@utils/constants";
-import definePlugin from "@utils/types";
-
-export default definePlugin({
- name: "ShowAllRoles",
- description: "Show all roles in new profiles.",
- authors: [Devs.Luna],
- patches: [
- {
- find: ".Messages.VIEW_ALL_ROLES",
- replacement: {
- match: /(\i)\.slice\(0,\i\)/,
- replace: "$1"
- }
- }
- ]
-});
diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx
index f9f3d9eb7..a946e0433 100644
--- a/src/plugins/showConnections/index.tsx
+++ b/src/plugins/showConnections/index.tsx
@@ -25,15 +25,12 @@ import { CopyIcon, LinkIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
-import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
-import { Text, Tooltip, UserProfileStore } from "@webpack/common";
+import { findByCodeLazy, findByPropsLazy } from "@webpack";
+import { Tooltip, UserProfileStore } from "@webpack/common";
import { User } from "discord-types/general";
import { VerifiedIcon } from "./VerifiedIcon";
-const Section = findComponentByCodeLazy(".lastSection", "children:");
-const ThemeStore = findStoreLazy("ThemeStore");
-
const useLegacyPlatformType: (platform: string) => string = findByCodeLazy(".TWITTER_LEGACY:");
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:");
@@ -76,7 +73,7 @@ interface ConnectionPlatform {
}
const profilePopoutComponent = ErrorBoundary.wrap(
- (props: { user: User; displayProfile?: any; simplified?: boolean; }) => (
+ (props: { user: User; displayProfile?: any; }) => (
(
-
- ),
- { noop: true }
-);
-
-function ConnectionsComponent({ id, theme, simplified }: { id: string, theme: string, simplified?: boolean; }) {
+function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {
const profile = UserProfileStore.getUserProfile(id);
if (!profile)
return null;
@@ -105,31 +92,14 @@ function ConnectionsComponent({ id, theme, simplified }: { id: string, theme: st
if (!connections?.length)
return null;
- const connectionsContainer = (
+ return (
{connections.map(connection => )}
);
-
- if (simplified)
- return connectionsContainer;
-
- return (
-
-
- Connections
-
- {connectionsContainer}
-
- );
}
function CompactConnectionComponent({ connection, theme }: { connection: Connection, theme: string; }) {
@@ -194,31 +164,17 @@ export default definePlugin({
name: "ShowConnections",
description: "Show connected accounts in user popouts",
authors: [Devs.TheKodeToad],
+ settings,
+
patches: [
{
- find: "{isUsingGuildBio:null!==(",
- replacement: {
- match: /,theme:\i\}\)(?=,.{0,150}setNote:)/,
- replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })"
- }
- },
- {
- find: ".PROFILE_PANEL,",
- replacement: {
- // createElement(Divider, {}), createElement(NoteComponent)
- match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/,
- replace: "$self.profilePanelComponent({ id: $1.recipients[0] }),$&"
- }
- },
- {
- find: '"BiteSizeProfileBody"',
+ find: ".hasAvatarForGuild(null==",
replacement: {
match: /currentUser:\i,guild:\i}\)(?<=user:(\i),bio:null==(\i)\?.+?)/,
- replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })"
+ replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2 })"
}
}
],
- settings,
+
profilePopoutComponent,
- profilePanelComponent
});
diff --git a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx
index c1bcbd657..8ca335bb6 100644
--- a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx
+++ b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx
@@ -18,12 +18,11 @@
import "./VoiceChannelSection.css";
-import { findByCodeLazy, findByPropsLazy } from "@webpack";
+import { findByPropsLazy } from "@webpack";
import { Button, Forms, PermissionStore, Toasts } from "@webpack/common";
import { Channel } from "discord-types/general";
const ChannelActions = findByPropsLazy("selectChannel", "selectVoiceChannel");
-const UserPopoutSection = findByCodeLazy(".lastSection", "children:");
const CONNECT = 1n << 20n;
@@ -34,7 +33,8 @@ interface VoiceChannelFieldProps {
}
export const VoiceChannelSection = ({ channel, label, showHeader }: VoiceChannelFieldProps) => (
-
+ // @TODO The div is supposed to be a UserPopoutSection
+
{showHeader && In a voice channel}
-
+
);
diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx
index 53044a558..cad539b46 100644
--- a/src/plugins/userVoiceShow/index.tsx
+++ b/src/plugins/userVoiceShow/index.tsx
@@ -84,7 +84,7 @@ export default definePlugin({
);
},
- patchPopout: ({ user }: UserProps) => {
+ patchProfilePopout: ({ user }: UserProps) => {
const isSelfUser = user.id === UserStore.getCurrentUser().id;
return (
@@ -94,21 +94,7 @@ export default definePlugin({
},
patches: [
- // above message box
- {
- find: ".popularApplicationCommandIds,",
- replacement: {
- match: /(?<=,)(?=!\i&&!\i&&.{0,50}setNote:)/,
- replace: "$self.patchPopout(arguments[0]),",
- }
- },
- // below username
- {
- find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Lazy-loaded
- replacement: {
- match: /\.body.+?displayProfile:\i}\),/,
- replace: "$&$self.patchModal(arguments[0]),",
- }
- }
+ // @TODO Maybe patch UserVoiceShow in simplified profile popout
+ // @TODO Patch new profile modal
],
});
diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx
index fbc75f52c..788a79ae7 100644
--- a/src/plugins/usrbg/index.tsx
+++ b/src/plugins/usrbg/index.tsx
@@ -57,14 +57,7 @@ export default definePlugin({
settings,
patches: [
{
- find: ".NITRO_BANNER,",
- replacement: {
- match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/,
- replace: "&&$self.shouldShowBadge(arguments[0])$&"
- }
- },
- {
- find: ".banner)==null",
+ find: '.banner)==null?"COMPLETE"',
replacement: {
match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/,
replace: "$self.patchBannerUrl(arguments[0])||$&"
@@ -109,10 +102,6 @@ export default definePlugin({
if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId);
},
- shouldShowBadge({ displayProfile, user }: any) {
- return displayProfile?.banner && (!this.userHasBackground(user.id) || settings.store.nitroFirst);
- },
-
userHasBackground(userId: string) {
return !!this.data?.users[userId];
},
diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx
index 927a974f0..c154de636 100644
--- a/src/plugins/viewIcons/index.tsx
+++ b/src/plugins/viewIcons/index.tsx
@@ -192,31 +192,12 @@ export default definePlugin({
},
all: true
},
- // Old Profiles Modal pfp
- {
- find: ".MODAL,hasProfileEffect",
- replacement: {
- match: /\{src:(\i)(?=,avatarDecoration)/,
- replace: "{src:$1,onClick:()=>$self.openImage($1)"
- }
- },
// Banners
- ...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({
- find,
- replacement: {
- // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl,
- match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/,
- replace:
- // onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0,
- 'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
- }
- })),
- // Old User DMs "User Profile" popup in the right
{
- find: ".avatarPositionPanel",
+ find: 'backgroundColor:"COMPLETE"',
replacement: {
- match: /(avatarWrapperNonUserBot.{0,50})onClick:(\i\|\|\i)\?void 0(?<=,avatarSrc:(\i).+?)/,
- replace: "$1style:($2)?{cursor:\"pointer\"}:{},onClick:$2?()=>{$self.openImage($3)}"
+ match: /(\.banner,.+?),style:{(?=.+?backgroundImage:null!=(\i)\?"url\("\.concat\(\2,)/,
+ replace: (_, rest, bannerSrc) => `${rest},onClick:()=>${bannerSrc}!=null&&$self.openImage(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
}
},
// Group DMs top small & large icon
diff --git a/src/plugins/webKeybinds.web/index.ts b/src/plugins/webKeybinds.web/index.ts
index 12d485aac..1c43dc0cf 100644
--- a/src/plugins/webKeybinds.web/index.ts
+++ b/src/plugins/webKeybinds.web/index.ts
@@ -35,6 +35,7 @@ export default definePlugin({
if (hasCtrl) switch (e.key) {
case "t":
case "T":
+ if (!IS_VESKTOP) return;
e.preventDefault();
if (e.shiftKey) {
if (SelectedGuildStore.getGuildId()) NavigationRouter.transitionToGuild("@me");
@@ -47,14 +48,15 @@ export default definePlugin({
});
}
break;
+ case "Tab":
+ if (!IS_VESKTOP) return;
+ const handler = e.shiftKey ? KeyBinds.SERVER_PREV : KeyBinds.SERVER_NEXT;
+ handler.action(e);
+ break;
case ",":
e.preventDefault();
SettingsRouter.open("My Account");
break;
- case "Tab":
- const handler = e.shiftKey ? KeyBinds.SERVER_PREV : KeyBinds.SERVER_NEXT;
- handler.action(e);
- break;
default:
if (e.key >= "1" && e.key <= "9") {
e.preventDefault();
diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts
index ce1e3e268..dd76d1ade 100644
--- a/src/webpack/common/types/utils.d.ts
+++ b/src/webpack/common/types/utils.d.ts
@@ -223,9 +223,26 @@ export interface Constants {
FriendsSections: Record
;
}
+export type ActiveView = LiteralUnion<"emoji" | "gif" | "sticker" | "soundboard", string>;
+
+export interface ExpressionPickerStoreState extends Record {
+ activeView: ActiveView | null;
+ lastActiveView: ActiveView | null;
+ activeViewType: any | null;
+ searchQuery: string;
+ isSearchSuggestion: boolean,
+ pickerId: string;
+}
+
export interface ExpressionPickerStore {
+ openExpressionPicker(activeView: ActiveView, activeViewType?: any): void;
closeExpressionPicker(activeViewType?: any): void;
- openExpressionPicker(activeView: LiteralUnion<"emoji" | "gif" | "sticker", string>, activeViewType?: any): void;
+ toggleMultiExpressionPicker(activeViewType?: any): void;
+ toggleExpressionPicker(activeView: ActiveView, activeViewType?: any): void;
+ setExpressionPickerView(activeView: ActiveView): void;
+ setSearchQuery(searchQuery: string, isSearchSuggestion?: boolean): void;
+ useExpressionPickerStore(): ExpressionPickerStoreState;
+ useExpressionPickerStore(selector: (state: ExpressionPickerStoreState) => T): T;
}
export interface BrowserWindowFeatures {
diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts
index 280b2ba90..f9cce556b 100644
--- a/src/webpack/common/utils.ts
+++ b/src/webpack/common/utils.ts
@@ -16,7 +16,6 @@
* along with this program. If not, see .
*/
-import { canonicalizeMatch } from "@utils/patches";
import type { Channel } from "discord-types/general";
// eslint-disable-next-line path-alias/no-relative
@@ -162,11 +161,14 @@ export const InviteActions = findByPropsLazy("resolveInvite");
export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");
-const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i,activeViewType:/);
-// TODO: type
export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", {
+ openExpressionPicker: filters.byCode(/setState\({activeView:(?:(?!null)\i),activeViewType:/),
closeExpressionPicker: filters.byCode("setState({activeView:null"),
- openExpressionPicker: m => typeof m === "function" && openExpressionPickerMatcher.test(m.toString()),
+ toggleMultiExpressionPicker: filters.byCode(".EMOJI,"),
+ toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/),
+ setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/),
+ setSearchQuery: filters.byCode("searchQuery:"),
+ useExpressionPickerStore: filters.byCode("Object.is")
});
export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', {
diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts
index f32aeb789..fb640cea8 100644
--- a/src/webpack/patchWebpack.ts
+++ b/src/webpack/patchWebpack.ts
@@ -233,7 +233,7 @@ function patchFactories(factories: Record string, original: any, (...args: any[]): void; };
+ } as any as { toString: () => string, original: any, (...args: any[]): void; $$vencordPatchedSource?: string; };
factory.toString = originalMod.toString.bind(originalMod);
factory.original = originalMod;
@@ -354,5 +354,17 @@ function patchFactories(factories: Record boolean;
-type PropsFilter = Array;
-type CodeFilter = Array;
-type StoreNameFilter = string;
+export type PropsFilter = Array;
+export type CodeFilter = Array;
+export type StoreNameFilter = string;
-const stringMatches = (s: string, filter: CodeFilter) =>
+export const stringMatches = (s: string, filter: CodeFilter) =>
filter.every(f =>
typeof f === "string"
? s.includes(f)
- : f.test(s)
+ : (f.global && (f.lastIndex = 0), f.test(s))
);
export const filters = {
@@ -258,6 +258,8 @@ export const findBulk = traceFunction("findBulk", function findBulk(...filterFns
* @returns string or null
*/
export const findModuleId = traceFunction("findModuleId", function findModuleId(...code: CodeFilter) {
+ code = code.map(canonicalizeMatch);
+
for (const id in wreq.m) {
if (stringMatches(wreq.m[id].toString(), code)) return id;
}
@@ -452,12 +454,9 @@ export function findExportedComponentLazy(...props: Prop
* })
*/
export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record): Record {
- if (!Array.isArray(code)) code = [code];
- code = code.map(canonicalizeMatch);
-
const exports = {} as Record;
- const id = findModuleId(...code);
+ const id = findModuleId(...Array.isArray(code) ? code : [code]);
if (id === null)
return exports;
@@ -606,6 +605,8 @@ export function waitFor(filter: string | PropsFilter | FilterFn, callback: Callb
* @returns Mapping of found modules
*/
export function search(...code: CodeFilter) {
+ code = code.map(canonicalizeMatch);
+
const results = {} as Record;
const factories = wreq.m;