From 7ec842d4b0c94ee10d598e7daa320549554a8978 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:09:58 -0300 Subject: [PATCH 01/35] MoreUserTags: fix settings ui logic Co-authored-by: Vendicated --- src/api/Settings.ts | 4 ++++ src/plugins/moreUserTags/index.tsx | 29 +++++++---------------------- src/utils/types.ts | 2 ++ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 88337a917d..ac116f547e 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/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 4802f04a1b..0a87c57a9d 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" } }); diff --git a/src/utils/types.ts b/src/utils/types.ts index 8c24843f86..e5486e9a52 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 From 4b16fbcaa9b8b0b2401dfd48d7690e7830d82af8 Mon Sep 17 00:00:00 2001 From: SerStars <90714930+SerStars@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:27:00 +0200 Subject: [PATCH 02/35] MentionAvatars: Also display role icons in role mentions (#2801) Co-authored-by: Vendicated --- src/plugins/mentionAvatars/README.md | 3 +- src/plugins/mentionAvatars/index.tsx | 59 ++++++++++++++++++++++++--- src/plugins/mentionAvatars/styles.css | 7 +++- src/utils/constants.ts | 4 ++ 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/plugins/mentionAvatars/README.md b/src/plugins/mentionAvatars/README.md index 912b519167..5e553419ed 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 311303ab92..53ab93e38d 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 022f968c0a..64eb41416c 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/utils/constants.ts b/src/utils/constants.ts index febb8f9afe..d27759e959 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 From 7d8214fc37b4f4baeca1c7deb06ef37147c70fde Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 28 Aug 2024 02:10:20 -0300 Subject: [PATCH 03/35] Fix PermissionsViewer on user popouts --- src/plugins/permissionsViewer/components/UserPermissions.tsx | 2 +- src/plugins/permissionsViewer/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index f53f93b6d2..dc2aa6fa4a 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -37,7 +37,7 @@ type UserPermissions = Array; const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() => { const [RoleRootClasses, RoleClasses, RoleBorderClasses] = findBulk( - filters.byProps("root", "showMoreButton", "collapseButton"), + filters.byProps("root", "expandButton", "collapseButton"), filters.byProps("role", "roleCircle", "roleName"), filters.byProps("roleCircle", "dot", "dotBorderColor") ) as Record[]; diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index 5039d04f83..7c3967a37e 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -172,7 +172,7 @@ export default definePlugin({ { find: ".VIEW_ALL_ROLES,", replacement: { - match: /children:"\+"\.concat\(\i\.length-\i\.length\).{0,20}\}\),/, + match: /\.collapseButton,.+?}\)}\),/, replace: "$&$self.ViewPermissionsButton(arguments[0])," } } From 5bfc608f7dc9e6c82f03adf3313a1c83e390291b Mon Sep 17 00:00:00 2001 From: Surge <112782958+surgedevs@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:43:52 +0200 Subject: [PATCH 04/35] new plugin AlwaysExpandRoles ~ Alternative to ShowAllRoles (#2809) --- src/plugins/alwaysExpandRoles/README.md | 3 ++ src/plugins/alwaysExpandRoles/index.ts | 37 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/plugins/alwaysExpandRoles/README.md create mode 100644 src/plugins/alwaysExpandRoles/index.ts diff --git a/src/plugins/alwaysExpandRoles/README.md b/src/plugins/alwaysExpandRoles/README.md new file mode 100644 index 0000000000..344268cbf3 --- /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 0000000000..1c20b97771 --- /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)` + } + } + ] +}); From db2f5c9292ad40c5939e1023b21aed622a793a6f Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 29 Aug 2024 03:56:21 -0300 Subject: [PATCH 05/35] ConsoleJanitor: Remove non needed patch notosans no longer errors --- src/plugins/consoleJanitor/index.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/plugins/consoleJanitor/index.ts b/src/plugins/consoleJanitor/index.ts index dfb59957d7..e847c41246 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, From ad3d936dfd75a22e17213e66d2288b46abe6cc90 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:41:51 -0300 Subject: [PATCH 06/35] Fix settings wrapping fallback --- src/plugins/_core/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 3cc020836f..be220db1a5 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -64,7 +64,7 @@ export default definePlugin({ replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` }, { - match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, + match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,60}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` } ] From e473a57c3d5ee624669910f7f9120038fe12a050 Mon Sep 17 00:00:00 2001 From: Cookie <52550063+Covkie@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:08:48 -0400 Subject: [PATCH 07/35] IgnoreActivities: Add option for blacklist filter (#2712) --- src/plugins/ignoreActivities/index.tsx | 88 ++++++++++++++++++-------- src/utils/constants.ts | 4 ++ 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index bab69d89b4..9e6c21bdde 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -26,6 +26,11 @@ interface IgnoredActivity { type: ActivitiesTypes; } +const enum FilterMode { + Whitelist, + Blacklist +} + const RunningGameStore = findStoreLazy("RunningGameStore"); const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; @@ -70,14 +75,17 @@ function handleActivityToggle(e: React.MouseEvent if (ignoredActivityIndex === -1) settings.store.ignoredActivities = getIgnoredActivities().concat(activity); else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex); - // Trigger activities recalculation + recalculateActivities(); +} + +function recalculateActivities() { ShowCurrentGame.updateSetting(old => old); } function ImportCustomRPCComponent() { return ( - Import the application id of the CustomRPC plugin to the allowed list + Import the application id of the CustomRPC plugin to the filter list
+ )} From 273981deb74039c0aa8252f5c981d21f7352a412 Mon Sep 17 00:00:00 2001 From: ImBanana Date: Sun, 1 Sep 2024 05:33:07 +0300 Subject: [PATCH 13/35] new plugin StickerPaste ~ Insert stickers instead of sending (#2781) --- src/plugins/stickerPaste/README.md | 3 +++ src/plugins/stickerPaste/index.ts | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/plugins/stickerPaste/README.md create mode 100644 src/plugins/stickerPaste/index.ts diff --git a/src/plugins/stickerPaste/README.md b/src/plugins/stickerPaste/README.md new file mode 100644 index 0000000000..a29170272c --- /dev/null +++ b/src/plugins/stickerPaste/README.md @@ -0,0 +1,3 @@ +# StickerPaste + +Makes picking a sticker in the sticker picker insert it into the chatbox instead of instantly sending. diff --git a/src/plugins/stickerPaste/index.ts b/src/plugins/stickerPaste/index.ts new file mode 100644 index 0000000000..8a00820734 --- /dev/null +++ b/src/plugins/stickerPaste/index.ts @@ -0,0 +1,24 @@ +/* + * 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: "StickerPaste", + description: "Makes picking a sticker in the sticker picker insert it into the chatbox instead of instantly sending", + authors: [Devs.ImBanana], + + patches: [ + { + find: ".stickers,previewSticker:", + replacement: { + match: /if\(\i\.\i\.getUploadCount/, + replace: "return true;$&", + } + } + ] +}); From e07a4e19e65b556b0e398a39499b85b88699756b Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:08:33 -0400 Subject: [PATCH 14/35] VolumeBooster: Support browser and Vesktop (#2730) --- src/plugins/volumeBooster/README.md | 9 +++ .../index.ts | 61 +++++++++++++++++-- src/utils/constants.ts | 4 ++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/plugins/volumeBooster/README.md rename src/plugins/{volumeBooster.discordDesktop => volumeBooster}/index.ts (64%) diff --git a/src/plugins/volumeBooster/README.md b/src/plugins/volumeBooster/README.md new file mode 100644 index 0000000000..62bd5ab52b --- /dev/null +++ b/src/plugins/volumeBooster/README.md @@ -0,0 +1,9 @@ +# Volume Booster + +Allows you to boost the volume over 200% on desktop and over 100% on other clients. + +Works on users, bots, and streams! + +![the volume being moved up to 270% on vesktop](https://github.com/user-attachments/assets/793e012e-c069-4fa4-a3d5-61c2f55edd3e) + +![the volume being moved up to 297% on a stream](https://github.com/user-attachments/assets/77463eb9-2537-4821-a3ab-82f60633ccbc) diff --git a/src/plugins/volumeBooster.discordDesktop/index.ts b/src/plugins/volumeBooster/index.ts similarity index 64% rename from src/plugins/volumeBooster.discordDesktop/index.ts rename to src/plugins/volumeBooster/index.ts index b455c97ec5..3ab47b197e 100644 --- a/src/plugins/volumeBooster.discordDesktop/index.ts +++ b/src/plugins/volumeBooster/index.ts @@ -31,10 +31,27 @@ const settings = definePluginSettings({ } }); +interface StreamData { + audioContext: AudioContext, + audioElement: HTMLAudioElement, + emitter: any, + // added by this plugin + gainNode?: GainNode, + id: string, + levelNode: AudioWorkletNode, + sinkId: string, + stream: MediaStream, + streamSourceNode?: MediaStreamAudioSourceNode, + videoStreamId: string, + _mute: boolean, + _speakingFlags: number, + _volume: number; +} + export default definePlugin({ name: "VolumeBooster", - authors: [Devs.Nuckyz], - description: "Allows you to set the user and stream volume above the default maximum.", + authors: [Devs.Nuckyz, Devs.sadan], + description: "Allows you to set the user and stream volume above the default maximum", settings, patches: [ @@ -45,12 +62,28 @@ export default definePlugin({ ].map(find => ({ find, replacement: { - match: /(?<=maxValue:\i\.\i)\?(\d+?):(\d+?)(?=,)/, - replace: (_, higherMaxVolume, minorMaxVolume) => "" - + `?${higherMaxVolume}*$self.settings.store.multiplier` - + `:${minorMaxVolume}*$self.settings.store.multiplier` + match: /(?<=maxValue:)\i\.\i\?(\d+?):(\d+?)(?=,)/, + replace: (_, higherMaxVolume, minorMaxVolume) => `${higherMaxVolume}*$self.settings.store.multiplier` } })), + // Patches needed for web/vesktop + { + find: "streamSourceNode", + predicate: () => IS_WEB, + group: true, + replacement: [ + // Remove rounding algorithm + { + match: /Math\.max.{0,30}\)\)/, + replace: "arguments[0]" + }, + // Patch the volume + { + match: /\.volume=this\._volume\/100;/, + replace: ".volume=0.00;$self.patchVolume(this);" + } + ] + }, // Prevent Audio Context Settings sync from trying to sync with values above 200, changing them to 200 before we send to Discord { find: "AudioContextSettingsMigrated", @@ -83,4 +116,20 @@ export default definePlugin({ ] } ], + + patchVolume(data: StreamData) { + if (data.stream.getAudioTracks().length === 0) return; + + data.streamSourceNode ??= data.audioContext.createMediaStreamSource(data.stream); + + if (!data.gainNode) { + const gain = data.gainNode = data.audioContext.createGain(); + data.streamSourceNode.connect(gain); + gain.connect(data.audioContext.destination); + } + + data.gainNode.gain.value = data._mute + ? 0 + : data._volume / 100; + } }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 60c2032524..7fb2b6bf04 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -534,6 +534,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Joona", id: 297410829589020673n }, + sadan: { + name: "sadan", + id: 521819891141967883n, + }, Kylie: { name: "Cookie", id: 721853658941227088n From b595a3e33c4a39e67a36dc9041ed2aa90c30aaea Mon Sep 17 00:00:00 2001 From: vxray <41696677+vxray@users.noreply.github.com> Date: Sun, 1 Sep 2024 05:20:22 +0200 Subject: [PATCH 15/35] OpenInApp: Add support for localization in Spotify URL regex (#2776) --- src/plugins/openInApp/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 1a68e8f5d9..2e332465e2 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -33,7 +33,7 @@ interface URLReplacementRule { // Do not forget to add protocols to the ALLOWED_PROTOCOLS constant const UrlReplacementRules: Record = { spotify: { - match: /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/, + match: /^https:\/\/open\.spotify\.com\/(?:intl-[a-z]{2}\/)?(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/, replace: (_, type, id) => `spotify://${type}/${id}`, description: "Open Spotify links in the Spotify app", shortlinkMatch: /^https:\/\/spotify\.link\/.+$/, From 968e688c106ea7b45c1bb522c73104239d3663a6 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:53:46 -0300 Subject: [PATCH 16/35] Bump to 1.9.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fdda2659c..65a2f4512b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.8", + "version": "1.9.9", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 74fd85bd3d7a86c495534f08c6c508f379514b23 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:39:19 -0400 Subject: [PATCH 17/35] VolumeBooster: Fix on Vesktop (#2828) Also fixed an IgnoreActivities patch and added a README to it --- .../README.md | 0 .../index.tsx | 0 .../style.css | 0 src/plugins/ignoreActivities/README.md | 13 +++++++++++++ src/plugins/ignoreActivities/index.tsx | 10 +++++++++- src/plugins/volumeBooster/index.ts | 2 +- 6 files changed, 23 insertions(+), 2 deletions(-) rename src/plugins/{CopyFileContents => copyFileContents}/README.md (100%) rename src/plugins/{CopyFileContents => copyFileContents}/index.tsx (100%) rename src/plugins/{CopyFileContents => copyFileContents}/style.css (100%) create mode 100644 src/plugins/ignoreActivities/README.md diff --git a/src/plugins/CopyFileContents/README.md b/src/plugins/copyFileContents/README.md similarity index 100% rename from src/plugins/CopyFileContents/README.md rename to src/plugins/copyFileContents/README.md diff --git a/src/plugins/CopyFileContents/index.tsx b/src/plugins/copyFileContents/index.tsx similarity index 100% rename from src/plugins/CopyFileContents/index.tsx rename to src/plugins/copyFileContents/index.tsx diff --git a/src/plugins/CopyFileContents/style.css b/src/plugins/copyFileContents/style.css similarity index 100% rename from src/plugins/CopyFileContents/style.css rename to src/plugins/copyFileContents/style.css diff --git a/src/plugins/ignoreActivities/README.md b/src/plugins/ignoreActivities/README.md new file mode 100644 index 0000000000..abc75720f0 --- /dev/null +++ b/src/plugins/ignoreActivities/README.md @@ -0,0 +1,13 @@ +# IgnoreActivities + +Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings. + +![](https://github.com/user-attachments/assets/f0c19060-0ecf-4f1c-8165-a5aa40143c82) + +![](https://github.com/user-attachments/assets/73c3fa7a-5b90-41ee-a4d6-91fa76458b74) + +![](https://github.com/user-attachments/assets/1ab3fe73-3911-48d1-8a08-e976af614b41) + +The activity stays showing as a detected game even if ignored, differently from the stock Toggle Detection button from Discord: + +![](https://github.com/user-attachments/assets/08ea60c3-3a31-42de-ae4c-7535fbf1b45a) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 9e6c21bdde..02261b5bad 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -237,7 +237,7 @@ function isActivityTypeIgnored(type: number, id?: string) { export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz, Devs.Kylie], - description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", + description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below", dependencies: ["UserSettingsAPI"], settings, @@ -266,6 +266,7 @@ export default definePlugin({ replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),` } }, + // Discord has 3 different components for activities. Currently, the last is the one being used { find: ".activityTitleText,variant", replacement: { @@ -279,6 +280,13 @@ export default definePlugin({ match: /\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),/, replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),` } + }, + { + find: ".promotedLabelWrapperNonBanner,children", + replacement: { + match: /\.appDetailsHeaderContainer.+?children:\i.*?}\),(?<=application:(\i).+?)/, + replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),` + } } ], diff --git a/src/plugins/volumeBooster/index.ts b/src/plugins/volumeBooster/index.ts index 3ab47b197e..02a955a803 100644 --- a/src/plugins/volumeBooster/index.ts +++ b/src/plugins/volumeBooster/index.ts @@ -69,7 +69,7 @@ export default definePlugin({ // Patches needed for web/vesktop { find: "streamSourceNode", - predicate: () => IS_WEB, + predicate: () => !IS_DISCORD_DESKTOP, group: true, replacement: [ // Remove rounding algorithm From 0c71d6c3fae57a3e5f9a86a69b309897171aa31d Mon Sep 17 00:00:00 2001 From: vishnyanetchereshnya <151846235+vishnyanetchereshnya@users.noreply.github.com> Date: Mon, 2 Sep 2024 06:42:52 +0300 Subject: [PATCH 18/35] PermissionsViewer: Show RoleIcons & which role grants permission (#2824) --- .../components/RolesAndUsersPermissions.tsx | 106 ++++++++------- .../components/UserPermissions.tsx | 92 +++++++++---- src/plugins/permissionsViewer/index.tsx | 91 +++++++------ src/plugins/permissionsViewer/styles.css | 121 ++++++++---------- src/webpack/common/types/components.d.ts | 2 +- 5 files changed, 226 insertions(+), 186 deletions(-) diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index 963750fa31..82925d610d 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -21,8 +21,10 @@ import { Flex } from "@components/Flex"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; import { getUniqueUsername } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; -import type { Guild } from "discord-types/general"; +import { findByCodeLazy } from "@webpack"; +import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; +import { UnicodeEmoji } from "@webpack/types"; +import type { Guild, Role, User } from "discord-types/general"; import { settings } from ".."; import { cl, getPermissionDescription, getPermissionString } from "../utils"; @@ -42,15 +44,15 @@ export interface RoleOrUserPermission { overwriteDeny?: bigint; } -function openRolesAndUsersPermissionsModal(permissions: Array, guild: Guild, header: string) { - return openModal(modalProps => ( - - )); +type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; }; +const getRoleIconData: GetRoleIconData = findByCodeLazy("convertSurrogateToName", "customIconSrc", "unicodeEmoji"); + +function getRoleIconSrc(role: Role) { + const icon = getRoleIconData(role, 20); + if (!icon) return; + + const { customIconSrc, unicodeEmoji } = icon; + return customIconSrc ?? unicodeEmoji?.url; } function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array; guild: Guild; modalProps: ModalProps; header: string; }) { @@ -86,31 +88,34 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea size={ModalSize.LARGE} > - {header} permissions: + {header} permissions: - + {!selectedItem && ( -
+
No permissions to display!
)} {selectedItem && ( -
-
+
+ {permissions.map((permission, index) => { - const user = UserStore.getUser(permission.id ?? ""); - const role = roles[permission.id ?? ""]; + const user: User | undefined = UserStore.getUser(permission.id ?? ""); + const role: Role | undefined = roles[permission.id ?? ""]; + const roleIconSrc = role != null ? getRoleIconSrc(role) : undefined; return ( - +
); })} -
-
+ +
+ {Object.entries(PermissionsBits).map(([permissionName, bit]) => ( -
-
+
+
{(() => { const { permissions, overwriteAllow, overwriteDeny } = selectedItem; @@ -192,11 +199,11 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
))} -
+
)} - + ); } @@ -208,7 +215,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str aria-label="Role Options" > { Clipboard.copy(roleId); @@ -217,14 +224,13 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str {(settings.store as any).unsafeViewAsRole && ( { const role = GuildStore.getRole(guild.id, roleId); if (!role) return; onClose(); - FluxDispatcher.dispatch({ type: "IMPERSONATE_UPDATE", guildId: guild.id, @@ -235,15 +241,14 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str } } }); - } - } + }} /> )} ); } -function UserContextMenu({ userId, onClose }: { userId: string; onClose: () => void; }) { +function UserContextMenu({ userId }: { userId: string; }) { return ( v aria-label="User Options" > { Clipboard.copy(userId); @@ -263,4 +268,13 @@ function UserContextMenu({ userId, onClose }: { userId: string; onClose: () => v const RolesAndUsersPermissions = ErrorBoundary.wrap(RolesAndUsersPermissionsComponent); -export default openRolesAndUsersPermissionsModal; +export default function openRolesAndUsersPermissionsModal(permissions: Array, guild: Guild, header: string) { + return openModal(modalProps => ( + + )); +} diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index dc2aa6fa4a..7c0858f108 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -29,6 +29,7 @@ import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermi interface UserPermission { permission: string; + roleName: string; roleColor: string; rolePosition: number; } @@ -45,8 +46,48 @@ const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() return { RoleRootClasses, RoleClasses, RoleBorderClasses }; }); +interface FakeRoleProps extends React.HTMLAttributes { + text: string; + color: string; +} + +function FakeRole({ text, color, ...props }: FakeRoleProps) { + return ( +
+
+ +
+
+ + {text} + +
+
+ ); +} + +interface GrantedByTooltipProps { + roleName: string; + roleColor: string; +} + +function GrantedByTooltip({ roleName, roleColor }: GrantedByTooltipProps) { + return ( + <> + Granted By + + + ); +} + function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { guild: Guild; guildMember: GuildMember; forceOpen?: boolean; }) { - const stns = settings.use(["permissionsSortOrder"]); + const { permissionsSortOrder } = settings.use(["permissionsSortOrder"]); const [rolePermissions, userPermissions] = useMemo(() => { const userPermissions: UserPermissions = []; @@ -67,6 +108,7 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g const OWNER = i18n.Messages.GUILD_OWNER || "Server Owner"; userPermissions.push({ permission: OWNER, + roleName: "Owner", roleColor: "var(--primary-300)", rolePosition: Infinity }); @@ -75,10 +117,11 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g sortUserRoles(userRoles); for (const [permission, bit] of Object.entries(PermissionsBits)) { - for (const { permissions, colorString, position } of userRoles) { + for (const { permissions, colorString, position, name } of userRoles) { if ((permissions & bit) === bit) { userPermissions.push({ permission: getPermissionString(permission), + roleName: name, roleColor: colorString || "var(--primary-300)", rolePosition: position }); @@ -91,7 +134,7 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g userPermissions.sort((a, b) => b.rolePosition - a.rolePosition); return [rolePermissions, userPermissions]; - }, [stns.permissionsSortOrder]); + }, [permissionsSortOrder]); return ( settings.store.defaultPermissionsDropdownState = !state} defaultState={settings.store.defaultPermissionsDropdownState} buttons={[ - ( + {tooltipProps => ( - +
)} - ) + ]}> {userPermissions.length > 0 && (
- {userPermissions.map(({ permission, roleColor }) => ( -
-
- -
-
- - {permission} - -
-
+ {userPermissions.map(({ permission, roleColor, roleName }) => ( + } + tooltipClassName={cl("granted-by-container")} + tooltipContentClassName={cl("granted-by-content")} + > + {tooltipProps => ( + + )} + ))}
)} diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index 7c3967a37e..44dfd5f54a 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -26,7 +26,7 @@ import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; +import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions"; @@ -54,12 +54,12 @@ export const settings = definePluginSettings({ options: [ { label: "Highest Role", value: PermissionsSortOrder.HighestRole, default: true }, { label: "Lowest Role", value: PermissionsSortOrder.LowestRole } - ], + ] }, defaultPermissionsDropdownState: { description: "Whether the permissions dropdown on user popouts should be open by default", type: OptionType.BOOLEAN, - default: false, + default: false } }); @@ -73,14 +73,11 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { action={() => { const guild = GuildStore.getGuild(guildId); - let permissions: RoleOrUserPermission[]; - let header: string; - - switch (type) { - case MenuItemParentType.User: { + const { permissions, header }: { permissions: RoleOrUserPermission[], header: string; } = match(type) + .with(MenuItemParentType.User, () => { const member = GuildMemberStore.getMember(guildId, id!); - permissions = getSortedRoles(guild, member) + const permissions: RoleOrUserPermission[] = getSortedRoles(guild, member) .map(role => ({ type: PermissionType.Role, ...role @@ -93,37 +90,37 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { }); } - header = member.nick ?? UserStore.getUser(member.userId).username; - - break; - } - - case MenuItemParentType.Channel: { + return { + permissions, + header: member.nick ?? UserStore.getUser(member.userId).username + }; + }) + .with(MenuItemParentType.Channel, () => { const channel = ChannelStore.getChannel(id!); - permissions = sortPermissionOverwrites(Object.values(channel.permissionOverwrites).map(({ id, allow, deny, type }) => ({ + const permissions = sortPermissionOverwrites(Object.values(channel.permissionOverwrites).map(({ id, allow, deny, type }) => ({ type: type as PermissionType, id, overwriteAllow: allow, overwriteDeny: deny })), guildId); - header = channel.name; - - break; - } - - default: { - permissions = Object.values(GuildStore.getRoles(guild.id)).map(role => ({ + return { + permissions, + header: channel.name + }; + }) + .otherwise(() => { + const permissions = Object.values(GuildStore.getRoles(guild.id)).map(role => ({ type: PermissionType.Role, ...role })); - header = guild.name; - - break; - } - } + return { + permissions, + header: guild.name + }; + }); openRolesAndUsersPermissionsModal(permissions, guild, header); }} @@ -133,32 +130,34 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentType): NavContextMenuPatchCallback { return (children, props) => { - if (!props) return; - if ((type === MenuItemParentType.User && !props.user) || (type === MenuItemParentType.Guild && !props.guild) || (type === MenuItemParentType.Channel && (!props.channel || !props.guild))) + if ( + !props || + (type === MenuItemParentType.User && !props.user) || + (type === MenuItemParentType.Guild && !props.guild) || + (type === MenuItemParentType.Channel && (!props.channel || !props.guild)) + ) { return; + } const group = findGroupChildrenByChildId(childId, children); - const item = (() => { - switch (type) { - case MenuItemParentType.User: - return MenuItem(props.guildId, props.user.id, type); - case MenuItemParentType.Channel: - return MenuItem(props.guild.id, props.channel.id, type); - case MenuItemParentType.Guild: - return MenuItem(props.guild.id); - default: - return null; - } - })(); + const item = match(type) + .with(MenuItemParentType.User, () => MenuItem(props.guildId, props.user.id, type)) + .with(MenuItemParentType.Channel, () => MenuItem(props.guild.id, props.channel.id, type)) + .with(MenuItemParentType.Guild, () => MenuItem(props.guild.id)) + .otherwise(() => null); + if (item == null) return; - if (group) - group.push(item); - else if (childId === "roles" && props.guildId) - // "roles" may not be present due to the member not having any roles. In that case, add it above "Copy ID" + if (group) { + return group.push(item); + } + + // "roles" may not be present due to the member not having any roles. In that case, add it above "Copy ID" + if (childId === "roles" && props.guildId) { children.splice(-1, 0, {item}); + } }; } diff --git a/src/plugins/permissionsViewer/styles.css b/src/plugins/permissionsViewer/styles.css index 0ef961e5a9..0123f86e2b 100644 --- a/src/plugins/permissionsViewer/styles.css +++ b/src/plugins/permissionsViewer/styles.css @@ -1,20 +1,6 @@ /* User Permissions Component */ -.vc-permviewer-userperms-title-container { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 10px; - margin-bottom: 6px; -} - -.vc-permviewer-userperms-btns-container { - display: flex; - align-items: center; -} - -.vc-permviewer-userperms-sortorder-btn { - all: unset; +.vc-permviewer-user-sortorder-btn { cursor: pointer; display: flex; align-items: center; @@ -23,27 +9,17 @@ height: 24px; } -.vc-permviewer-userperms-permdetails-btn { - all: unset; - cursor: pointer; - display: flex; - align-items: center; -} +/* RolesAndUsersPermissions Component */ -.vc-permviewer-userperms-toggleperms-btn { - all: unset; - cursor: pointer; - display: flex; - align-items: center; +.vc-permviewer-modal-content { + padding: 16px 4px 16px 16px; } -/* RolesAndUsersPermissions Component */ - -.vc-permviewer-perms-title { +.vc-permviewer-modal-title { flex-grow: 1; } -.vc-permviewer-perms-no-perms { +.vc-permviewer-modal-no-perms { width: 100%; height: 100%; display: flex; @@ -52,101 +28,103 @@ text-align: center; } -.vc-permviewer-perms-container { - display: grid; - grid-template-columns: 1fr 2fr; - grid-template-areas: "list permissions"; - padding: 16px 0; +.vc-permviewer-modal-container { + width: 100%; + height: 100%; + display: flex; + gap: 8px; } -.vc-permviewer-perms-list { - grid-area: list; +.vc-permviewer-modal-list { display: flex; flex-direction: column; gap: 2px; - border-right: 2px solid var(--background-modifier-active); + padding-right: 8px; + width: 200px; } -.vc-permviewer-perms-list-item-btn { - all: unset; +.vc-permviewer-modal-list-item-btn { cursor: pointer; } -.vc-permviewer-perms-list-item { +.vc-permviewer-modal-list-item { display: flex; align-items: center; - padding: 8px 5px; - cursor: pointer; - width: 230px; + gap: 8px; + padding: 8px; border-radius: 5px; } -.vc-permviewer-perms-list-item:hover { +.vc-permviewer-modal-list-item:hover { background-color: var(--background-modifier-hover); } -.vc-permviewer-perms-list-item-active { +.vc-permviewer-modal-list-item-active { background-color: var(--background-modifier-selected); } -.vc-permviewer-perms-list-item > div { +.vc-permviewer-modal-list-item > div { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } -.vc-permviewer-perms-role-circle { +.vc-permviewer-modal-role-circle { border-radius: 50%; width: 12px; height: 12px; - margin-left: 3px; - margin-right: 11px; flex-shrink: 0; } -.vc-permviewer-perms-user-img { +.vc-permviewer-modal-role-image { + width: 20px; + height: 20px; + object-fit: contain; +} + +.vc-permviewer-modal-user-img { border-radius: 50%; width: 20px; height: 20px; - margin-right: 6px; } -.vc-permviewer-perms-perms { - grid-area: permissions; +.vc-permviewer-modal-divider { + width: 2px; + background-color: var(--background-modifier-active); +} + +.vc-permviewer-modal-perms { display: flex; flex-direction: column; - margin-left: 5px; + padding-right: 8px; } -.vc-permviewer-perms-perms-item { - position: relative; +.vc-permviewer-modal-perms-item { display: flex; align-items: center; - padding: 10px; + gap: 5px; + padding: 10px 2px 10px 10px; border-bottom: 2px solid var(--background-modifier-active); } -.vc-permviewer-perms-perms-item:last-child { +.vc-permviewer-modal-perms-item:last-child { border: 0; } -.vc-permviewer-perms-perms-item-icon { +.vc-permviewer-modal-perms-item-icon { border: 1px solid var(--background-modifier-selected); width: 24px; height: 24px; - margin-right: 5px; } -.vc-permviewer-perms-perms-item .vc-info-icon { +.vc-permviewer-modal-perms-item .vc-info-icon { color: var(--interactive-muted); + margin-left: auto; cursor: pointer; - position: absolute; - right: 0; - scale: 0.9; transition: color ease-in 0.1s; } -.vc-permviewer-perms-perms-item .vc-info-icon:hover { +.vc-permviewer-modal-perms-item .vc-info-icon:hover { color: var(--interactive-active); } @@ -167,3 +145,14 @@ background: rgb(var(--bg-overlay-color)/var(--bg-overlay-opacity-6)); border-color: var(--profile-body-border-color) } + +.vc-permviewer-granted-by-container { + max-width: 300px; + width: auto; +} + +.vc-permviewer-granted-by-content { + display: flex; + align-items: center; + gap: 4px; +} diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index 5dcc951948..0588d5e4cc 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -459,7 +459,7 @@ export type ScrollerThin = ComponentType Date: Mon, 2 Sep 2024 06:50:52 +0300 Subject: [PATCH 19/35] MutualGroupDMs: Add Mutual Groups to DM Sidebar (#2817) --- src/plugins/mutualGroupDMs/index.tsx | 77 +++++++++++++++++++--------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index a1e73cabfb..ec52b40617 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -20,7 +20,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common"; import { Channel, User } from "discord-types/general"; @@ -28,6 +28,7 @@ const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); +const ExpandableList = findComponentByCodeLazy(".mutualFriendItem]"); const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { @@ -50,6 +51,29 @@ function getMutualGDMCountText(user: User) { return `${count === 0 ? "No" : count} Mutual Group${count !== 1 ? "s" : ""}`; } +function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) { + return mutualDms.map(c => ( + { + onClose(); + SelectedChannelActionCreators.selectPrivateChannel(c.id); + }} + > + + +
+
{getGroupDMName(c)}
+
{c.recipients.length + 1} Members
+
+
+ )); +} + const IS_PATCHED = Symbol("MutualGroupDMs.Patched"); export default definePlugin({ @@ -70,6 +94,13 @@ export default definePlugin({ replace: "$1==='MUTUAL_GDMS'?$self.renderMutualGDMs(arguments[0]):$&" } ] + }, + { + find: 'section:"MUTUAL_FRIENDS"', + replacement: { + match: /\.openUserProfileModal.+?\)}\)}\)(?<=(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/, + replace: "$&,$self.renderDMPageList({user: arguments[0].user, Divider: $1, listStyle: $2.list})" + } } ], @@ -84,28 +115,9 @@ export default definePlugin({ }, renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => { - const mutualDms = useMemo(() => getMutualGroupDms(user.id), [user.id]); - - const entries = mutualDms.map(c => ( - { - onClose(); - SelectedChannelActionCreators.selectPrivateChannel(c.id); - }} - > - - -
-
{getGroupDMName(c)}
-
{c.recipients.length + 1} Members
-
-
- )); + const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]); + + const entries = renderClickableGDMs(mutualGDms, onClose); return ( ); + }), + + renderDMPageList: ErrorBoundary.wrap(({ user, Divider, listStyle }: { user: User, Divider: JSX.Element, listStyle: string; }) => { + const mutualGDms = getMutualGroupDms(user.id); + if (mutualGDms.length === 0) return null; + + const header = getMutualGDMCountText(user); + + return ( + <> + {Divider} + { })} + /> + + ); }) }); From 27e81b20db23f09ca7334cc74bc5aee841cd6705 Mon Sep 17 00:00:00 2001 From: Maddie <52103563+maddie480@users.noreply.github.com> Date: Mon, 2 Sep 2024 06:51:29 +0200 Subject: [PATCH 20/35] Allow online themes to be applied only in dark or light mode (#2701) --- src/components/VencordSettings/ThemesTab.tsx | 19 ++++++++++++++----- src/plugins/clientTheme/index.tsx | 3 +-- src/utils/quickCss.ts | 15 ++++++++++++++- src/webpack/common/stores.ts | 2 ++ src/webpack/common/types/stores.d.ts | 8 ++++++++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/components/VencordSettings/ThemesTab.tsx b/src/components/VencordSettings/ThemesTab.tsx index bb9d37894d..f718ab11f0 100644 --- a/src/components/VencordSettings/ThemesTab.tsx +++ b/src/components/VencordSettings/ThemesTab.tsx @@ -77,8 +77,16 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) { Validator This section will tell you whether your themes can successfully be loaded
- {themeLinks.map(link => ( - { + const { label, link } = (() => { + const match = /^@(light|dark) (.*)/.exec(rawLink); + if (!match) return { label: rawLink, link: rawLink }; + + const [, mode, link] = match; + return { label: `[${mode} mode only] ${link}`, link }; + })(); + + return - {link} + {label} - - ))} + ; + })}
); @@ -296,6 +304,7 @@ function ThemesTab() { Paste links to css files here One link per line + You can prefix lines with @light or @dark to toggle them based on your Discord theme Make sure to use direct links to files (raw or github.io)! diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 358bae017c..59f3d5fe2f 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -12,7 +12,7 @@ import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { Button, Forms, useStateFromStores } from "@webpack/common"; +import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); @@ -36,7 +36,6 @@ function setTheme(theme: string) { saveClientTheme({ theme }); } -const ThemeStore = findStoreLazy("ThemeStore"); const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore"); function ThemeSettings() { diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index 99f06004c9..6a18948d15 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -17,6 +17,7 @@ */ import { Settings, SettingsStore } from "@api/Settings"; +import { ThemeStore } from "@webpack/common"; let style: HTMLStyleElement; @@ -59,7 +60,18 @@ async function initThemes() { const { themeLinks, enabledThemes } = Settings; - const links: string[] = [...themeLinks]; + // "darker" and "midnight" both count as dark + const activeTheme = ThemeStore.theme === "light" ? "light" : "dark"; + + const links = themeLinks + .map(rawLink => { + const match = /^@(light|dark) (.*)/.exec(rawLink); + if (!match) return rawLink; + + const [, mode, link] = match; + return mode === activeTheme ? link : null; + }) + .filter(link => link !== null); if (IS_WEB) { for (const theme of enabledThemes) { @@ -85,6 +97,7 @@ document.addEventListener("DOMContentLoaded", () => { SettingsStore.addChangeListener("themeLinks", initThemes); SettingsStore.addChangeListener("enabledThemes", initThemes); + ThemeStore.addChangeListener(initThemes); if (!IS_WEB) VencordNative.quickCss.addThemeChangeListener(initThemes); diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 74813357a6..8579f8b922 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -53,6 +53,7 @@ export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & { }; export let EmojiStore: t.EmojiStore; +export let ThemeStore: t.ThemeStore; export let WindowStore: t.WindowStore; export let DraftStore: t.DraftStore; @@ -84,3 +85,4 @@ waitForStore("GuildChannelStore", m => GuildChannelStore = m); waitForStore("MessageStore", m => MessageStore = m); waitForStore("WindowStore", m => WindowStore = m); waitForStore("EmojiStore", m => EmojiStore = m); +waitForStore("ThemeStore", m => ThemeStore = m); diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 037b2d81c5..9ca7dfc94b 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -220,6 +220,14 @@ export class GuildStore extends FluxStore { getAllGuildRoles(): Record>; } +export class ThemeStore extends FluxStore { + theme: "light" | "dark" | "darker" | "midnight"; + darkSidebar: boolean; + isSystemThemeAvailable: boolean; + systemPrefersColorScheme: "light" | "dark"; + systemTheme: null; +} + export type useStateFromStores = ( stores: t.FluxStore[], mapper: () => T, From accfc15125c5a611cb5835815a6e36d6bd4e8f89 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:54:16 -0300 Subject: [PATCH 21/35] Ban ts-pattern normal import --- scripts/build/common.mjs | 1 + src/plugins/permissionsViewer/index.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 247557e3c5..e88f1e2b95 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -334,5 +334,6 @@ export const commonRendererPlugins = [ banImportPlugin(builtinModuleRegex, "Cannot import node inbuilt modules in browser code. You need to use a native.ts file"), banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"), + banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"), ...commonOpts.plugins ]; diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index 44dfd5f54a..ca28f845f5 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -73,7 +73,8 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { action={() => { const guild = GuildStore.getGuild(guildId); - const { permissions, header }: { permissions: RoleOrUserPermission[], header: string; } = match(type) + const { permissions, header } = match(type) + .returnType<{ permissions: RoleOrUserPermission[], header: string; }>() .with(MenuItemParentType.User, () => { const member = GuildMemberStore.getMember(guildId, id!); From c51d7b8fb4ea38f0ada2fe210f21263d2f2dd711 Mon Sep 17 00:00:00 2001 From: June Park Date: Mon, 2 Sep 2024 17:09:45 +0900 Subject: [PATCH 22/35] ReviewDB: Fix wording in server reviews (#2826) --- src/plugins/reviewDB/components/ReviewModal.tsx | 7 +++++-- src/plugins/reviewDB/components/ReviewsView.tsx | 9 ++++++--- src/plugins/reviewDB/index.tsx | 8 ++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/plugins/reviewDB/components/ReviewModal.tsx b/src/plugins/reviewDB/components/ReviewModal.tsx index e12a98acd4..71ac021f0d 100644 --- a/src/plugins/reviewDB/components/ReviewModal.tsx +++ b/src/plugins/reviewDB/components/ReviewModal.tsx @@ -22,12 +22,13 @@ import { useForceUpdater } from "@utils/react"; import { Paginator, Text, useRef, useState } from "@webpack/common"; import { Auth } from "../auth"; +import { ReviewType } from "../entities"; import { Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; import { cl } from "../utils"; import ReviewComponent from "./ReviewComponent"; import ReviewsView, { ReviewsInputComponent } from "./ReviewsView"; -function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; modalKey: string, discordId: string; name: string; }) { +function Modal({ modalProps, modalKey, discordId, name, type }: { modalProps: any; modalKey: string, discordId: string; name: string; type: ReviewType; }) { const [data, setData] = useState(); const [signal, refetch] = useForceUpdater(true); const [page, setPage] = useState(1); @@ -58,6 +59,7 @@ function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; mod onFetchReviews={setData} scrollToTop={() => ref.current?.scrollTo({ top: 0, behavior: "smooth" })} hideOwnReview + type={type} />
@@ -95,7 +97,7 @@ function Modal({ modalProps, modalKey, discordId, name }: { modalProps: any; mod ); } -export function openReviewsModal(discordId: string, name: string) { +export function openReviewsModal(discordId: string, name: string, type: ReviewType) { const modalKey = "vc-rdb-modal-" + Date.now(); openModal(props => ( @@ -104,6 +106,7 @@ export function openReviewsModal(discordId: string, name: string) { modalProps={props} discordId={discordId} name={name} + type={type} /> ), { modalKey }); } diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index 76a0be4756..7a7d8d0206 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -21,7 +21,7 @@ import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpa import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Auth, authorize } from "../auth"; -import { Review } from "../entities"; +import { Review, ReviewType } from "../entities"; import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi"; import { settings } from "../settings"; import { cl, showToast } from "../utils"; @@ -45,6 +45,7 @@ interface Props extends UserProps { page?: number; scrollToTop?(): void; hideOwnReview?: boolean; + type: ReviewType; } export default function ReviewsView({ @@ -56,6 +57,7 @@ export default function ReviewsView({ page = 1, showInput = false, hideOwnReview = false, + type, }: Props) { const [signal, refetch] = useForceUpdater(true); @@ -80,6 +82,7 @@ export default function ReviewsView({ reviews={reviewData!.reviews} hideOwnReview={hideOwnReview} profileId={discordId} + type={type} /> {showInput && ( @@ -94,7 +97,7 @@ export default function ReviewsView({ ); } -function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; }) { +function ReviewList({ refetch, reviews, hideOwnReview, profileId, type }: { refetch(): void; reviews: Review[]; hideOwnReview: boolean; profileId: string; type: ReviewType; }) { const myId = UserStore.getCurrentUser().id; return ( @@ -111,7 +114,7 @@ function ReviewList({ refetch, reviews, hideOwnReview, profileId }: { refetch(): {reviews?.length === 0 && ( - Looks like nobody reviewed this user yet. You could be the first! + Looks like nobody reviewed this {type === ReviewType.User ? "user" : "server"} yet. You could be the first! )}
diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index caf9bacbaf..1164a2c5bb 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -30,7 +30,7 @@ import { Guild, User } from "discord-types/general"; import { Auth, initAuth, updateAuth } from "./auth"; import { openReviewsModal } from "./components/ReviewModal"; -import { NotificationType } from "./entities"; +import { NotificationType, ReviewType } from "./entities"; import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; import { settings } from "./settings"; import { showToast } from "./utils"; @@ -44,7 +44,7 @@ const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { gu label="View Reviews" id="vc-rdb-server-reviews" icon={OpenExternalIcon} - action={() => openReviewsModal(guild.id, guild.name)} + action={() => openReviewsModal(guild.id, guild.name, ReviewType.Server)} /> ); }; @@ -56,7 +56,7 @@ const userContextPatch: NavContextMenuPatchCallback = (children, { user }: { use label="View Reviews" id="vc-rdb-user-reviews" icon={OpenExternalIcon} - action={() => openReviewsModal(user.id, user.username)} + action={() => openReviewsModal(user.id, user.username, ReviewType.User)} /> ); }; @@ -157,7 +157,7 @@ export default definePlugin({ return (
) } - - + ); }, { noop: true }), }); From 244cb26c328828503835c680fa294917cb7d5f9b Mon Sep 17 00:00:00 2001 From: ElectricSteve <96793824+electricsteve@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:00:49 +0200 Subject: [PATCH 28/35] Dearrow: Add option to not dearrow by default (#2818) --- src/plugins/dearrow/index.tsx | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 5fb4382566..10175c8468 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -46,7 +46,7 @@ const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/; async function embedDidMount(this: Component) { try { const { embed } = this.props; - const { replaceElements } = settings.store; + const { replaceElements, dearrowByDefault } = settings.store; if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return; @@ -63,18 +63,22 @@ async function embedDidMount(this: Component) { if (!hasTitle && !hasThumb) return; + embed.dearrow = { - enabled: true + enabled: dearrowByDefault }; if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) { - embed.dearrow.oldTitle = embed.rawTitle; - embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2"); - } + const replacementTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2"); + embed.dearrow.oldTitle = dearrowByDefault ? embed.rawTitle : replacementTitle; + if (dearrowByDefault) embed.rawTitle = replacementTitle; + } if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) { - embed.dearrow.oldThumb = embed.thumbnail.proxyURL; - embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; + const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; + + embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL; + if (embed.thumbnail.proxyURL) embed.thumbnail.proxyURL = replacementProxyURL; } this.forceUpdate(); @@ -96,6 +100,7 @@ function DearrowButton({ component }: { component: Component; }) { className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")} onClick={() => { const { enabled, oldThumb, oldTitle } = embed.dearrow; + settings.store.dearrowByDefault = !enabled; embed.dearrow.enabled = !enabled; if (oldTitle) { embed.dearrow.oldTitle = embed.rawTitle; @@ -153,6 +158,12 @@ const settings = definePluginSettings({ { label: "Titles", value: ReplaceElements.ReplaceTitlesOnly }, { label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly }, ], + }, + dearrowByDefault: { + description: "Dearrow videos automatically", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: false } }); From 7333f40db697bc5f06694a0b1d462468b0605529 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:16:45 -0300 Subject: [PATCH 29/35] Dearrow: Fix thumbnails with default option --- src/plugins/dearrow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 10175c8468..b7e90e0966 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -78,7 +78,7 @@ async function embedDidMount(this: Component) { const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL; - if (embed.thumbnail.proxyURL) embed.thumbnail.proxyURL = replacementProxyURL; + if (dearrowByDefault) embed.thumbnail.proxyURL = replacementProxyURL; } this.forceUpdate(); From 99cd423efbaf4da9c5d1c7bf23061d933eb99d18 Mon Sep 17 00:00:00 2001 From: Haruka <96925398+nakoyasha@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:26:08 +0300 Subject: [PATCH 30/35] fix crashing on canary when searching slash commands (#2844) --- src/api/Commands/index.ts | 2 ++ src/api/Commands/types.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/api/Commands/index.ts b/src/api/Commands/index.ts index ef4db171c8..5e3fc8f4de 100644 --- a/src/api/Commands/index.ts +++ b/src/api/Commands/index.ts @@ -138,6 +138,8 @@ export function registerCommand(command: C, plugin: string) { throw new Error(`Command '${command.name}' already exists.`); command.isVencordCommand = true; + command.untranslatedName ??= command.name; + command.untranslatedDescription ??= command.description; command.id ??= `-${BUILT_IN.length + 1}`; command.applicationId ??= "-1"; // BUILT_IN; command.type ??= ApplicationCommandType.CHAT_INPUT; diff --git a/src/api/Commands/types.ts b/src/api/Commands/types.ts index bd349e250a..70b73775a7 100644 --- a/src/api/Commands/types.ts +++ b/src/api/Commands/types.ts @@ -93,8 +93,10 @@ export interface Command { isVencordCommand?: boolean; name: string; + untranslatedName?: string; displayName?: string; description: string; + untranslatedDescription?: string; displayDescription?: string; options?: Option[]; From be02baffaa8c46d499b90c6e2f638e4fe7aa28bd Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 5 Sep 2024 02:12:09 +0200 Subject: [PATCH 31/35] MemberCount: fix null safety and improve types for PopoutPosition Co-Authored-By: fres621 <126067139+fres621@users.noreply.github.com> --- src/plugins/memberCount/MemberCount.tsx | 4 ++-- .../memberCount/OnlineMemberCountStore.ts | 8 ++++---- src/plugins/memberCount/index.tsx | 6 +++--- src/utils/discord.tsx | 6 +++--- src/webpack/common/types/components.d.ts | 18 ++++++++++-------- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/plugins/memberCount/MemberCount.tsx b/src/plugins/memberCount/MemberCount.tsx index 084e7ecc46..0a3f5e620e 100644 --- a/src/plugins/memberCount/MemberCount.tsx +++ b/src/plugins/memberCount/MemberCount.tsx @@ -14,7 +14,7 @@ import { OnlineMemberCountStore } from "./OnlineMemberCountStore"; export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) { const currentChannel = useStateFromStores([SelectedChannelStore], () => getCurrentChannel()); - const guildId = isTooltip ? tooltipGuildId! : currentChannel.guild_id; + const guildId = isTooltip ? tooltipGuildId! : currentChannel?.guild_id; const totalCount = useStateFromStores( [GuildMemberCountStore], @@ -33,7 +33,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t const threadGroups = useStateFromStores( [ThreadMemberListStore], - () => ThreadMemberListStore.getMemberListSections(currentChannel.id) + () => ThreadMemberListStore.getMemberListSections(currentChannel?.id) ); if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) { diff --git a/src/plugins/memberCount/OnlineMemberCountStore.ts b/src/plugins/memberCount/OnlineMemberCountStore.ts index 8790f5e297..d74bea2a0c 100644 --- a/src/plugins/memberCount/OnlineMemberCountStore.ts +++ b/src/plugins/memberCount/OnlineMemberCountStore.ts @@ -15,8 +15,8 @@ export const OnlineMemberCountStore = proxyLazy(() => { const onlineMemberMap = new Map(); class OnlineMemberCountStore extends Flux.Store { - getCount(guildId: string) { - return onlineMemberMap.get(guildId); + getCount(guildId?: string) { + return onlineMemberMap.get(guildId!); } async _ensureCount(guildId: string) { @@ -25,8 +25,8 @@ export const OnlineMemberCountStore = proxyLazy(() => { await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id); } - ensureCount(guildId: string) { - if (onlineMemberMap.has(guildId)) return; + ensureCount(guildId?: string) { + if (!guildId || onlineMemberMap.has(guildId)) return; preloadQueue.push(() => this._ensureCount(guildId) diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index 7e591357df..85dcc4b2d0 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -28,12 +28,12 @@ import { FluxStore } from "@webpack/types"; import { MemberCount } from "./MemberCount"; -export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; }; +export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; }; export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & { - getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; }; + 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[]; }; }; + getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; }; }; diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index c1d1e2253b..4c7cc38a0a 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -18,7 +18,7 @@ import { MessageObject } from "@api/MessageEvents"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; -import { Guild, Message, User } from "discord-types/general"; +import { Channel, Guild, Message, User } from "discord-types/general"; import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; @@ -54,12 +54,12 @@ export async function openInviteModal(code: string) { }); } -export function getCurrentChannel() { +export function getCurrentChannel(): Channel | undefined { return ChannelStore.getChannel(SelectedChannelStore.getChannelId()); } export function getCurrentGuild(): Guild | undefined { - return GuildStore.getGuild(getCurrentChannel()?.guild_id); + return GuildStore.getGuild(getCurrentChannel()?.guild_id!); } export function openPrivateChannel(userId: string) { diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index 0588d5e4cc..6c76233393 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -91,7 +91,7 @@ export type Tooltip = ComponentType<{ /** Tooltip.Colors.BLACK */ color?: string; /** TooltipPositions.TOP */ - position?: string; + position?: PopoutPosition; tooltipClassName?: string; tooltipContentClassName?: string; @@ -110,7 +110,7 @@ export type TooltipContainer = ComponentType & { From 40512d729490091f61d645cc2e9eded9390ef26d Mon Sep 17 00:00:00 2001 From: Nyako <24845294+nyakowint@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:16:41 -0400 Subject: [PATCH 32/35] XSOverlay: fix profile images (#2788) Fixes https://github.com/Vendicated/Vencord/issues/2787 Co-authored-by: v --- src/plugins/xsOverlay/index.tsx | 42 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/plugins/xsOverlay/index.tsx b/src/plugins/xsOverlay/index.tsx index 12dbf6b616..4c5b7a80ea 100644 --- a/src/plugins/xsOverlay/index.tsx +++ b/src/plugins/xsOverlay/index.tsx @@ -86,7 +86,7 @@ interface NotificationObject { title: string; content: string; useBase64Icon: boolean; - icon: ArrayBuffer | string; + icon: string; sourceApp: string; } @@ -320,23 +320,29 @@ function shouldIgnoreForChannelType(channel: Channel) { } function sendMsgNotif(titleString: string, content: string, message: Message) { - fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => { - const msgData: NotificationObject = { - type: 1, - timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout, - height: calculateHeight(content), - opacity: settings.store.opacity, - volume: settings.store.volume, - audioPath: settings.store.soundPath, - title: titleString, - content: content, - useBase64Icon: true, - icon: new TextDecoder().decode(result), - sourceApp: "Vencord" - }; - - sendToOverlay(msgData); - }); + fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`) + .then(response => response.blob()) + .then(blob => new Promise(resolve => { + const r = new FileReader(); + r.onload = () => resolve((r.result as string).split(",")[1]); + r.readAsDataURL(blob); + })).then(result => { + const msgData: NotificationObject = { + type: 1, + timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout, + height: calculateHeight(content), + opacity: settings.store.opacity, + volume: settings.store.volume, + audioPath: settings.store.soundPath, + title: titleString, + content: content, + useBase64Icon: true, + icon: result, + sourceApp: "Vencord" + }; + + sendToOverlay(msgData); + }); } function sendOtherNotif(content: string, titleString: string) { From e6994e1946ea2b34c0b6b842d3a323beb9cfa3ef Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 5 Sep 2024 02:24:10 +0200 Subject: [PATCH 33/35] MentionAvatars: Fix compatibility with ServerInfo plugin --- src/plugins/mentionAvatars/styles.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/mentionAvatars/styles.css b/src/plugins/mentionAvatars/styles.css index 64eb41416c..cf6e2c14c0 100644 --- a/src/plugins/mentionAvatars/styles.css +++ b/src/plugins/mentionAvatars/styles.css @@ -9,3 +9,8 @@ .vc-mentionAvatars-role-icon { margin: 0 2px 0.2rem 4px; } + +/** don't display inside the ServerInfo modal owner mention */ +.vc-gp-owner .vc-mentionAvatars-icon { + display: none; +} From 61d3c08f1f6b9c44bd193aab4f8568734ba60ad8 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 5 Sep 2024 02:25:27 +0200 Subject: [PATCH 34/35] bump to v1.10.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65a2f4512b..e65e1b0a76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.9", + "version": "1.10.1", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 8890c8c6b4201121c559e671e0110b45937467c2 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 5 Sep 2024 02:33:07 +0200 Subject: [PATCH 35/35] fix Commands, Badges, RoleColorEverywhere --- src/api/Commands/index.ts | 7 ++++--- src/plugins/_api/badges/fixBadgeOverflow.css | 3 --- src/plugins/_api/badges/index.tsx | 4 +--- src/plugins/roleColorEverywhere/index.tsx | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 src/plugins/_api/badges/fixBadgeOverflow.css diff --git a/src/api/Commands/index.ts b/src/api/Commands/index.ts index 5e3fc8f4de..e5803ba026 100644 --- a/src/api/Commands/index.ts +++ b/src/api/Commands/index.ts @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { Logger } from "@utils/Logger"; import { makeCodeblock } from "@utils/text"; import { sendBotMessage } from "./commandHelpers"; @@ -46,10 +47,10 @@ export let RequiredMessageOption: Option = ReqPlaceholder; export const _init = function (cmds: Command[]) { try { BUILT_IN = cmds; - OptionalMessageOption = cmds.find(c => c.name === "shrug")!.options![0]; - RequiredMessageOption = cmds.find(c => c.name === "me")!.options![0]; + OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0]; + RequiredMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "me")!.options![0]; } catch (e) { - console.error("Failed to load CommandsApi"); + new Logger("CommandsAPI").error("Failed to load CommandsApi", e, " - cmds is", cmds); } return cmds; } as never; diff --git a/src/plugins/_api/badges/fixBadgeOverflow.css b/src/plugins/_api/badges/fixBadgeOverflow.css deleted file mode 100644 index 348d0ff385..0000000000 --- a/src/plugins/_api/badges/fixBadgeOverflow.css +++ /dev/null @@ -1,3 +0,0 @@ -[class*="profileBadges"] { - flex: none; -} diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index cf00a0e292..c44d98b902 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -import "./fixBadgeOverflow.css"; - import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import DonateButton from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -79,7 +77,7 @@ export default definePlugin({ replace: "...$1.props,$& $1.image??" }, { - match: /(?<=text:(\i)\.description,.{0,50})children:/, + match: /(?<=text:(\i)\.description,.{0,200})children:/, replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :" }, // conditionally override their onClick with badge.onClick if it exists diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 3e7d216b71..b5f66c0982 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -57,7 +57,7 @@ export default definePlugin({ patches: [ // Chat Mentions { - find: 'location:"UserMention', + find: ".USER_MENTION)", replacement: [ { match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,