-
+
+ {((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && (
+
UserStore.getUser(id))}
+ guildId={guildId}
+ renderIcon={false}
+ max={3}
+ showDefaultAvatarsForNullUsers
+ showUserPopout
+ size={16}
+ className="vc-typing-indicator-avatars"
+ />
+ )}
+ {((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && (
+
+
+
+ )}
)}
@@ -119,13 +140,22 @@ const settings = definePluginSettings({
type: OptionType.BOOLEAN,
description: "Whether to show the typing indicator for blocked users.",
default: false
+ },
+ indicatorMode: {
+ type: OptionType.SELECT,
+ description: "How should the indicator be displayed?",
+ options: [
+ { label: "Avatars and animated dots", value: IndicatorMode.Dots | IndicatorMode.Avatars, default: true },
+ { label: "Animated dots", value: IndicatorMode.Dots },
+ { label: "Avatars", value: IndicatorMode.Avatars },
+ ],
}
});
export default definePlugin({
name: "TypingIndicator",
description: "Adds an indicator if someone is typing on a channel.",
- authors: [Devs.Nuckyz, Devs.fawn],
+ authors: [Devs.Nuckyz, Devs.fawn, Devs.Sqaaakoi],
settings,
patches: [
diff --git a/src/plugins/typingIndicator/style.css b/src/plugins/typingIndicator/style.css
new file mode 100644
index 000000000..d92ef0f1e
--- /dev/null
+++ b/src/plugins/typingIndicator/style.css
@@ -0,0 +1,18 @@
+.vc-typing-indicator {
+ display: flex;
+ align-items: center;
+ height: 20px;
+}
+
+.vc-typing-indicator-avatars {
+ margin-left: 6px;
+}
+
+.vc-typing-indicator-dots {
+ margin-left: 6px;
+ height: 16px;
+ display: flex;
+ align-items: center;
+ z-index: 0;
+ cursor: pointer;
+}
diff --git a/src/plugins/unlockedAvatarZoom/README.md b/src/plugins/unlockedAvatarZoom/README.md
new file mode 100644
index 000000000..9ecd0b1a4
--- /dev/null
+++ b/src/plugins/unlockedAvatarZoom/README.md
@@ -0,0 +1,5 @@
+# UnlockedAvatarZoom
+
+Allows you to zoom in further in the image crop tool when changing your avatar
+
+
diff --git a/src/plugins/unlockedAvatarZoom/index.ts b/src/plugins/unlockedAvatarZoom/index.ts
new file mode 100644
index 000000000..08503eb8b
--- /dev/null
+++ b/src/plugins/unlockedAvatarZoom/index.ts
@@ -0,0 +1,35 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { definePluginSettings } from "@api/Settings";
+import { makeRange } from "@components/PluginSettings/components";
+import { Devs } from "@utils/constants";
+import definePlugin, { OptionType } from "@utils/types";
+
+const settings = definePluginSettings({
+ zoomMultiplier: {
+ type: OptionType.SLIDER,
+ description: "Zoom multiplier",
+ markers: makeRange(2, 16),
+ default: 4,
+ },
+});
+
+export default definePlugin({
+ name: "UnlockedAvatarZoom",
+ description: "Allows you to zoom in further in the image crop tool when changing your avatar",
+ authors: [Devs.nakoyasha],
+ settings,
+ patches: [
+ {
+ find: ".Messages.AVATAR_UPLOAD_EDIT_MEDIA",
+ replacement: {
+ match: /maxValue:\d/,
+ replace: "maxValue:$self.settings.store.zoomMultiplier",
+ }
+ }
+ ]
+});
diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx
index 200cfe895..feba28316 100644
--- a/src/plugins/userVoiceShow/index.tsx
+++ b/src/plugins/userVoiceShow/index.tsx
@@ -104,7 +104,7 @@ export default definePlugin({
},
// below username
{
- find: ".USER_PROFILE_MODAL",
+ find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Lazy-loaded
replacement: {
match: /\.body.+?displayProfile:\i}\),/,
replace: "$&$self.patchModal(arguments[0]),",
diff --git a/src/plugins/validUser/index.tsx b/src/plugins/validUser/index.tsx
index b0c77cb46..2fce693e8 100644
--- a/src/plugins/validUser/index.tsx
+++ b/src/plugins/validUser/index.tsx
@@ -111,19 +111,28 @@ function MentionWrapper({ data, UserMention, RoleMention, parse, props }: Mentio
export default definePlugin({
name: "ValidUser",
- description: "Fix mentions for unknown users showing up as '<@343383572805058560>' (hover over a mention to fix it)",
+ description: "Fix mentions for unknown users showing up as '@unknown-user' (hover over a mention to fix it)",
authors: [Devs.Ven],
tags: ["MentionCacheFix"],
- patches: [{
- find: 'className:"mention"',
- replacement: {
- // mention = { react: function (data, parse, props) { if (data.userId == null) return RoleMention() else return UserMention()
- match: /react(?=\(\i,\i,\i\).{0,50}return null==\i\?\(0,\i\.jsx\)\((\i\.\i),.+?jsx\)\((\i\.\i),\{className:"mention")/,
- // react: (...args) => OurWrapper(RoleMention, UserMention, ...args), originalReact: theirFunc
- replace: "react:(...args)=>$self.renderMention($1,$2,...args),originalReact"
+ patches: [
+ {
+ find: 'className:"mention"',
+ replacement: {
+ // mention = { react: function (data, parse, props) { if (data.userId == null) return RoleMention() else return UserMention()
+ match: /react(?=\(\i,\i,\i\).{0,100}return null==.{0,70}\?\(0,\i\.jsx\)\((\i\.\i),.+?jsx\)\((\i\.\i),\{className:"mention")/,
+ // react: (...args) => OurWrapper(RoleMention, UserMention, ...args), originalReact: theirFunc
+ replace: "react:(...args)=>$self.renderMention($1,$2,...args),originalReact"
+ }
+ },
+ {
+ find: "unknownUserMentionPlaceholder:",
+ replacement: {
+ match: /unknownUserMentionPlaceholder:/,
+ replace: "$&false&&"
+ }
}
- }],
+ ],
renderMention(RoleMention, UserMention, data, parse, props) {
return (
diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx
index 6eb773c28..f71777ad7 100644
--- a/src/plugins/viewIcons/index.tsx
+++ b/src/plugins/viewIcons/index.tsx
@@ -174,7 +174,7 @@ export default definePlugin({
find: ".NITRO_BANNER,",
replacement: {
// style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl,
- match: /style:\{(?=backgroundImage:(\i)\?"url\("\.concat\((\i),)/,
+ match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/,
replace:
// onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0,
'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index f1abac73c..bb704816b 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -155,7 +155,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
},
kemo: {
name: "kemo",
- id: 299693897859465228n
+ id: 715746190813298788n
},
dzshn: {
name: "dzshn",
@@ -429,6 +429,30 @@ export const Devs = /* #__PURE__*/ Object.freeze({
newwares: {
name: "newwares",
id: 421405303951851520n
+ },
+ Kodarru: {
+ name: "Kodarru",
+ id: 785227396218748949n
+ },
+ nakoyasha: {
+ name: "nakoyasha",
+ id: 222069018507345921n
+ },
+ Sqaaakoi: {
+ name: "Sqaaakoi",
+ id: 259558259491340288n
+ },
+ Byron: {
+ name: "byeoon",
+ id: 1167275288036655133n
+ },
+ Kaitlyn: {
+ name: "kaitlyn",
+ id: 306158896630988801n
+ },
+ PolisanTheEasyNick: {
+ name: "Oleh Polisan",
+ id: 242305263313485825n
}
} satisfies Record
);
diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts
index 32336fb40..a61785df9 100644
--- a/src/utils/lazy.ts
+++ b/src/utils/lazy.ts
@@ -116,8 +116,11 @@ export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T
attempts,
true
);
-
- return Reflect.get(target[kGET](), p, receiver);
+ const lazyTarget = target[kGET]();
+ if (typeof lazyTarget === "object" || typeof lazyTarget === "function") {
+ return Reflect.get(lazyTarget, p, receiver);
+ }
+ throw new Error("proxyLazy called on a primitive value");
}
}) as any;
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index 0790e8bf1..564da4813 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -92,7 +92,7 @@ if (IS_DEV && IS_DISCORD_DESKTOP) {
}, 0);
}
-function handleModuleNotFound(method: string, ...filter: unknown[]) {
+export function handleModuleNotFound(method: string, ...filter: unknown[]) {
const err = new Error(`webpack.${method} found no module`);
logger.error(err, "Filter:", filter);
@@ -406,13 +406,15 @@ export function findExportedComponentLazy(...props: stri
});
}
+const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/;
+
/**
* Extract and load chunks using their entry point
* @param code An array of all the code the module factory containing the lazy chunk loading must include
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
* @returns A promise that resolves when the chunks were loaded
*/
-export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\)/) {
+export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) {
const module = findModuleFactory(...code);
if (!module) {
const err = new Error("extractAndLoadChunks: Couldn't find module factory");
@@ -434,7 +436,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
}
const [, rawChunkIds, entryPointId] = match;
- if (!rawChunkIds || Number.isNaN(entryPointId)) {
+ if (Number.isNaN(entryPointId)) {
const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number");
logger.warn(err, "Code:", code, "Matcher:", matcher);
@@ -445,9 +447,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
return;
}
- const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]);
+ if (rawChunkIds) {
+ const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]);
+ await Promise.all(chunkIds.map(id => wreq.e(id)));
+ }
- await Promise.all(chunkIds.map(id => wreq.e(id)));
wreq(entryPointId);
}
@@ -459,7 +463,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
* @returns A function that returns a promise that resolves when the chunks were loaded, on first call
*/
-export function extractAndLoadChunksLazy(code: string[], matcher: RegExp = /Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\)/) {
+export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) {
if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
return () => extractAndLoadChunks(code, matcher);