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/api/Settings.ts b/src/api/Settings.ts index 88337a917..ac116f547 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -230,6 +230,10 @@ export function definePluginSettings< if (!definedSettings.pluginName) throw new Error("Cannot access settings before plugin is initialized"); return Settings.plugins[definedSettings.pluginName] as any; }, + get plain() { + if (!definedSettings.pluginName) throw new Error("Cannot access settings before plugin is initialized"); + return PlainSettings.plugins[definedSettings.pluginName] as any; + }, use: settings => useSettings( settings?.map(name => `plugins.${definedSettings.pluginName}.${name}`) as UseSettings[] ).plugins[definedSettings.pluginName] as any, 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/alwaysExpandRoles/README.md b/src/plugins/alwaysExpandRoles/README.md new file mode 100644 index 000000000..344268cbf --- /dev/null +++ b/src/plugins/alwaysExpandRoles/README.md @@ -0,0 +1,3 @@ +# Always Expand Roles + +Always expands the role list in profile popouts diff --git a/src/plugins/alwaysExpandRoles/index.ts b/src/plugins/alwaysExpandRoles/index.ts new file mode 100644 index 000000000..1c20b9777 --- /dev/null +++ b/src/plugins/alwaysExpandRoles/index.ts @@ -0,0 +1,37 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { migratePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +migratePluginSettings("AlwaysExpandRoles", "ShowAllRoles"); +export default definePlugin({ + name: "AlwaysExpandRoles", + description: "Always expands the role list in profile popouts", + authors: [Devs.surgedevs], + patches: [ + { + find: 'action:"EXPAND_ROLES"', + replacement: { + match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/, + replace: (_, rest, setExpandedRoles) => `${rest}!0)` + } + } + ] +}); 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/consoleJanitor/index.ts b/src/plugins/consoleJanitor/index.ts index dfb59957d..e847c4124 100644 --- a/src/plugins/consoleJanitor/index.ts +++ b/src/plugins/consoleJanitor/index.ts @@ -60,13 +60,6 @@ export default definePlugin({ replace: "" } }, - { - find: "notosans-400-normalitalic", - replacement: { - match: /,"notosans-.+?"/g, - replace: "" - } - }, { find: 'console.warn("[DEPRECATED] Please use `subscribeWithSelector` middleware");', all: true, 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/mentionAvatars/README.md b/src/plugins/mentionAvatars/README.md index 912b51916..5e553419e 100644 --- a/src/plugins/mentionAvatars/README.md +++ b/src/plugins/mentionAvatars/README.md @@ -1,5 +1,6 @@ # MentionAvatars -Shows user avatars inside mentions +Shows user avatars and role icons inside mentions ![](https://github.com/user-attachments/assets/fc76ea47-5e19-4063-a592-c57785a75cc7) +![](https://github.com/user-attachments/assets/76c4c3d9-7cde-42db-ba84-903cbb40c163) diff --git a/src/plugins/mentionAvatars/index.tsx b/src/plugins/mentionAvatars/index.tsx index 311303ab9..53ab93e38 100644 --- a/src/plugins/mentionAvatars/index.tsx +++ b/src/plugins/mentionAvatars/index.tsx @@ -10,21 +10,42 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { SelectedGuildStore, useState } from "@webpack/common"; +import { GuildStore, SelectedGuildStore, useState } from "@webpack/common"; import { User } from "discord-types/general"; const settings = definePluginSettings({ showAtSymbol: { type: OptionType.BOOLEAN, - description: "Whether the the @ symbol should be displayed", + description: "Whether the the @ symbol should be displayed on user mentions", default: true } }); +function DefaultRoleIcon() { + return ( + + + + + + ); +} + export default definePlugin({ name: "MentionAvatars", - description: "Shows user avatars inside mentions", - authors: [Devs.Ven], + description: "Shows user avatars and role icons inside mentions", + authors: [Devs.Ven, Devs.SerStars], patches: [{ find: ".USER_MENTION)", @@ -32,6 +53,13 @@ export default definePlugin({ match: /children:"@"\.concat\((null!=\i\?\i:\i)\)(?<=\.useName\((\i)\).+?)/, replace: "children:$self.renderUsername({username:$1,user:$2})" } + }, + { + find: ".ROLE_MENTION)", + replacement: { + match: /children:\[\i&&.{0,50}\.RoleDot.{0,300},\i(?=\])/, + replace: "$&,$self.renderRoleIcon(arguments[0])" + } }], settings, @@ -47,12 +75,31 @@ export default definePlugin({ onMouseEnter={() => setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} > - + {getUsernameString(username)} ); - }, { noop: true }) + }, { noop: true }), + renderRoleIcon: ErrorBoundary.wrap(({ roleId, guildId }: { roleId: string, guildId: string; }) => { + // Discord uses Role Mentions for uncached users because .... idk + if (!roleId) return null; + + const role = GuildStore.getRole(guildId, roleId); + + if (!role?.icon) return ; + + return ( + + ); + }), }); function getUsernameString(username: string) { diff --git a/src/plugins/mentionAvatars/styles.css b/src/plugins/mentionAvatars/styles.css index 022f968c0..64eb41416 100644 --- a/src/plugins/mentionAvatars/styles.css +++ b/src/plugins/mentionAvatars/styles.css @@ -1,8 +1,11 @@ -.vc-mentionAvatars-avatar { +.vc-mentionAvatars-icon { vertical-align: middle; width: 1em !important; /* insane discord sets width: 100% in channel topic */ height: 1em; margin: 0 4px 0.2rem 2px; - border-radius: 50%; box-sizing: border-box; } + +.vc-mentionAvatars-role-icon { + margin: 0 2px 0.2rem 4px; +} diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 45538fb66..0a87c57a9 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -22,7 +22,7 @@ import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy, findLazy } from "@webpack"; -import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip, useState } from "@webpack/common"; +import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common"; import type { Permissions, RC } from "@webpack/types"; import type { Channel, Guild, Message, User } from "discord-types/general"; @@ -107,14 +107,8 @@ const defaultSettings = Object.fromEntries( tags.map(({ name, displayName }) => [name, { text: displayName, showInChat: true, showInNotChat: true }]) ) as TagSettings; -function SettingsComponent(props: { setValue(v: any): void; }) { - settings.store.tagSettings ??= defaultSettings; - - const [tagSettings, setTagSettings] = useState(settings.store.tagSettings as TagSettings); - const setValue = (v: TagSettings) => { - setTagSettings(v); - props.setValue(v); - }; +function SettingsComponent() { + const tagSettings = settings.store.tagSettings ??= defaultSettings; return ( @@ -137,19 +131,13 @@ function SettingsComponent(props: { setValue(v: any): void; }) { type="text" value={tagSettings[t.name]?.text ?? t.displayName} placeholder={`Text on tag (default: ${t.displayName})`} - onChange={v => { - tagSettings[t.name].text = v; - setValue(tagSettings); - }} + onChange={v => tagSettings[t.name].text = v} className={Margins.bottom16} /> { - tagSettings[t.name].showInChat = v; - setValue(tagSettings); - }} + onChange={v => tagSettings[t.name].showInChat = v} hideBorder > Show in messages @@ -157,10 +145,7 @@ function SettingsComponent(props: { setValue(v: any): void; }) { { - tagSettings[t.name].showInNotChat = v; - setValue(tagSettings); - }} + onChange={v => tagSettings[t.name].showInNotChat = v} hideBorder > Show in member list and profiles @@ -183,7 +168,7 @@ const settings = definePluginSettings({ tagSettings: { type: OptionType.COMPONENT, component: SettingsComponent, - description: "fill me", + description: "fill me" } }); @@ -247,9 +232,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..dc2aa6fa4 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", "expandButton", "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..7c3967a37 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -169,32 +169,22 @@ 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: { - match: /children:"\+"\.concat\(\i\.length-\i\.length\).{0,20}\}\),/, + match: /\.collapseButton,.+?}\)}\),/, replace: "$&$self.ViewPermissionsButton(arguments[0])," } } ], - 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/utils/constants.ts b/src/utils/constants.ts index 95d46d1cc..91ff3c6e6 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -546,6 +546,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Lumap", id: 585278686291427338n, }, + SerStars: { + name: "SerStars", + id: 861631850681729045n, + }, } satisfies Record); // iife so #__PURE__ works correctly diff --git a/src/utils/types.ts b/src/utils/types.ts index 8c24843f8..e5486e9a5 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -309,6 +309,8 @@ export interface DefinedSettings< > { /** Shorthand for `Vencord.Settings.plugins.PluginName`, but with typings */ store: SettingsStore & PrivateSettings; + /** Shorthand for `Vencord.PlainSettings.plugins.PluginName`, but with typings */ + plain: SettingsStore & PrivateSettings; /** * React hook for getting the settings for this plugin * @param filter optional filter to avoid rerenders for irrelevent settings 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;