Merge branch 'dev' into patcher-rewrite

This commit is contained in:
Nuckyz 2025-01-30 20:45:13 -03:00
commit 8cd8334ada
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
18 changed files with 87 additions and 103 deletions

View file

@ -16,6 +16,7 @@ export default definePlugin({
{ {
find: '"sticker")', find: '"sticker")',
replacement: { replacement: {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /return\((!)?\i\.\i(?:\|\||&&)(?=\(\i\.isDM.+?(\i)\.push)/, match: /return\((!)?\i\.\i(?:\|\||&&)(?=\(\i\.isDM.+?(\i)\.push)/,
replace: (m, not, children) => not replace: (m, not, children) => not
? `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),true)&&` ? `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),true)&&`

View file

@ -37,12 +37,13 @@ export default definePlugin({
{ {
find: ".handleSendMessage,onResize", find: ".handleSendMessage,onResize",
replacement: { replacement: {
// FIXME: Simplify this change once all branches share the same code
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); // props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid) // Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
match: /(\{openWarningPopout:.{0,100}type:this.props.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);)(?<=\)\(({.+?})\)\.then.+?)/, match: /(\{openWarningPopout:.{0,100}type:this.props.chatInputType.+?\.then\((?:async )?)(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);)(?<=\)\(({.+?})\)\.then.+?)/,
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true }; // props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" + replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
`${rest1}async ${rest2}` + `${rest1}${rest1.includes("async") ? "" : "async "}${rest2}` +
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` + `if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
"return{shouldClear:false,shouldRefocus:true};" "return{shouldClear:false,shouldRefocus:true};"
} }

View file

@ -65,6 +65,7 @@ export default definePlugin({
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
}, },
{ {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?(?:function\(\){return |\(\)=>))\2/, match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?(?:function\(\){return |\(\)=>))\2/,
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
} }

View file

@ -44,6 +44,7 @@ export default definePlugin({
{ {
find: ".selectPreviousCommandOption(", find: ".selectPreviousCommandOption(",
replacement: { replacement: {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(?<=(\i)\.which(?:!==|===)\i\.\i.ENTER(\|\||&&)).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=(?:\|\||&&)\(\i\.preventDefault)/, match: /(?<=(\i)\.which(?:!==|===)\i\.\i.ENTER(\|\||&&)).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=(?:\|\||&&)\(\i\.preventDefault)/,
replace: (_, event, condition, codeblock) => `${condition === "||" ? "!" : ""}$self.shouldSubmit(${event},${codeblock})` replace: (_, event, condition, codeblock) => `${condition === "||" ? "!" : ""}$self.shouldSubmit(${event},${codeblock})`
} }

View file

@ -256,6 +256,7 @@ export default definePlugin({
}, },
{ {
// Disallow the emoji for premium locked if the intention doesn't allow it // Disallow the emoji for premium locked if the intention doesn't allow it
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(!)?(\i\.\i\.canUseEmojisEverywhere\(\i\))/, match: /(!)?(\i\.\i\.canUseEmojisEverywhere\(\i\))/,
replace: (m, not) => not replace: (m, not) => not
? `(${m}&&!${IS_BYPASSEABLE_INTENTION})` ? `(${m}&&!${IS_BYPASSEABLE_INTENTION})`

View file

@ -23,11 +23,11 @@ import definePlugin, { OptionType } from "@utils/types";
import { useMemo } from "@webpack/common"; import { useMemo } from "@webpack/common";
// Calculate a CSS color string based on the user ID // Calculate a CSS color string based on the user ID
function calculateNameColorForUser(id: string) { function calculateNameColorForUser(id?: string) {
const { lightness } = settings.use(["lightness"]); const { lightness } = settings.use(["lightness"]);
const idHash = useMemo(() => h64(id), [id]); const idHash = useMemo(() => id ? h64(id) : null, [id]);
return `hsl(${idHash % 360n}, 100%, ${lightness}%)`; return idHash && `hsl(${idHash % 360n}, 100%, ${lightness}%)`;
} }
const settings = definePluginSettings({ const settings = definePluginSettings({
@ -70,16 +70,10 @@ export default definePlugin({
calculateNameColorForMessageContext(context: any) { calculateNameColorForMessageContext(context: any) {
const id = context?.message?.author?.id; const id = context?.message?.author?.id;
if (id == null) {
return null;
}
return calculateNameColorForUser(id); return calculateNameColorForUser(id);
}, },
calculateNameColorForListContext(context: any) { calculateNameColorForListContext(context: any) {
const id = context?.user?.id; const id = context?.user?.id;
if (id == null) {
return null;
}
return calculateNameColorForUser(id); return calculateNameColorForUser(id);
} }
}); });

View file

@ -89,7 +89,7 @@ export default definePlugin({
settings, settings,
async start() { async start() {
// TODO: Remove DataStore tags migration once enough time has passed // TODO(OptionType.CUSTOM Related): Remove DataStore tags migration once enough time has passed
const oldTags = await DataStore.get<Tag[]>(DATA_KEY); const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
if (oldTags != null) { if (oldTags != null) {
// @ts-ignore // @ts-ignore

View file

@ -1,42 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 <https://www.gnu.org/licenses/>.
*/
import { getUserSettingLazy } from "@api/UserSettings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
const DisableStreamPreviews = getUserSettingLazy<boolean>("voiceAndVideo", "disableStreamPreviews")!;
// @TODO: Delete this plugin in the future
export default definePlugin({
name: "NoScreensharePreview",
description: "Disables screenshare previews from being sent.",
authors: [Devs.Nuckyz],
start() {
if (!DisableStreamPreviews.getSetting()) {
DisableStreamPreviews.updateSetting(true);
}
},
stop() {
if (DisableStreamPreviews.getSetting()) {
DisableStreamPreviews.updateSetting(false);
}
}
});

View file

@ -100,6 +100,7 @@ export default definePlugin({
replace: "true" replace: "true"
}, },
{ {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(!)?\(0,\i\.isDesktop\)\(\)/, match: /(!)?\(0,\i\.isDesktop\)\(\)/,
replace: (_, not) => not ? "false" : "true" replace: (_, not) => not ? "false" : "true"
} }

View file

@ -46,6 +46,7 @@ export default definePlugin({
find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}", find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}",
replacement: [ replacement: [
{ {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /{(?:\i:(?:function\(\){return |\(\)=>)\i}?,?){2}}/, match: /{(?:\i:(?:function\(\){return |\(\)=>)\i}?,?){2}}/,
replace: m => m.replaceAll(canonicalizeMatch(/(function\(\){return |\(\)=>)\i/g), "$1()=>Promise.resolve(true)") replace: m => m.replaceAll(canonicalizeMatch(/(function\(\){return |\(\)=>)\i/g), "$1()=>Promise.resolve(true)")
} }

View file

@ -155,7 +155,7 @@ export function moveChannel(channelId: string, direction: -1 | 1) {
swapElementsInArray(category.channels, a, b); swapElementsInArray(category.channels, a, b);
} }
// TODO: Remove DataStore PinnedDms migration once enough time has passed // TODO(OptionType.CUSTOM Related): Remove DataStore PinnedDms migration once enough time has passed
async function migrateData() { async function migrateData() {
if (Settings.plugins.PinDMs.dmSectioncollapsed != null) { if (Settings.plugins.PinDMs.dmSectioncollapsed != null) {
settings.store.dmSectionCollapsed = Settings.plugins.PinDMs.dmSectioncollapsed; settings.store.dmSectionCollapsed = Settings.plugins.PinDMs.dmSectioncollapsed;

View file

@ -196,7 +196,7 @@ function nextReply(isUp: boolean) {
channel, channel,
message, message,
shouldMention: shouldMention(message), shouldMention: shouldMention(message),
showMentionToggle: channel.isPrivate() && message.author.id !== meId, showMentionToggle: !channel.isPrivate() && message.author.id !== meId,
_isQuickReply: true _isQuickReply: true
}); });
ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS"); ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS");

View file

@ -108,6 +108,7 @@ export default definePlugin({
}, },
{ {
// Prevent Discord from trying to connect to hidden voice channels // Prevent Discord from trying to connect to hidden voice channels
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(?=(\|\||&&)\i\.\i\.selectVoiceChannel\((\i)\.id\))/, match: /(?=(\|\||&&)\i\.\i\.selectVoiceChannel\((\i)\.id\))/,
replace: (_, condition, channel) => condition === "||" replace: (_, condition, channel) => condition === "||"
? `||$self.isHiddenChannel(${channel})` ? `||$self.isHiddenChannel(${channel})`
@ -124,6 +125,7 @@ export default definePlugin({
{ {
find: ".AUDIENCE),{isSubscriptionGated", find: ".AUDIENCE),{isSubscriptionGated",
replacement: { replacement: {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(!)?(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/, match: /(!)?(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/,
replace: (m, not, channel) => not replace: (m, not, channel) => not
? `${m}&&!$self.isHiddenChannel(${channel})` ? `${m}&&!$self.isHiddenChannel(${channel})`
@ -177,6 +179,7 @@ export default definePlugin({
}, },
// Make voice channels also appear as muted if they are muted // Make voice channels also appear as muted if they are muted
{ {
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)(if\()?(\i)(?:\)return |\?)(\i\.MUTED)/, match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)(if\()?(\i)(?:\)return |\?)(\i\.MUTED)/,
replace: (_, otherClasses, isIf, isMuted, mutedClassExpression) => isIf replace: (_, otherClasses, isIf, isMuted, mutedClassExpression) => isIf
? `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return ""` ? `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return ""`
@ -190,6 +193,7 @@ export default definePlugin({
{ {
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden // Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle, predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
match: /(?<=\.LOCKED(?:;if\(|:))(?<={channel:(\i).+?)/, match: /(?<=\.LOCKED(?:;if\(|:))(?<={channel:(\i).+?)/,
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&` replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
}, },

View file

@ -244,7 +244,7 @@ export default definePlugin({
}, },
async start() { async start() {
// TODO: Remove DataStore rules migrations once enough time has passed // TODO(OptionType.CUSTOM Related): Remove DataStore rules migrations once enough time has passed
const oldStringRules = await DataStore.get<Rule[]>(STRING_RULES_KEY); const oldStringRules = await DataStore.get<Rule[]>(STRING_RULES_KEY);
if (oldStringRules != null) { if (oldStringRules != null) {
settings.store.stringRules = oldStringRules; settings.store.stringRules = oldStringRules;

View file

@ -100,6 +100,13 @@ function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: s
{props => ( {props => (
<div className="vc-typing-indicator" {...props}> <div className="vc-typing-indicator" {...props}>
{((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && ( {((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && (
<div
onClick={e => {
e.stopPropagation();
e.preventDefault();
}}
onKeyPress={e => e.stopPropagation()}
>
<UserSummaryItem <UserSummaryItem
users={typingUsersArray.map(id => UserStore.getUser(id))} users={typingUsersArray.map(id => UserStore.getUser(id))}
guildId={guildId} guildId={guildId}
@ -110,6 +117,7 @@ function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: s
size={16} size={16}
className="vc-typing-indicator-avatars" className="vc-typing-indicator-avatars"
/> />
</div>
)} )}
{((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && ( {((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && (
<div className="vc-typing-indicator-dots"> <div className="vc-typing-indicator-dots">

View file

@ -93,7 +93,7 @@ function makeRenderMoreUsers(users: User[]) {
}; };
} }
function handleClickAvatar(event: React.MouseEvent<HTMLElement, MouseEvent>) { function handleClickAvatar(event: React.UIEvent<HTMLElement, Event>) {
event.stopPropagation(); event.stopPropagation();
} }
@ -165,7 +165,7 @@ export default definePlugin({
<div <div
style={{ marginLeft: "0.5em", transform: "scale(0.9)" }} style={{ marginLeft: "0.5em", transform: "scale(0.9)" }}
> >
<div onClick={handleClickAvatar}> <div onClick={handleClickAvatar} onKeyPress={handleClickAvatar}>
<UserSummaryItem <UserSummaryItem
users={users} users={users}
guildId={ChannelStore.getChannel(message.channel_id)?.guild_id} guildId={ChannelStore.getChannel(message.channel_id)?.guild_id}

View file

@ -12,7 +12,7 @@ import { PatchReplacement } from "@utils/types";
import { traceFunctionWithResults } from "../debug/Tracer"; import { traceFunctionWithResults } from "../debug/Tracer";
import { patches } from "../plugins"; import { patches } from "../plugins";
import { _initWebpack, AnyModuleFactory, AnyWebpackRequire, factoryListeners, findModuleId, ModuleExports, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from "."; import { _initWebpack, _shouldIgnoreModule, AnyModuleFactory, AnyWebpackRequire, factoryListeners, findModuleId, ModuleExports, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from ".";
const logger = new Logger("WebpackInterceptor", "#8caaee"); const logger = new Logger("WebpackInterceptor", "#8caaee");
@ -327,33 +327,10 @@ function wrapAndPatchFactory(id: PropertyKey, originalFactory: AnyModuleFactory)
exports = module.exports; exports = module.exports;
if (exports == null) return factoryReturn; if (exports == null) return factoryReturn;
// There are (at the time of writing) 11 modules exporting the window
// Make these non enumerable to improve webpack search performance
if (typeof require === "function") { if (typeof require === "function") {
let shouldMakeNonEnumerable = false; const shouldIgnoreModule = _shouldIgnoreModule(exports);
nonEnumerableChecking: { if (shouldIgnoreModule) {
// There are (at the time of writing) 11 modules exporting the window,
// and also modules exporting DOMTokenList, which breaks webpack finding
// Make these non enumerable to improve search performance and avoid erros
if (exports === window || exports[Symbol.toStringTag] === "DOMTokenList") {
shouldMakeNonEnumerable = true;
break nonEnumerableChecking;
}
if (typeof exports !== "object") {
break nonEnumerableChecking;
}
for (const exportKey in exports) {
if (exports[exportKey] === window || exports[exportKey]?.[Symbol.toStringTag] === "DOMTokenList") {
shouldMakeNonEnumerable = true;
break nonEnumerableChecking;
}
}
}
if (shouldMakeNonEnumerable) {
if (require.c != null) { if (require.c != null) {
Object.defineProperty(require.c, id, { Object.defineProperty(require.c, id, {
value: require.c[id], value: require.c[id],

View file

@ -71,13 +71,16 @@ export const filters = {
componentByCode: (...code: CodeFilter): FilterFn => { componentByCode: (...code: CodeFilter): FilterFn => {
const filter = filters.byCode(...code); const filter = filters.byCode(...code);
return m => { return m => {
if (filter(m)) return true; let inner = m;
if (!m.$$typeof) return false;
if (m.type) while (inner != null) {
return m.type.render if (filter(inner)) return true;
? filter(m.type.render) // memo + forwardRef else if (!inner.$$typeof) return false;
: filter(m.type); // memo else if (inner.type) inner = inner.type; // memos
if (m.render) return filter(m.render); // forwardRef else if (inner.render) inner = inner.render; // forwardRefs
else return false;
}
return false; return false;
}; };
} }
@ -104,6 +107,38 @@ export function _initWebpack(webpackRequire: WebpackRequire) {
}); });
} }
// Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too
const TypedArray = Object.getPrototypeOf(Int8Array);
function _shouldIgnoreValue(value: any) {
if (value == null) return true;
if (value === window) return true;
if (value === document || value === document.documentElement) return true;
if (value[Symbol.toStringTag] === "DOMTokenList") return true;
if (value instanceof TypedArray) return true;
return false;
}
export function _shouldIgnoreModule(exports: any) {
if (_shouldIgnoreValue(exports)) {
return true;
}
if (typeof exports !== "object") {
return false;
}
let allNonEnumerable = true;
for (const exportKey in exports) {
if (!_shouldIgnoreValue(exports[exportKey])) {
allNonEnumerable = false;
}
}
return allNonEnumerable;
}
let devToolsOpen = false; let devToolsOpen = false;
if (IS_DEV && IS_DISCORD_DESKTOP) { if (IS_DEV && IS_DISCORD_DESKTOP) {
// At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed // At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed
@ -164,7 +199,8 @@ export function findAll(filter: FilterFn) {
if (filter(mod.exports)) if (filter(mod.exports))
ret.push(mod.exports); ret.push(mod.exports);
else if (typeof mod.exports !== "object")
if (typeof mod.exports !== "object")
continue; continue;
for (const nestedMod in mod.exports) { for (const nestedMod in mod.exports) {