-
-
+
{renderTimeout(message, true)} timeout remaining
diff --git a/src/plugins/showTimeoutDuration/styles.css b/src/plugins/showTimeoutDuration/styles.css
index 70a826e10..a6f830c38 100644
--- a/src/plugins/showTimeoutDuration/styles.css
+++ b/src/plugins/showTimeoutDuration/styles.css
@@ -2,3 +2,7 @@
display: flex;
align-items: center;
}
+
+.vc-std-wrapper [class*="communicationDisabled"] {
+ margin-right: 0;
+}
diff --git a/src/plugins/startupTimings/index.tsx b/src/plugins/startupTimings/index.tsx
index cf366df38..5051fdf4a 100644
--- a/src/plugins/startupTimings/index.tsx
+++ b/src/plugins/startupTimings/index.tsx
@@ -28,7 +28,7 @@ export default definePlugin({
patches: [{
find: "Messages.ACTIVITY_SETTINGS",
replacement: {
- match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\))/,
+ match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/,
replace: (_, commaOrSemi, settings, elements) => "" +
`${commaOrSemi}${settings}?.[0]==="CHANGELOG"` +
`&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})`
diff --git a/src/plugins/vcDoubleClick/index.ts b/src/plugins/vcDoubleClick/index.ts
index a55ac7b67..8d4cae6a9 100644
--- a/src/plugins/vcDoubleClick/index.ts
+++ b/src/plugins/vcDoubleClick/index.ts
@@ -48,7 +48,7 @@ export default definePlugin({
})),
{
// channel mentions
- find: ".shouldCloseDefaultModals",
+ find: 'className:"channelMention",children',
replacement: {
match: /onClick:(\i)(?=,.{0,30}className:"channelMention".+?(\i)\.inContent)/,
replace: (_, onClick, props) => ""
diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx
index f71777ad7..359365ee4 100644
--- a/src/plugins/viewIcons/index.tsx
+++ b/src/plugins/viewIcons/index.tsx
@@ -36,6 +36,10 @@ interface GuildContextProps {
guild?: Guild;
}
+interface GroupDMContextProps {
+ channel: Channel;
+}
+
const settings = definePluginSettings({
format: {
type: OptionType.SELECT,
@@ -145,10 +149,27 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
));
};
+const GroupDMContext: NavContextMenuPatchCallback = (children, { channel }: GroupDMContextProps) => {
+ if (!channel) return;
+
+ children.splice(-1, 0, (
+
+
+ openImage(IconUtils.getChannelIconURL(channel)!)
+ }
+ icon={ImageIcon}
+ />
+
+ ));
+};
+
export default definePlugin({
name: "ViewIcons",
- authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz],
- description: "Makes avatars and banners in user profiles clickable, and adds View Icon/Banner entries in the user and server context menu",
+ authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz, Devs.nyx],
+ description: "Makes avatars and banners in user profiles clickable, adds View Icon/Banner entries in the user, server and group channel context menu.",
tags: ["ImageUtilities"],
settings,
@@ -157,11 +178,12 @@ export default definePlugin({
contextMenus: {
"user-context": UserContext,
- "guild-context": GuildContext
+ "guild-context": GuildContext,
+ "gdm-context": GroupDMContext
},
patches: [
- // Make pfps clickable
+ // Profiles Modal pfp
{
find: "User Profile Modal - Context Menu",
replacement: {
@@ -169,7 +191,7 @@ export default definePlugin({
replace: "{src:$1,onClick:()=>$self.openImage($1)"
}
},
- // Make banners clickable
+ // Banners
{
find: ".NITRO_BANNER,",
replacement: {
@@ -180,12 +202,37 @@ export default definePlugin({
'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
}
},
+ // User DMs "User Profile" popup in the right
{
find: ".avatarPositionPanel",
replacement: {
match: /(?<=avatarWrapperNonUserBot.{0,50})onClick:(\i\|\|\i)\?void 0(?<=,avatarSrc:(\i).+?)/,
replace: "style:($1)?{cursor:\"pointer\"}:{},onClick:$1?()=>{$self.openImage($2)}"
}
+ },
+ // Group DMs top small & large icon
+ {
+ find: /\.recipients\.length>=2(?!
`${m},onClick:()=>$self.openImage(${iconUrl})`
+ }
+ },
+ // User DMs top small icon
+ {
+ find: ".cursorPointer:null,children",
+ replacement: {
+ match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
+ replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})`
+ }
+ },
+ // User Dms top large icon
+ {
+ find: 'experimentLocation:"empty_messages"',
+ replacement: {
+ match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
+ replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})`
+ }
}
]
});
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index a77edf7d5..2f686d69d 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -422,6 +422,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "Av32000",
id: 593436735380127770n,
},
+ Noxillio: {
+ name: "Noxillio",
+ id: 138616536502894592n,
+ },
Kyuuhachi: {
name: "Kyuuhachi",
id: 236588665420251137n,
@@ -442,6 +446,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "newwares",
id: 421405303951851520n
},
+ JohnyTheCarrot: {
+ name: "JohnyTheCarrot",
+ id: 132819036282159104n
+ },
puv: {
name: "puv",
id: 469441552251355137n
@@ -490,6 +498,22 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "ScattrdBlade",
id: 678007540608532491n
},
+ goodbee: {
+ name: "goodbee",
+ id: 658968552606400512n
+ },
+ Moxxie: {
+ name: "Moxxie",
+ id: 712653921692155965n,
+ },
+ Ethan: {
+ name: "Ethan",
+ id: 721717126523781240n,
+ },
+ nyx: {
+ name: "verticalsync",
+ id: 328165170536775680n
+ },
} satisfies Record);
// iife so #__PURE__ works correctly
diff --git a/src/utils/index.ts b/src/utils/index.ts
index ea4adce4a..62f3f6e96 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -23,9 +23,11 @@ export * from "./constants";
export * from "./discord";
export * from "./guards";
export * from "./lazy";
+export * from "./lazyReact";
export * from "./localStorage";
export * from "./Logger";
export * from "./margins";
+export * from "./mergeDefaults";
export * from "./misc";
export * from "./modal";
export * from "./onlyOnce";
diff --git a/src/utils/patches.ts b/src/utils/patches.ts
index 99f0595d6..87f3ce78c 100644
--- a/src/utils/patches.ts
+++ b/src/utils/patches.ts
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-import { PatchReplacement, ReplaceFn } from "./types";
+import { Patch, PatchReplacement, ReplaceFn } from "./types";
export function canonicalizeMatch(match: T): T {
if (typeof match === "string") return match;
@@ -55,3 +55,9 @@ export function canonicalizeReplacement(replacement: Pick SettingsRouter = m);
export const { Permissions: PermissionsBits } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; };
-export const zustandCreate: typeof import("zustand").default = findByCodeLazy("will be removed in v4");
+export const zustandCreate = findByCodeLazy("will be removed in v4");
const persistFilter = filters.byCode("[zustand persist middleware]");
-export const { persist: zustandPersist }: typeof import("zustand/middleware") = findLazy(m => m.persist && persistFilter(m.persist));
+export const { persist: zustandPersist } = findLazy(m => m.persist && persistFilter(m.persist));
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts
index c7e424671..311e6f2bc 100644
--- a/src/webpack/patchWebpack.ts
+++ b/src/webpack/patchWebpack.ts
@@ -99,6 +99,16 @@ Object.defineProperty(Function.prototype, "O", {
};
onChunksLoaded.toString = originalOnChunksLoaded.toString.bind(originalOnChunksLoaded);
+
+ // Returns whether a chunk has been loaded
+ Object.defineProperty(onChunksLoaded, "j", {
+ set(v) {
+ delete onChunksLoaded.j;
+ onChunksLoaded.j = v;
+ originalOnChunksLoaded.j = v;
+ },
+ configurable: true
+ });
}
Object.defineProperty(this, "O", {
@@ -122,7 +132,7 @@ Object.defineProperty(Function.prototype, "m", {
// When using react devtools or other extensions, we may also catch their webpack here.
// This ensures we actually got the right one
const { stack } = new Error();
- if (stack?.includes("discord.com") || stack?.includes("discordapp.com")) {
+ if ((stack?.includes("discord.com") || stack?.includes("discordapp.com")) && !Array.isArray(v)) {
logger.info("Found Webpack module factory", stack.match(/\/assets\/(.+?\.js)/)?.[1] ?? "");
patchFactories(v);
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index 8ea6713d0..854820851 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-import { proxyLazy } from "@utils/lazy";
+import { makeLazy, proxyLazy } from "@utils/lazy";
import { LazyComponent } from "@utils/lazyReact";
import { Logger } from "@utils/Logger";
import { canonicalizeMatch } from "@utils/patches";
@@ -402,7 +402,8 @@ export function findExportedComponentLazy(...props: stri
});
}
-const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/;
+export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/;
+export const ChunkIdsRegex = /\("(.+?)"\)/g;
/**
* Extract and load chunks using their entry point
@@ -431,7 +432,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
return;
}
- const [, rawChunkIds, entryPointId] = match;
+ const [, rawChunkIdsArray, rawChunkIdsSingle, entryPointId] = match;
if (Number.isNaN(Number(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);
@@ -443,8 +444,9 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
return;
}
+ const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle;
if (rawChunkIds) {
- const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]);
+ const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => m[1]);
await Promise.all(chunkIds.map(id => wreq.e(id)));
}
@@ -462,7 +464,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def
export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) {
if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
- return () => extractAndLoadChunks(code, matcher);
+ return makeLazy(() => extractAndLoadChunks(code, matcher));
}
/**
diff --git a/tsconfig.json b/tsconfig.json
index 96c904766..8db0ab3c1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,7 +29,15 @@
"@webpack/types": ["./webpack/common/types"],
"@webpack/common": ["./webpack/common"],
"@webpack": ["./webpack/webpack"]
- }
+ },
+
+ "plugins": [
+ // Transform paths in output .d.ts files (Include this line if you output declarations files)
+ {
+ "transform": "typescript-transform-paths",
+ "afterDeclarations": true
+ }
+ ]
},
"include": ["src/**/*", "browser/**/*", "scripts/**/*"]
}