diff --git a/package.json b/package.json index abb11ee5c..057175f9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.11.0", + "version": "1.11.3", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/src/api/ChatButtons.tsx b/src/api/ChatButtons.tsx index c24e3886f..6f4285ff5 100644 --- a/src/api/ChatButtons.tsx +++ b/src/api/ChatButtons.tsx @@ -9,7 +9,7 @@ import "./ChatButton.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Logger } from "@utils/Logger"; import { waitFor } from "@webpack"; -import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common"; +import { Button, ButtonWrapperClasses, Tooltip } from "@webpack/common"; import { Channel } from "discord-types/general"; import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react"; @@ -110,7 +110,7 @@ export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => { @@ -158,8 +158,8 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on className={classes(ButtonClasses.button, cl("info-button"))} > {plugin.options && !isObjectEmpty(plugin.options) - ? - : } + ? + : } } /> diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index d3d182e58..a4f9aeee1 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -63,10 +63,7 @@ height: 8em; display: flex; flex-direction: column; -} - -.vc-plugins-info-card div { - line-height: 32px; + gap: 0.25em; } .vc-plugins-restart-card { @@ -76,11 +73,11 @@ color: var(--info-warning-text); } -.vc-plugins-restart-card button { +.vc-plugins-restart-button { margin-top: 0.5em; background: var(--info-warning-foreground) !important; } -.vc-plugins-info-button svg:not(:hover, :focus) { +.vc-plugins-info-icon:not(:hover, :focus) { color: var(--text-muted); } diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index ddd5e5f18..aef6ba3c8 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -62,14 +62,21 @@ async function runReporter() { if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); } catch (e) { let logMessage = searchType; - if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; - else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`; - else if (method === "mapMangledModule") { + if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { + if (args[0].$$vencordProps != null) { + logMessage += `(${args[0].$$vencordProps.map(arg => `"${arg}"`).join(", ")})`; + } else { + logMessage += `(${args[0].toString().slice(0, 147)}...)`; + } + } else if (method === "extractAndLoadChunks") { + logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`; + } else if (method === "mapMangledModule") { const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null); logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; + } else { + logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; } - else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; ReporterLogger.log("Webpack Find Fail:", logMessage); } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 2a83809d6..22e96834f 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -28,7 +28,7 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { isPluginDev } from "@utils/misc"; -import { closeModal, Modals, openModal } from "@utils/modal"; +import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; import { Forms, Toasts, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; @@ -144,8 +144,8 @@ export default definePlugin({ closeModal(modalKey); VencordNative.native.openExternal("https://github.com/sponsors/Vendicated"); }}> - - + + - - + + - - + + - - + + )); }, diff --git a/src/plugins/_api/chatButtons.ts b/src/plugins/_api/chatButtons.ts index 578861e2e..184b01584 100644 --- a/src/plugins/_api/chatButtons.ts +++ b/src/plugins/_api/chatButtons.ts @@ -12,11 +12,16 @@ export default definePlugin({ description: "API to add buttons to the chat input", authors: [Devs.Ven], - patches: [{ - find: '"sticker")', - replacement: { - match: /return\(!\i\.\i&&(?=\(\i\.isDM.+?(\i)\.push\(.{0,50}"gift")/, - replace: "$&(Vencord.Api.ChatButtons._injectButtons($1,arguments[0]),true)&&" + patches: [ + { + find: '"sticker")', + replacement: { + // FIXME(Bundler change related): Remove old compatiblity once enough time has passed + match: /return\((!)?\i\.\i(?:\|\||&&)(?=\(\i\.isDM.+?(\i)\.push)/, + replace: (m, not, children) => not + ? `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),true)&&` + : `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),false)||` + } } - }] + ] }); diff --git a/src/plugins/_api/contextMenu.ts b/src/plugins/_api/contextMenu.ts index 01619546d..debc55273 100644 --- a/src/plugins/_api/contextMenu.ts +++ b/src/plugins/_api/contextMenu.ts @@ -34,12 +34,22 @@ export default definePlugin({ } }, { - find: ".Menu,{", + find: "navId:", all: true, - replacement: { - match: /Menu,{(?<=\.jsxs?\)\(\i\.Menu,{)/g, - replace: "$&contextMenuApiArguments:typeof arguments!=='undefined'?arguments:[]," - } + noWarn: true, + replacement: [ + { + match: /navId:(?=.+?([,}].*?\)))/g, + replace: (m, rest) => { + // Check if this navId: match is a destructuring statement, ignore it if it is + const destructuringMatch = rest.match(/}=.+/); + if (destructuringMatch == null) { + return `contextMenuAPIArguments:typeof arguments!=='undefined'?arguments:[],${m}`; + } + return m; + } + } + ] } ] }); diff --git a/src/plugins/_api/menuItemDemangler.ts b/src/plugins/_api/menuItemDemangler.ts new file mode 100644 index 000000000..b6a03fe88 --- /dev/null +++ b/src/plugins/_api/menuItemDemangler.ts @@ -0,0 +1,68 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import { canonicalizeMatch } from "@utils/patches"; +import definePlugin from "@utils/types"; + +// duplicate values have multiple branches with different types. Just include all to be safe +const nameMap = { + radio: "MenuRadioItem", + separator: "MenuSeparator", + checkbox: "MenuCheckboxItem", + groupstart: "MenuGroup", + + control: "MenuControlItem", + compositecontrol: "MenuControlItem", + + item: "MenuItem", + customitem: "MenuItem", +}; + +export default definePlugin({ + name: "MenuItemDemanglerAPI", + description: "Demangles Discord's Menu Item module", + authors: [Devs.Ven], + required: true, + patches: [ + { + find: '"Menu API', + replacement: { + match: /function.{0,80}type===(\i\.\i)\).{0,50}navigable:.+?Menu API/s, + replace: (m, mod) => { + const nameAssignments = [] as string[]; + + // if (t.type === m.MenuItem) + const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g); + // push({type:"item"}) + const pushTypeRe = /type:"(\w+)"/g; + + let typeMatch: RegExpExecArray | null; + // for each if (t.type === ...) + while ((typeMatch = typeCheckRe.exec(m)) !== null) { + // extract the current menu item + const item = typeMatch[1]; + // Set the starting index of the second regex to that of the first to start + // matching from after the if + pushTypeRe.lastIndex = typeCheckRe.lastIndex; + // extract the first type: "..." + const type = pushTypeRe.exec(m)?.[1]; + if (type && type in nameMap) { + const name = nameMap[type]; + nameAssignments.push(`Object.defineProperty(${item},"name",{value:"${name}"})`); + } + } + if (nameAssignments.length < 6) { + console.warn("[MenuItemDemanglerAPI] Expected to at least remap 6 items, only remapped", nameAssignments.length); + } + + // Merge all our redefines with the actual module + return `${nameAssignments.join(";")};${m}`; + }, + }, + }, + ], +}); diff --git a/src/plugins/_api/messageEvents.ts b/src/plugins/_api/messageEvents.ts index 97ed1746d..9dfc55e27 100644 --- a/src/plugins/_api/messageEvents.ts +++ b/src/plugins/_api/messageEvents.ts @@ -37,12 +37,9 @@ export default definePlugin({ { find: ".handleSendMessage,onResize", replacement: { - // 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) - match: /(\{openWarningPopout:.{0,100}type:this.props.chatInputType.+?\.then\()(\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 }; - replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" + - `${rest1}async ${rest2}` + + // https://regex101.com/r/hBlXpl/1 + match: /let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);(?<=\)\(({.+?})\)\.then.+?)/, + replace: (m, parsedMessage, channel, replyOptions, extra) => m + `if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` + "return{shouldClear:false,shouldRefocus:true};" } @@ -52,8 +49,7 @@ export default definePlugin({ replacement: { match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/, replace: (m, message, channel, event) => - // the message param is shadowed by the event param, so need to alias them - `const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});` + `const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg,vcChan,${event});` } } ] diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index d58c7a98c..f48f38e03 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -65,7 +65,8 @@ export default definePlugin({ replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` }, { - match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, + // 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/, replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` } ] diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts index fc528466f..a5297445b 100644 --- a/src/plugins/alwaysAnimate/index.ts +++ b/src/plugins/alwaysAnimate/index.ts @@ -43,8 +43,8 @@ export default definePlugin({ // Status emojis find: "#{intl::GUILD_OWNER}),children:", replacement: { - match: /(?<=\.activityEmoji,.+?animate:)\i/, - replace: "!0" + match: /(\.CUSTOM_STATUS.+?animate:)\i/, + replace: (_, rest) => `${rest}!0` } }, { diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 53d24ed93..5203b14d2 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -17,14 +17,13 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { useStateFromStores } from "@webpack/common"; +import { findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { Animations, useStateFromStores } from "@webpack/common"; import type { CSSProperties } from "react"; import { ExpandedGuildFolderStore, settings } from "."; const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); -const Animations = findByPropsLazy("a", "animated", "useTransition"); const GuildsBar = findComponentByCodeLazy('("guildsnav")'); export default ErrorBoundary.wrap(guildsBarProps => { diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index d9d68f138..39c109656 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -173,8 +173,8 @@ export default definePlugin({ // Disable expanding and collapsing folders transition in the normal GuildsBar sidebar { predicate: () => !settings.store.keepIcons, - match: /(?<=#{intl::SERVER_FOLDER_PLACEHOLDER}.+?useTransition\)\()/, - replace: "$self.shouldShowTransition(arguments[0])&&" + match: /(?=,\{from:\{height)/, + replace: "&&$self.shouldShowTransition(arguments[0])" }, // If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded { diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index 1029c07e2..afef63908 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -83,7 +83,7 @@ export default definePlugin({ if (!role) return; if (role.colorString) { - children.push( + children.unshift( { + await GuildSettingsActions.open(guild.id, "ROLES"); + GuildSettingsActions.selectRole(id); + }} + icon={PencilIcon} + /> + ); + } + if (role.icon) { children.push( { - await GuildSettingsActions.open(guild.id, "ROLES"); - GuildSettingsActions.selectRole(id); - }} - icon={PencilIcon} - /> - ); - } } } }); diff --git a/src/plugins/betterSessions/index.tsx b/src/plugins/betterSessions/index.tsx index 9347c398c..8f8ef141f 100644 --- a/src/plugins/betterSessions/index.tsx +++ b/src/plugins/betterSessions/index.tsx @@ -21,7 +21,7 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { Constants, React, RestAPI, Tooltip } from "@webpack/common"; import { RenameButton } from "./components/RenameButton"; @@ -34,7 +34,7 @@ const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open"); const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer"); const SessionIconClasses = findByPropsLazy("sessionIcon"); -const BlobMask = findExportedComponentLazy("BlobMask"); +const BlobMask = findComponentByCodeLazy("!1,lowerBadgeSize:"); const settings = definePluginSettings({ backgroundCheck: { diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 5a97b0a6e..461dfaac8 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -101,8 +101,8 @@ export default definePlugin({ find: 'minimal:"contentColumnMinimal"', replacement: [ { - match: /\(0,\i\.useTransition\)\((\i)/, - replace: "(_cb=>_cb(void 0,$1))||$&" + match: /(?=\(0,\i\.\i\)\((\i),\{from:\{position:"absolute")/, + replace: "(_cb=>_cb(void 0,$1))||" }, { match: /\i\.animated\.div/, diff --git a/src/plugins/blurNsfw/index.ts b/src/plugins/blurNsfw/index.ts index df3c7aa20..d98f24ef1 100644 --- a/src/plugins/blurNsfw/index.ts +++ b/src/plugins/blurNsfw/index.ts @@ -41,7 +41,7 @@ export default definePlugin({ settings: definePluginSettings({ blurAmount: { type: OptionType.NUMBER, - description: "Blur Amount", + description: "Blur Amount (in pixels)", default: 10, onChange(v) { setStyleVariables(style, { blurAmount: v }); diff --git a/src/plugins/blurNsfw/style.css b/src/plugins/blurNsfw/style.css index f7cce7416..5221f9c3b 100644 --- a/src/plugins/blurNsfw/style.css +++ b/src/plugins/blurNsfw/style.css @@ -1,10 +1,9 @@ -.vc-nsfw-img [class^="imageWrapper"] img, -.vc-nsfw-img [class^="wrapperPaused"] video { +.vc-nsfw-img [class^="imageContainer"], +.vc-nsfw-img [class^="wrapperPaused"] { filter: blur([--blur-amount]px); transition: filter 0.2s; -} -.vc-nsfw-img [class^="imageWrapper"]:hover img, -.vc-nsfw-img [class^="wrapperPaused"]:hover video { - filter: unset; + &:hover { + filter: blur(0); + } } diff --git a/src/plugins/clientTheme/clientTheme.css b/src/plugins/clientTheme/clientTheme.css index 64aefdcf5..795b5457e 100644 --- a/src/plugins/clientTheme/clientTheme.css +++ b/src/plugins/clientTheme/clientTheme.css @@ -1,29 +1,29 @@ -.client-theme-settings { +.vc-clientTheme-settings { display: flex; flex-direction: column; } -.client-theme-container { +.vc-clientTheme-container { display: flex; flex-direction: row; justify-content: space-between; } -.client-theme-settings-labels { +.vc-clientTheme-labels { display: flex; flex-direction: column; justify-content: flex-start; } -.client-theme-container > [class^="colorSwatch"] > [class^="swatch"] { +.vc-clientTheme-container [class^="swatch"] { border: thin solid var(--background-modifier-accent) !important; } -.client-theme-warning * { +.vc-clientTheme-warning-text { color: var(--text-danger); } -.client-theme-contrast-warning { +.vc-clientTheme-contrast-warning { background-color: var(--background-primary); padding: 0.5rem; border-radius: .5rem; diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index ef3b466aa..834a32f98 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -7,7 +7,7 @@ import "./clientTheme.css"; import { definePluginSettings } from "@api/Settings"; -import { createStyle, deleteStyle } from "@api/Styles"; +import { classNameFactory, createStyle, deleteStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; @@ -15,6 +15,8 @@ import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; +const cl = classNameFactory("vc-clientTheme-"); + const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)"); const colorPresets = [ @@ -61,9 +63,9 @@ function ThemeSettings() { } return ( -
-
-
+
+
+
Theme Color Add a color to your Discord client theme
@@ -77,10 +79,10 @@ function ThemeSettings() { {(contrastWarning || nitroThemeEnabled) && (<>
-
- Warning, your theme won't look good: - {contrastWarning && Selected color won't contrast well with text} - {nitroThemeEnabled && Nitro themes aren't supported} +
+ Warning, your theme won't look good: + {contrastWarning && Selected color won't contrast well with text} + {nitroThemeEnabled && Nitro themes aren't supported}
{(contrastWarning && fixableContrast) && } {(nitroThemeEnabled) && } @@ -92,15 +94,12 @@ function ThemeSettings() { const settings = definePluginSettings({ color: { - description: "Color your Discord client theme will be based around. Light mode isn't supported", type: OptionType.COMPONENT, default: "313338", - component: () => + component: ThemeSettings }, resetColor: { - description: "Reset Theme Color", type: OptionType.COMPONENT, - default: "313338", component: () => ( + ) } }); diff --git a/src/plugins/messageLogger/deleteStyleText.css b/src/plugins/messageLogger/deleteStyleText.css index a4e9a93c1..a114b7de8 100644 --- a/src/plugins/messageLogger/deleteStyleText.css +++ b/src/plugins/messageLogger/deleteStyleText.css @@ -1,24 +1,8 @@ -/* Message content highlighting */ -.messagelogger-deleted [class*="contents"] > :is(div, h1, h2, h3, p) { - color: var(--status-danger, #f04747) !important; -} - -/* Markdown title highlighting */ -.messagelogger-deleted [class*="contents"] :is(h1, h2, h3) { - color: var(--status-danger, #f04747) !important; -} - -/* Bot "thinking" text highlighting */ -.messagelogger-deleted [class*="colorStandard"] { - color: var(--status-danger, #f04747) !important; -} - -/* Embed highlighting */ -.messagelogger-deleted article :is(div, span, h1, h2, h3, p) { - color: var(--status-danger, #f04747) !important; -} - -.messagelogger-deleted a { - color: var(--red-460, #be3535) !important; - text-decoration: underline; +.messagelogger-deleted { + --text-normal: var(--status-danger, #f04747); + --interactive-normal: var(--status-danger, #f04747); + --text-muted: var(--status-danger, #f04747); + --embed-title: var(--red-460, #be3535); + --text-link: var(--red-460, #be3535); + --header-primary: var(--red-460, #be3535); } diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 333de9a4e..dee58f2f9 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -211,7 +211,8 @@ export default definePlugin({ collapseDeleted: { type: OptionType.BOOLEAN, description: "Whether to collapse deleted messages, similar to blocked messages", - default: false + default: false, + restartNeeded: true, }, logEdits: { type: OptionType.BOOLEAN, @@ -441,15 +442,10 @@ export default definePlugin({ { // Attachment renderer find: ".removeMosaicItemHoverButton", - group: true, replacement: [ { - match: /(className:\i,item:\i),/, - replace: "$1,item: deleted," - }, - { - match: /\[\i\.obscured\]:.+?,/, - replace: "$& 'messagelogger-deleted-attachment': deleted," + match: /\[\i\.obscured\]:.+?,(?<=item:(\i).+?)/, + replace: '$&"messagelogger-deleted-attachment":$1.originalItem?.deleted,' } ] }, @@ -500,7 +496,7 @@ export default definePlugin({ { // Message context base menu - find: "useMessageMenu:", + find: ".MESSAGE,commandTargetId:", replacement: [ { // Remove the first section if message is deleted diff --git a/src/plugins/messageLogger/messageLogger.css b/src/plugins/messageLogger/messageLogger.css index 2759129d9..a76e98888 100644 --- a/src/plugins/messageLogger/messageLogger.css +++ b/src/plugins/messageLogger/messageLogger.css @@ -4,12 +4,12 @@ .messagelogger-deleted :is( - video, + .messagelogger-deleted-attachment, .emoji, [data-type="sticker"], - iframe, - .messagelogger-deleted-attachment, - [class|="inlineMediaEmbed"] + [class*="embedIframe"], + [class*="embedSpotify"], + [class*="imageContainer"] ) { filter: grayscale(1) !important; transition: 150ms filter ease-in-out; @@ -17,18 +17,14 @@ &[class*="hiddenMosaicItem_"] { filter: grayscale(1) blur(var(--custom-message-attachment-spoiler-blur-radius, 44px)) !important; } + + &:hover { + filter: grayscale(0) !important; + } } -.messagelogger-deleted -:is( - video, - .emoji, - [data-type="sticker"], - iframe, - .messagelogger-deleted-attachment, - [class|="inlineMediaEmbed"] -):hover { - filter: grayscale(0) !important; +.messagelogger-deleted [class*="spoilerWarning"] { + color: var(--status-danger); } .theme-dark .messagelogger-edited { diff --git a/src/plugins/messageTags/index.ts b/src/plugins/messageTags/index.ts index 5a5d03fdb..49e88c42d 100644 --- a/src/plugins/messageTags/index.ts +++ b/src/plugins/messageTags/index.ts @@ -89,7 +89,7 @@ export default definePlugin({ settings, 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(DATA_KEY); if (oldTags != null) { // @ts-ignore diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx deleted file mode 100644 index 8029b4833..000000000 --- a/src/plugins/moreUserTags/index.tsx +++ /dev/null @@ -1,372 +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 . -*/ - -import { definePluginSettings } from "@api/Settings"; -import { Flex } from "@components/Flex"; -import { Devs } from "@utils/constants"; -import { getIntlMessage } from "@utils/discord"; -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 } from "@webpack/common"; -import type { Permissions, RC } from "@webpack/types"; -import type { Channel, Guild, Message, User } from "discord-types/general"; - -interface Tag { - // name used for identifying, must be alphanumeric + underscores - name: string; - // name shown on the tag itself, can be anything probably; automatically uppercase'd - displayName: string; - description: string; - permissions?: Permissions[]; - condition?(message: Message | null, user: User, channel: Channel): boolean; -} - -interface TagSetting { - text: string; - showInChat: boolean; - showInNotChat: boolean; -} -interface TagSettings { - WEBHOOK: TagSetting, - OWNER: TagSetting, - ADMINISTRATOR: TagSetting, - MODERATOR_STAFF: TagSetting, - MODERATOR: TagSetting, - VOICE_MODERATOR: TagSetting, - TRIAL_MODERATOR: TagSetting, - [k: string]: TagSetting; -} - -// PermissionStore.computePermissions will not work here since it only gets permissions for the current user -const computePermissions: (options: { - user?: { id: string; } | string | null; - context?: Guild | Channel | null; - overwrites?: Channel["permissionOverwrites"] | null; - checkElevated?: boolean /* = true */; - excludeGuildPermissions?: boolean /* = false */; -}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); - -const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; - -const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); - -const tags: Tag[] = [ - { - name: "WEBHOOK", - displayName: "Webhook", - description: "Messages sent by webhooks", - condition: isWebhook - }, { - name: "OWNER", - displayName: "Owner", - description: "Owns the server", - condition: (_, user, channel) => GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id - }, { - name: "ADMINISTRATOR", - displayName: "Admin", - description: "Has the administrator permission", - permissions: ["ADMINISTRATOR"] - }, { - name: "MODERATOR_STAFF", - displayName: "Staff", - description: "Can manage the server, channels or roles", - permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS", "MANAGE_ROLES"] - }, { - name: "MODERATOR", - displayName: "Mod", - description: "Can manage messages or kick/ban people", - permissions: ["MANAGE_MESSAGES", "KICK_MEMBERS", "BAN_MEMBERS"] - }, { - name: "VOICE_MODERATOR", - displayName: "VC Mod", - description: "Can manage voice chats", - permissions: ["MOVE_MEMBERS", "MUTE_MEMBERS", "DEAFEN_MEMBERS"] - }, { - name: "CHAT_MODERATOR", - displayName: "Chat Mod", - description: "Can timeout people", - permissions: ["MODERATE_MEMBERS"] - } -]; -const defaultSettings = Object.fromEntries( - tags.map(({ name, displayName }) => [name, { text: displayName, showInChat: true, showInNotChat: true }]) -) as TagSettings; - -function SettingsComponent() { - const tagSettings = settings.store.tagSettings ??= defaultSettings; - - return ( - - {tags.map(t => ( - - - - {({ onMouseEnter, onMouseLeave }) => ( -
- {t.displayName} Tag -
- )} -
-
- - tagSettings[t.name].text = v} - className={Margins.bottom16} - /> - - tagSettings[t.name].showInChat = v} - hideBorder - > - Show in messages - - - tagSettings[t.name].showInNotChat = v} - hideBorder - > - Show in member list and profiles - -
- ))} -
- ); -} - -const settings = definePluginSettings({ - dontShowForBots: { - description: "Don't show extra tags for bots (excluding webhooks)", - type: OptionType.BOOLEAN - }, - dontShowBotTag: { - description: "Only show extra tags for bots / Hide [BOT] text", - type: OptionType.BOOLEAN - }, - tagSettings: { - type: OptionType.COMPONENT, - component: SettingsComponent, - description: "fill me" - } -}); - -export default definePlugin({ - name: "MoreUserTags", - description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)", - authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN], - settings, - patches: [ - // add tags to the tag list - { - find: ".ORIGINAL_POSTER=", - replacement: { - match: /(?=(\i)\[\i\.BOT)/, - replace: "$self.genTagTypes($1);" - } - }, - { - find: "#{intl::DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL}", - replacement: [ - // make the tag show the right text - { - match: /(switch\((\i)\){.+?)case (\i(?:\.\i)?)\.BOT:default:(\i)=(.{0,40}#{intl::APP_TAG}\))/, - replace: (_, origSwitch, variant, tags, displayedText, originalText) => - `${origSwitch}default:{${displayedText} = $self.getTagText(${tags}[${variant}],${originalText})}` - }, - // show OP tags correctly - { - match: /(\i)=(\i)===\i(?:\.\i)?\.ORIGINAL_POSTER/, - replace: "$1=$self.isOPTag($2)" - }, - // add HTML data attributes (for easier theming) - { - match: /.botText,children:(\i)}\)]/, - replace: "$&,'data-tag':$1.toLowerCase()" - } - ], - }, - // in messages - { - find: ".Types.ORIGINAL_POSTER", - replacement: { - match: /;return\((\(null==\i\?void 0:\i\.isSystemDM\(\).+?.Types.ORIGINAL_POSTER\)),null==(\i)\)/, - replace: ";$1;$2=$self.getTag({...arguments[0],origType:$2,location:'chat'});return $2 == null" - } - }, - // in the member list - { - find: "#{intl::GUILD_OWNER}),children:", - replacement: { - match: /(?\i)=\(null==.{0,100}\.BOT;return null!=(?\i)&&\i\.bot/, - replace: "$ = $self.getTag({user: $, channel: arguments[0].channel, origType: $.bot ? 0 : null, location: 'not-chat' }); return typeof $ === 'number'" - } - }, - // pass channel id down props to be used in profiles - { - find: ".hasAvatarForGuild(null==", - replacement: { - match: /(?=usernameIcon:)/, - replace: "moreTags_channelId:arguments[0].channelId," - } - }, - { - find: "#{intl::USER_PROFILE_PRONOUNS}", - replacement: { - match: /(?=,hideBotTag:!0)/, - replace: ",moreTags_channelId:arguments[0].moreTags_channelId" - } - }, - // in profiles - { - find: ",overrideDiscriminator:", - group: true, - replacement: [ - { - // prevent channel id from getting ghosted - // it's either this or extremely long lookbehind - match: /user:\i,nick:\i,/, - replace: "$&moreTags_channelId," - }, { - match: /,botType:(\i),botVerified:(\i),(?!discriminatorClass:)(?<=user:(\i).+?)/g, - replace: ",botType:$self.getTag({user:$3,channelId:moreTags_channelId,origType:$1,location:'not-chat'}),botVerified:$2," - } - ] - }, - ], - - start() { - settings.store.tagSettings ??= defaultSettings; - - // newly added field might be missing from old users - settings.store.tagSettings.CHAT_MODERATOR ??= { - text: "Chat Mod", - showInChat: true, - showInNotChat: true - }; - }, - - getPermissions(user: User, channel: Channel): string[] { - const guild = GuildStore.getGuild(channel?.guild_id); - if (!guild) return []; - - const permissions = computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites }); - return Object.entries(PermissionsBits) - .map(([perm, permInt]) => - permissions & permInt ? perm : "" - ) - .filter(Boolean); - }, - - genTagTypes(obj) { - let i = 100; - tags.forEach(({ name }) => { - obj[name] = ++i; - obj[i] = name; - obj[`${name}-BOT`] = ++i; - obj[i] = `${name}-BOT`; - obj[`${name}-OP`] = ++i; - obj[i] = `${name}-OP`; - }); - }, - - isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]), - - getTagText(passedTagName: string, originalText: string) { - try { - const [tagName, variant] = passedTagName.split("-"); - if (!passedTagName) return getIntlMessage("APP_TAG"); - const tag = tags.find(({ name }) => tagName === name); - if (!tag) return getIntlMessage("APP_TAG"); - if (variant === "BOT" && tagName !== "WEBHOOK" && this.settings.store.dontShowForBots) return getIntlMessage("APP_TAG"); - - const tagText = settings.store.tagSettings?.[tag.name]?.text || tag.displayName; - switch (variant) { - case "OP": - return `${getIntlMessage("BOT_TAG_FORUM_ORIGINAL_POSTER")} • ${tagText}`; - case "BOT": - return `${getIntlMessage("APP_TAG")} • ${tagText}`; - default: - return tagText; - } - } catch { - return originalText; - } - }, - - getTag({ - message, user, channelId, origType, location, channel - }: { - message?: Message, - user: User & { isClyde(): boolean; }, - channel?: Channel & { isForumPost(): boolean; isMediaPost(): boolean; }, - channelId?: string; - origType?: number; - location: "chat" | "not-chat"; - }): number | null { - if (!user) - return null; - if (location === "chat" && user.id === "1") - return Tag.Types.OFFICIAL; - if (user.isClyde()) - return Tag.Types.AI; - - let type = typeof origType === "number" ? origType : null; - - channel ??= ChannelStore.getChannel(channelId!) as any; - if (!channel) return type; - - const settings = this.settings.store; - const perms = this.getPermissions(user, channel); - - for (const tag of tags) { - if (location === "chat" && !settings.tagSettings[tag.name].showInChat) continue; - if (location === "not-chat" && !settings.tagSettings[tag.name].showInNotChat) continue; - - // If the owner tag is disabled, and the user is the owner of the guild, - // avoid adding other tags because the owner will always match the condition for them - if ( - tag.name !== "OWNER" && - GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id && - (location === "chat" && !settings.tagSettings.OWNER.showInChat) || - (location === "not-chat" && !settings.tagSettings.OWNER.showInNotChat) - ) continue; - - if ( - tag.permissions?.some(perm => perms.includes(perm)) || - (tag.condition?.(message!, user, channel)) - ) { - if ((channel.isForumPost() || channel.isMediaPost()) && channel.ownerId === user.id) - type = Tag.Types[`${tag.name}-OP`]; - else if (user.bot && !isWebhook(message!, user) && !settings.dontShowBotTag) - type = Tag.Types[`${tag.name}-BOT`]; - else - type = Tag.Types[tag.name]; - break; - } - } - return type; - } -}); diff --git a/src/plugins/noBlockedMessages/index.ts b/src/plugins/noBlockedMessages/index.ts index 48ca63d18..95b53c6b3 100644 --- a/src/plugins/noBlockedMessages/index.ts +++ b/src/plugins/noBlockedMessages/index.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { Settings } from "@api/Settings"; +import { definePluginSettings, migratePluginSetting } from "@api/Settings"; import { Devs } from "@utils/constants"; import { runtimeHashMessageKey } from "@utils/intlHash"; import { Logger } from "@utils/Logger"; @@ -32,10 +32,29 @@ interface MessageDeleteProps { collapsedReason: () => any; } +// Remove this migration once enough time has passed +migratePluginSetting("NoBlockedMessages", "ignoreBlockedMessages", "ignoreMessages"); +const settings = definePluginSettings({ + ignoreMessages: { + description: "Completely ignores incoming messages from blocked and ignored (if enabled) users", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true + }, + applyToIgnoredUsers: { + description: "Additionally apply to 'ignored' users", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: false + } +}); + export default definePlugin({ name: "NoBlockedMessages", - description: "Hides all blocked messages from chat completely.", - authors: [Devs.rushii, Devs.Samu], + description: "Hides all blocked/ignored messages from chat completely", + authors: [Devs.rushii, Devs.Samu, Devs.jamesbt365], + settings, + patches: [ { find: "#{intl::BLOCKED_MESSAGES_HIDE}", @@ -51,38 +70,40 @@ export default definePlugin({ '"ReadStateStore"' ].map(find => ({ find, - predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true, + predicate: () => settings.store.ignoreMessages, replacement: [ { match: /(?<=function (\i)\((\i)\){)(?=.*MESSAGE_CREATE:\1)/, - replace: (_, _funcName, props) => `if($self.isBlocked(${props}.message))return;` + replace: (_, _funcName, props) => `if($self.shouldIgnoreMessage(${props}.message))return;` } ] })) ], - options: { - ignoreBlockedMessages: { - description: "Completely ignores (recent) incoming messages from blocked users (locally).", - type: OptionType.BOOLEAN, - default: false, - restartNeeded: true, - }, - }, - isBlocked(message: Message) { + shouldIgnoreMessage(message: Message) { try { - return RelationshipStore.isBlocked(message.author.id); + if (RelationshipStore.isBlocked(message.author.id)) { + return true; + } + return settings.store.applyToIgnoredUsers && RelationshipStore.isIgnored(message.author.id); } catch (e) { - new Logger("NoBlockedMessages").error("Failed to check if user is blocked:", e); + new Logger("NoBlockedMessages").error("Failed to check if user is blocked or ignored:", e); + return false; } }, - shouldHide(props: MessageDeleteProps) { + shouldHide(props: MessageDeleteProps): boolean { try { - return props.collapsedReason() === i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")](); + const collapsedReason = props.collapsedReason(); + const blockedReason = i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")](); + const ignoredReason = settings.store.applyToIgnoredUsers + ? i18n.t[runtimeHashMessageKey("IGNORED_MESSAGE_COUNT")]() + : null; + + return collapsedReason === blockedReason || collapsedReason === ignoredReason; } catch (e) { console.error(e); + return false; } - return false; } }); diff --git a/src/plugins/noScreensharePreview/index.ts b/src/plugins/noScreensharePreview/index.ts deleted file mode 100644 index d4bb9c1eb..000000000 --- a/src/plugins/noScreensharePreview/index.ts +++ /dev/null @@ -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 . -*/ - -import { getUserSettingLazy } from "@api/UserSettings"; -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - -const DisableStreamPreviews = getUserSettingLazy("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); - } - } -}); diff --git a/src/plugins/notificationVolume/index.ts b/src/plugins/notificationVolume/index.ts index bc3c7539d..d320d76f1 100644 --- a/src/plugins/notificationVolume/index.ts +++ b/src/plugins/notificationVolume/index.ts @@ -25,9 +25,9 @@ export default definePlugin({ settings, patches: [ { - find: "_ensureAudio(){", + find: "ensureAudio(){", replacement: { - match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/, + match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/g, replace: "$self.settings.store.notificationVolume/100*" }, }, diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 09fc2f3b7..1c90b5290 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -100,8 +100,9 @@ export default definePlugin({ replace: "true" }, { - match: /!\(0,\i\.isDesktop\)\(\)/, - replace: "false" + // FIXME(Bundler change related): Remove old compatiblity once enough time has passed + match: /(!)?\(0,\i\.isDesktop\)\(\)/, + replace: (_, not) => not ? "false" : "true" } ] }, diff --git a/src/plugins/permissionFreeWill/index.ts b/src/plugins/permissionFreeWill/index.ts index c45cbff60..8a6135145 100644 --- a/src/plugins/permissionFreeWill/index.ts +++ b/src/plugins/permissionFreeWill/index.ts @@ -46,8 +46,9 @@ export default definePlugin({ find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}", replacement: [ { - match: /{(\i:function\(\){return \i},?){2}}/, - replace: m => m.replaceAll(canonicalizeMatch(/return \i/g), "return ()=>Promise.resolve(true)") + // FIXME(Bundler change related): Remove old compatiblity once enough time has passed + match: /{(?:\i:(?:function\(\){return |\(\)=>)\i}?,?){2}}/, + replace: m => m.replaceAll(canonicalizeMatch(/(function\(\){return |\(\)=>)\i/g), "$1()=>Promise.resolve(true)") } ], predicate: () => settings.store.onboarding diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index 341971ff8..02662fe97 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -157,7 +157,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea src={user.getAvatarURL(void 0, void 0, false)} /> )} - + { permission.type === PermissionType.Role ? role?.name ?? "Unknown Role" diff --git a/src/plugins/permissionsViewer/styles.css b/src/plugins/permissionsViewer/styles.css index b7e420964..2ca61025d 100644 --- a/src/plugins/permissionsViewer/styles.css +++ b/src/plugins/permissionsViewer/styles.css @@ -73,7 +73,7 @@ background-color: var(--background-modifier-selected); } -.vc-permviewer-modal-list-item > div { +.vc-permviewer-modal-list-item-text { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index 17f7dfdd3..8c0fc6599 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; -import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useMemo, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -30,7 +30,7 @@ interface ColorPickerWithSwatchesProps { } const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)"); -const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); +const ColorPickerWithSwatches = findComponentByCodeLazy('id:"color-picker"'); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/pinDms/data.ts b/src/plugins/pinDms/data.ts index 2f4a1156e..d689bd2af 100644 --- a/src/plugins/pinDms/data.ts +++ b/src/plugins/pinDms/data.ts @@ -155,7 +155,7 @@ export function moveChannel(channelId: string, direction: -1 | 1) { 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() { if (Settings.plugins.PinDMs.dmSectioncollapsed != null) { settings.store.dmSectionCollapsed = Settings.plugins.PinDMs.dmSectioncollapsed; diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index d2b722eff..4612082f5 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -25,7 +25,7 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; @@ -70,7 +70,9 @@ const Icons = { }; type Platform = keyof typeof Icons; -const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); +const { useStatusFillColor } = mapMangledModuleLazy(".concat(.5625*", { + useStatusFillColor: filters.byCode(".hex") +}); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const tooltip = platform === "embedded" @@ -79,7 +81,7 @@ const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: const Icon = Icons[platform] ?? Icons.desktop; - return ; + return ; }; function ensureOwnStatus(user: User) { diff --git a/src/plugins/quickReply/index.ts b/src/plugins/quickReply/index.ts index 4a7060c59..f6ca5b459 100644 --- a/src/plugins/quickReply/index.ts +++ b/src/plugins/quickReply/index.ts @@ -196,7 +196,7 @@ function nextReply(isUp: boolean) { channel, message, shouldMention: shouldMention(message), - showMentionToggle: channel.isPrivate() && message.author.id !== meId, + showMentionToggle: !channel.isPrivate() && message.author.id !== meId, _isQuickReply: true }); ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS"); diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx index 4cd81f2ea..8d9789dd7 100644 --- a/src/plugins/reviewDB/auth.tsx +++ b/src/plugins/reviewDB/auth.tsx @@ -7,15 +7,12 @@ import { DataStore } from "@api/index"; import { Logger } from "@utils/Logger"; import { openModal } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { showToast, Toasts, UserStore } from "@webpack/common"; +import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common"; import { ReviewDBAuth } from "./entities"; const DATA_STORE_KEY = "rdb-auth"; -const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); - export let Auth: ReviewDBAuth = {}; export async function initAuth() { diff --git a/src/plugins/reviewDB/components/BlockedUserModal.tsx b/src/plugins/reviewDB/components/BlockedUserModal.tsx index 43c81eb52..8b8271746 100644 --- a/src/plugins/reviewDB/components/BlockedUserModal.tsx +++ b/src/plugins/reviewDB/components/BlockedUserModal.tsx @@ -39,7 +39,7 @@ function BlockedUser({ user, isBusy, setIsBusy }: { user: ReviewDBUser; isBusy: return (
- + {user.username} { diff --git a/src/plugins/reviewDB/components/ReviewModal.tsx b/src/plugins/reviewDB/components/ReviewModal.tsx index 71ac021f0..1485022da 100644 --- a/src/plugins/reviewDB/components/ReviewModal.tsx +++ b/src/plugins/reviewDB/components/ReviewModal.tsx @@ -65,7 +65,7 @@ function Modal({ modalProps, modalKey, discordId, name, type }: { modalProps: an -
+
{ownReview && ( ( diff --git a/src/plugins/reviewDB/style.css b/src/plugins/reviewDB/style.css index 190b8f620..c62c300e9 100644 --- a/src/plugins/reviewDB/style.css +++ b/src/plugins/reviewDB/style.css @@ -16,16 +16,11 @@ border: 1px solid var(--profile-message-input-border-color); } -.vc-rdb-modal-footer > div { +.vc-rdb-modal-footer-wrapper { width: 100%; margin: 6px 16px; } -/* When input becomes disabled(while sending review), input adds unneccesary padding to left, this prevents it */ -.vc-rdb-input > div > div { - padding-left: 0 !important; -} - .vc-rdb-placeholder { margin-bottom: 4px; font-weight: bold; @@ -69,7 +64,7 @@ border-radius: 8px; } -.vc-rdb-review-comment img { +.vc-rdb-review-comment [class*="avatar"] { vertical-align: text-top; } @@ -117,13 +112,13 @@ align-items: center; } -.vc-rdb-block-modal-row img { +.vc-rdb-block-modal-avatar { border-radius: 50%; height: 2em; width: 2em; } -.vc-rdb-block-modal img::before { +.vc-rdb-block-modal-avatar::before { content: ""; display: block; width: 100%; diff --git a/src/plugins/sendTimestamps/index.tsx b/src/plugins/sendTimestamps/index.tsx index 437df1a58..57b9de0df 100644 --- a/src/plugins/sendTimestamps/index.tsx +++ b/src/plugins/sendTimestamps/index.tsx @@ -68,15 +68,16 @@ function PickerModal({ rootProps, close }: { rootProps: ModalProps, close(): voi return ( - + Timestamp Picker - + setValue(e.currentTarget.value)} @@ -86,23 +87,25 @@ function PickerModal({ rootProps, close }: { rootProps: ModalProps, close(): voi /> Timestamp Format - ({ + label: m, + value: m + })) + } + isSelected={v => v === format} + select={v => setFormat(v)} + serialize={v => v} + renderOptionLabel={o => ( +
+ {Parser.parse(formatTimestamp(time, o.value))} +
+ )} + renderOptionValue={() => rendered} + /> +
Preview diff --git a/src/plugins/sendTimestamps/styles.css b/src/plugins/sendTimestamps/styles.css index 033d5c9d5..e7efbe59e 100644 --- a/src/plugins/sendTimestamps/styles.css +++ b/src/plugins/sendTimestamps/styles.css @@ -1,4 +1,4 @@ -.vc-st-modal-content input { +.vc-st-date-picker { background-color: var(--input-background); color: var(--text-normal); width: 95%; @@ -12,35 +12,28 @@ font-size: 100%; } -.vc-st-format-label, -.vc-st-format-label span { - background-color: transparent; -} - -.vc-st-modal-content [class|="select"] { +.vc-st-format-select { margin-bottom: 1em; + + --background-modifier-accent: transparent; } -.vc-st-modal-content [class|="select"] span { - background-color: var(--input-background); +.vc-st-format-label { + --background-modifier-accent: transparent; } .vc-st-modal-header { place-content: center space-between; } -.vc-st-modal-header h1 { +.vc-st-modal-title { margin: 0; } -.vc-st-modal-header button { +.vc-st-modal-close-button { padding: 0; } .vc-st-preview-text { margin-bottom: 1em; } - -.vc-st-button svg { - transform: scale(1.1) translateY(1px); -} diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index be77ca1c7..9f2d3008d 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -31,7 +31,8 @@ export function openGuildInfoModal(guild: Guild) { const enum Tabs { ServerInfo, Friends, - BlockedUsers + BlockedUsers, + IgnoredUsers } interface GuildProps { @@ -44,7 +45,8 @@ interface RelationshipProps extends GuildProps { const fetched = { friends: false, - blocked: false + blocked: false, + ignored: false }; function renderTimestamp(timestamp: number) { @@ -56,10 +58,12 @@ function renderTimestamp(timestamp: number) { function GuildInfoModal({ guild }: GuildProps) { const [friendCount, setFriendCount] = useState(); const [blockedCount, setBlockedCount] = useState(); + const [ignoredCount, setIgnoredCount] = useState(); useEffect(() => { fetched.friends = false; fetched.blocked = false; + fetched.ignored = false; }, []); const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo); @@ -90,6 +94,7 @@ function GuildInfoModal({ guild }: GuildProps) {
{iconUrl ? openImageModal({ @@ -132,12 +137,19 @@ function GuildInfoModal({ guild }: GuildProps) { > Blocked Users{blockedCount !== undefined ? ` (${blockedCount})` : ""} + + Ignored Users{ignoredCount !== undefined ? ` (${ignoredCount})` : ""} +
{currentTab === Tabs.ServerInfo && } {currentTab === Tabs.Friends && } {currentTab === Tabs.BlockedUsers && } + {currentTab === Tabs.IgnoredUsers && }
); @@ -159,6 +171,7 @@ function Owner(guildId: string, owner: User) { return (
openImageModal({ @@ -211,7 +224,13 @@ function BlockedUsersTab({ guild, setCount }: RelationshipProps) { return UserList("blocked", guild, blockedIds, setCount); } -function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setCount: (count: number) => void) { +function IgnoredUserTab({ guild, setCount }: RelationshipProps) { + const ignoredIds = Object.keys(RelationshipStore.getRelationships()).filter(id => RelationshipStore.isIgnored(id)); + return UserList("ignored", guild, ignoredIds, setCount); +} + + +function UserList(type: "friends" | "blocked" | "ignored", guild: Guild, ids: string[], setCount: (count: number) => void) { const missing = [] as string[]; const members = [] as string[]; diff --git a/src/plugins/serverInfo/styles.css b/src/plugins/serverInfo/styles.css index 8c88e4f40..274b7d138 100644 --- a/src/plugins/serverInfo/styles.css +++ b/src/plugins/serverInfo/styles.css @@ -21,7 +21,7 @@ margin: 0.5em; } -.vc-gp-header img { +.vc-gp-icon { width: 48px; height: 48px; cursor: pointer; @@ -82,7 +82,7 @@ gap: 0.2em; } -.vc-gp-owner img { +.vc-gp-owner-avatar { height: 20px; border-radius: 50%; cursor: pointer; diff --git a/src/plugins/shikiCodeblocks.desktop/components/Code.tsx b/src/plugins/shikiCodeblocks.desktop/components/Code.tsx index 8deca5882..2794234df 100644 --- a/src/plugins/shikiCodeblocks.desktop/components/Code.tsx +++ b/src/plugins/shikiCodeblocks.desktop/components/Code.tsx @@ -84,9 +84,9 @@ export const Code = ({ } const codeTableRows = lines.map((line, i) => ( - - {i + 1} - {line} + + {i + 1} + {line} )); diff --git a/src/plugins/shikiCodeblocks.desktop/components/Highlighter.tsx b/src/plugins/shikiCodeblocks.desktop/components/Highlighter.tsx index dd1401939..2d62af6e1 100644 --- a/src/plugins/shikiCodeblocks.desktop/components/Highlighter.tsx +++ b/src/plugins/shikiCodeblocks.desktop/components/Highlighter.tsx @@ -102,7 +102,7 @@ export const Highlighter = ({ color: themeBase.plainColor, }} > - +
); } diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 46629c770..f99c0be96 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -125,7 +125,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect {connection.name} {connection.verified && } - + } key={connection.id} diff --git a/src/plugins/showConnections/styles.css b/src/plugins/showConnections/styles.css index cead5201c..5bb16e0fd 100644 --- a/src/plugins/showConnections/styles.css +++ b/src/plugins/showConnections/styles.css @@ -14,6 +14,6 @@ word-break: break-all; } -.vc-sc-tooltip svg { +.vc-sc-tooltip-icon { min-width: 16px; } diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 15cd17c46..bbe286af5 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -18,6 +18,7 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; +import { classes } from "@utils/misc"; import { formatDuration } from "@utils/text"; import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; @@ -25,7 +26,7 @@ import type { Channel } from "discord-types/general"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions"; import { sortPermissionOverwrites } from "../../permissionsViewer/utils"; -import { settings } from ".."; +import { cl, settings } from ".."; const enum SortOrderTypes { LATEST_ACTIVITY = 0, @@ -168,19 +169,19 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { }, [channelId]); return ( -
-
- +
+
+ -
- This is a {!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) ? "hidden" : "locked"} {ChannelTypesToChannelNames[type]} channel. +
+ This is a {!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) ? "hidden" : "locked"} {ChannelTypesToChannelNames[type]} channel {channel.isNSFW() && {({ onMouseLeave, onMouseEnter }) => ( 0 && ( -
+
{Parser.parseTopic(topic, false, { channelId })}
)} @@ -213,7 +214,6 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { } - {lastPinTimestamp && Last message pin: } @@ -247,7 +247,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { Default sort order: {SortOrderTypesToNames[defaultSortOrder]} } {defaultReactionEmoji != null && -
+
Default reaction emoji: {Parser.defaultRules[defaultReactionEmoji.emojiName ? "emoji" : "customEmoji"].react({ name: defaultReactionEmoji.emojiName @@ -258,29 +258,29 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { src: defaultReactionEmoji.emojiName ? EmojiUtils.getURL(defaultReactionEmoji.emojiName) : void 0 - }, void 0, { key: "0" })} + }, void 0, { key: 0 })}
} {channel.hasFlag(ChannelFlags.REQUIRE_TAG) && Posts on this forum require a tag to be set. } {availableTags && availableTags.length > 0 && -
+
Available tags: -
+
{availableTags.map(tag => )}
} -
-
- {Settings.plugins.PermissionsViewer.enabled && ( +
+
+ {Vencord.Plugins.isPluginEnabled("PermissionsViewer") && ( {({ onMouseLeave, onMouseEnter }) => (