diff --git a/package.json b/package.json index 97da80cdb..d5b23e57c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.10.6", + "version": "1.10.7", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 2ec9fba7c..c18bc14a3 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -225,7 +225,7 @@ page.on("console", async e => { plugin, type, id, - match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), + match: regex.replace(/\(\?:\[A-Za-z_\$\]\[\\w\$\]\*\)/g, "\\i"), error: await maybeGetError(e.args()[3]) }); diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index fd33c09df..c11551873 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -247,7 +247,7 @@ function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: Fu } try { - const parsed = (0, eval)(`(${fullPatch})`) as Patch; + const parsed = (0, eval)(`([${fullPatch}][0])`) as Patch; if (!parsed.find) throw new Error("No 'find' field"); if (!parsed.replacement) throw new Error("No 'replacement' field"); diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index b7a8fbc66..6923d3a23 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -27,7 +27,12 @@ export async function loadLazyChunks() { const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); + const foundCssDebuggingLoad = false; + async function searchAndLoadLazyChunks(factoryCode: string) { + // Workaround to avoid loading the CSS debugging chunk which turns the app pink + const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : factoryCode.includes(".cssDebuggingEnabled&&"); + const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>(); @@ -43,6 +48,16 @@ export async function loadLazyChunks() { let invalidChunkGroup = false; for (const id of chunkIds) { + if (hasCssDebuggingLoad) { + if (chunkIds.length > 1) { + throw new Error("Found multiple chunks in factory that loads the CSS debugging chunk"); + } + + invalidChunks.add(id); + invalidChunkGroup = true; + break; + } + if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 8d6a1e76d..802e1c95a 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -59,15 +59,7 @@ export default definePlugin({ replace: "$&return;" } ] - }, - { - find: ".installedLogHooks)", - replacement: { - // if getDebugLogging() returns false, the hooks don't get installed. - match: "getDebugLogging(){", - replace: "getDebugLogging(){return false;" - } - }, + } ], startAt: StartAt.Init, diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index a94e8cfaf..d58c7a98c 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -65,7 +65,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,60}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})` } ] diff --git a/src/plugins/alwaysExpandRoles/index.ts b/src/plugins/alwaysExpandRoles/index.ts index 1c20b9777..c674f90c5 100644 --- a/src/plugins/alwaysExpandRoles/index.ts +++ b/src/plugins/alwaysExpandRoles/index.ts @@ -28,10 +28,18 @@ export default definePlugin({ 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)` - } + replacement: [ + { + match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/, + replace: (_, rest, setExpandedRoles) => `${rest}!0)` + }, + { + // Fix not calculating non-expanded roles because the above patch makes the default "expanded", + // which makes the collapse button never show up and calculation never occur + match: /(?<=useLayoutEffect\(\(\)=>{if\()\i/, + replace: isExpanded => "false" + } + ] } ] }); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 50dee4f56..5d4d52c42 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -275,16 +275,16 @@ export default definePlugin({ }, makeGuildsBarGuildListFilter(isBetterFolders: boolean) { - try { return child => { if (isBetterFolders) { - return child?.props?.["aria-label"] === getIntlMessage("SERVERS"); + try { + return child?.props?.["aria-label"] === getIntlMessage("SERVERS"); + } catch (e) { + console.error(e); + } } return true; }; - } catch { - return true; - } }, makeGuildsBarTreeFilter(isBetterFolders: boolean) { diff --git a/src/plugins/consoleJanitor/index.ts b/src/plugins/consoleJanitor/index.ts index 2d5d60ecf..a02fcad1b 100644 --- a/src/plugins/consoleJanitor/index.ts +++ b/src/plugins/consoleJanitor/index.ts @@ -130,6 +130,27 @@ export default definePlugin({ replace: "" } }, + // Zustand section + { + find: "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.", + replacement: [ + { + match: /&&console\.warn\("\[DEPRECATED\] Passing a vanilla store will be unsupported in a future version\. Instead use `import { useStore } from 'zustand'`\."\)/, + replace: "" + }, + { + match: /console\.warn\("\[DEPRECATED\] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`\. They can be imported from 'zustand\/traditional'\. https:\/\/github\.com\/pmndrs\/zustand\/discussions\/1937"\),/, + replace: "" + } + ] + }, + { + find: "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.", + replacement: { + match: /console\.warn\("\[DEPRECATED\] `getStorage`, `serialize` and `deserialize` options are deprecated\. Use `storage` option instead\."\),/, + replace: "" + } + }, // Patches discords generic logger function { find: "Σ:", @@ -147,5 +168,5 @@ export default definePlugin({ replace: "$self.NoopLogger()" } } - ], + ] }); diff --git a/src/plugins/decor/lib/stores/AuthorizationStore.tsx b/src/plugins/decor/lib/stores/AuthorizationStore.tsx index ba71da99e..7f3468fd0 100644 --- a/src/plugins/decor/lib/stores/AuthorizationStore.tsx +++ b/src/plugins/decor/lib/stores/AuthorizationStore.tsx @@ -93,7 +93,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate( } as AuthorizationState), { name: "decor-auth", - getStorage: () => indexedDBStorage, + storage: indexedDBStorage, partialize: state => ({ tokens: state.tokens }), onRehydrateStorage: () => state => state?.init() } diff --git a/src/plugins/decor/lib/stores/UsersDecorationsStore.ts b/src/plugins/decor/lib/stores/UsersDecorationsStore.ts index 53aa33e63..f47ccbc56 100644 --- a/src/plugins/decor/lib/stores/UsersDecorationsStore.ts +++ b/src/plugins/decor/lib/stores/UsersDecorationsStore.ts @@ -95,24 +95,39 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any, } as UsersDecorationsState))); export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined { - const [decorAvatarDecoration, setDecorAvatarDecoration] = useState(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null); + try { + const [decorAvatarDecoration, setDecorAvatarDecoration] = useState(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null); - useEffect(() => { - const destructor = useUsersDecorationsStore.subscribe( - state => { - if (!user) return; - const newDecorAvatarDecoration = state.getAsset(user.id); - if (!newDecorAvatarDecoration) return; - if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration); - } - ); + useEffect(() => { + const destructor = (() => { + try { + return useUsersDecorationsStore.subscribe( + state => { + if (!user) return; + const newDecorAvatarDecoration = state.getAsset(user.id); + if (!newDecorAvatarDecoration) return; + if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration); + } + ); + } catch { + return () => { }; + } + })(); - if (user) { - const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState(); - fetchUserDecorAvatarDecoration(user.id); - } - return destructor; - }, []); + try { + if (user) { + const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState(); + fetchUserDecorAvatarDecoration(user.id); + } + } catch { } - return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null; + return destructor; + }, []); + + return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null; + } catch (e) { + console.error(e); + } + + return null; } diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 087bb5551..86c5ae4d6 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -20,11 +20,11 @@ import { addPreEditListener, addPreSendListener, removePreEditListener, removePr import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; -import { getCurrentGuild } from "@utils/discord"; +import { getCurrentGuild, getEmojiURL } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, Patch } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; -import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; +import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; @@ -920,7 +920,7 @@ export default definePlugin({ const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; - const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize })); + const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize)); url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("name", emoji.name); @@ -953,7 +953,7 @@ export default definePlugin({ hasBypass = true; - const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize })); + const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize)); url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("name", emoji.name); diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index bc3c1ed90..a79e3f31e 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -28,7 +28,7 @@ import { getIntlMessage } from "@utils/discord"; import { Logger } from "@utils/Logger"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import { Message } from "discord-types/general"; @@ -43,7 +43,6 @@ interface MLMessage extends Message { } const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); -const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")'); function addDeleteStyle() { if (Settings.plugins.MessageLogger.deleteStyle === "text") { @@ -312,35 +311,33 @@ export default definePlugin({ ); }, - Messages: { - // DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}") - // TODO: find a better way to generate intl messages - DELETED_MESSAGE_COUNT: () => ({ - ast: [[ - 6, - "count", - { - "=0": ["No deleted messages"], - one: [ - [ - 1, - "count" - ], - " deleted message" + // DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}") + // TODO: Find a better way to generate intl messages + DELETED_MESSAGE_COUNT: () => ({ + ast: [[ + 6, + "count", + { + "=0": ["No deleted messages"], + one: [ + [ + 1, + "count" ], - other: [ - [ - 1, - "count" - ], - " deleted messages" - ] - }, - 0, - "cardinal" - ]] - }) - }, + " deleted message" + ], + other: [ + [ + 1, + "count" + ], + " deleted messages" + ] + }, + 0, + "cardinal" + ]] + }), patches: [ { @@ -531,7 +528,7 @@ export default definePlugin({ }, { match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/, - replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:', + replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.DELETED_MESSAGE_COUNT:', }, ], predicate: () => Settings.plugins.MessageLogger.collapseDeleted diff --git a/src/plugins/noBlockedMessages/index.ts b/src/plugins/noBlockedMessages/index.ts index 1a1700e40..65ce6136f 100644 --- a/src/plugins/noBlockedMessages/index.ts +++ b/src/plugins/noBlockedMessages/index.ts @@ -28,8 +28,8 @@ import { Message } from "discord-types/general"; const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); interface MessageDeleteProps { - // i18n message i18n.t["+FcYMz"] if deleted, with args - collapsedReason: () => any + // Internal intl message for BLOCKED_MESSAGE_COUNT + collapsedReason: () => any; } export default definePlugin({ diff --git a/src/plugins/noMosaic/index.ts b/src/plugins/noMosaic/index.ts index 962b90fe7..6ff34a350 100644 --- a/src/plugins/noMosaic/index.ts +++ b/src/plugins/noMosaic/index.ts @@ -20,7 +20,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "NoMosaic", authors: [Devs.AutumnVN], - description: "Removes Discord new image mosaic", + description: "Removes Discord image mosaic", tags: ["image", "mosaic", "media"], settings, @@ -29,8 +29,8 @@ export default definePlugin({ { find: '=>"IMAGE"===', replacement: { - match: /=>"IMAGE"===\i\|\|"VIDEO"===\i;/, - replace: "=>false;" + match: /=>"IMAGE"===\i\|\|"VIDEO"===\i(?:\|\|("VISUAL_PLACEHOLDER"===\i))?;/, + replace: (_, visualPlaceholderPred) => visualPlaceholderPred != null ? `=>${visualPlaceholderPred};` : "=>false;" } }, { diff --git a/src/plugins/relationshipNotifier/utils.ts b/src/plugins/relationshipNotifier/utils.ts index 053cff835..84e812a75 100644 --- a/src/plugins/relationshipNotifier/utils.ts +++ b/src/plugins/relationshipNotifier/utils.ts @@ -50,6 +50,8 @@ async function runMigrations() { export async function syncAndRunChecks() { await runMigrations(); + if (UserStore.getCurrentUser() == null) return; + const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([ guildsKey(), groupsKey(), diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index e23cf49d2..e9e343311 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -24,7 +24,7 @@ import { Devs } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; -import { ChannelStore, GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; +import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { buildSeveralUsers } from "../typingTweaks"; @@ -44,7 +44,7 @@ function getDisplayName(guildId: string, userId: string) { return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username; } -function TypingIndicator({ channelId }: { channelId: string; }) { +function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: string; }) { const typingUsers: Record = useStateFromStores( [TypingStore], () => ({ ...TypingStore.getTypingUsers(channelId) as Record }), @@ -57,7 +57,6 @@ function TypingIndicator({ channelId }: { channelId: string; }) { } ); const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId()); - const guildId = ChannelStore.getChannel(channelId).guild_id; if (!settings.store.includeMutedChannels) { const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId); @@ -165,7 +164,7 @@ export default definePlugin({ find: "UNREAD_IMPORTANT:", replacement: { match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/, - replace: "$&,$self.TypingIndicator($1.id)" + replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())" } }, // Theads @@ -174,14 +173,14 @@ export default definePlugin({ find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z", replacement: { match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/, - replace: "$&,$self.TypingIndicator($1.id)" + replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())" } } ], - TypingIndicator: (channelId: string) => ( + TypingIndicator: (channelId: string, guildId: string) => ( - + ), }); diff --git a/src/plugins/typingTweaks/index.tsx b/src/plugins/typingTweaks/index.tsx index 4fb3c7757..e2bbb4bc9 100644 --- a/src/plugins/typingTweaks/index.tsx +++ b/src/plugins/typingTweaks/index.tsx @@ -129,14 +129,22 @@ export default definePlugin({ buildSeveralUsers, mutateChildren(props: any, users: User[], children: any) { - if (!Array.isArray(children)) return children; + try { + if (!Array.isArray(children)) { + return children; + } - let element = 0; + let element = 0; - return children.map(c => - c.type === "strong" - ? - : c - ); + return children.map(c => + c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c)) + ? + : c + ); + } catch (e) { + console.error(e); + } + + return children; } }); diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index 629708140..c53116b4b 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -209,10 +209,11 @@ export default definePlugin({ }, // Group DMs top small & large icon { - find: /\.recipients\.length>=2(?! `${m},onClick:()=>$self.openAvatar(${iconUrl})` + // We have to check that icon is not an unread GDM in the server bar + replace: (m, iconUrl) => `${m},onClick:()=>arguments[0]?.size!=="SIZE_48"&&$self.openAvatar(${iconUrl})` } }, // User DMs top small icon diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 91fd791b2..ec96d0d41 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -19,7 +19,7 @@ import "./discord.css"; import { MessageObject } from "@api/MessageEvents"; -import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; +import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; import { Except } from "type-fest"; @@ -212,3 +212,14 @@ export async function fetchUserProfile(id: string, options?: FetchUserProfileOpt export function getUniqueUsername(user: User) { return user.discriminator === "0" ? user.username : user.tag; } + +/** + * Get the URL for an emoji. This function always returns a gif URL for animated emojis, instead of webp + * @param id The emoji id + * @param animated Whether the emoji is animated + * @param size The size for the emoji + */ +export function getEmojiURL(id: string, animated: boolean, size: number) { + const url = IconUtils.getEmojiURL({ id, animated, size }); + return animated ? url.replace(".webp", ".gif") : url; +} diff --git a/src/utils/patches.ts b/src/utils/patches.ts index 3812acd91..097c64560 100644 --- a/src/utils/patches.ts +++ b/src/utils/patches.ts @@ -21,8 +21,8 @@ import { Patch, PatchReplacement, ReplaceFn } from "./types"; export function canonicalizeMatch(match: T): T { let partialCanon = typeof match === "string" ? match : match.source; - partialCanon = partialCanon.replaceAll(/#{intl::([A-Za-z_$][\w$]*)}/g, (_, key) => { - const hashed = runtimeHashMessageKey(key); + partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => { + const hashed = modifier === "raw" ? key : runtimeHashMessageKey(key); const isString = typeof match === "string"; const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/"); @@ -40,7 +40,7 @@ export function canonicalizeMatch(match: T): T { return partialCanon as T; } - const canonSource = partialCanon.replaceAll(String.raw`\i`, String.raw`(?:[A-Za-z_$][\w$]*)`); + const canonSource = partialCanon.replaceAll("\\i", String.raw`(?:[A-Za-z_$][\w$]*)`); return new RegExp(canonSource, match.flags) as T; } diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 3f44d106d..4b1959b5a 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -163,9 +163,13 @@ waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); -export const zustandCreate = findByCodeLazy("will be removed in v4"); +export const { zustandCreate } = mapMangledModuleLazy(["useSyncExternalStoreWithSelector:", "Object.assign", /(\i)\?(\i)\(\1\):\2/], { + zustandCreate: filters.byCode(/(\i)\?(\i)\(\1\):\2/) +}); -export const zustandPersist = findByCodeLazy("[zustand persist middleware]"); +export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", { + zustandPersist: filters.byCode(/(\(\i,\i\))=>.+?\i\1/) +}); export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); @@ -181,7 +185,7 @@ export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLa toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/), setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/), setSearchQuery: filters.byCode("searchQuery:"), - useExpressionPickerStore: filters.byCode("Object.is") + useExpressionPickerStore: filters.byCode(/\(\i,\i=\i\)=>/) }); export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', {