You are using an outdated version of Vencord! Chances are, your issue is already fixed.
-
- Please first update using the Updater Page in Settings, or use the VencordInstaller (Update Vencord Button)
- to do so, in case you can't access the Updater page.
+
+ Please first update before asking for support!
+ You are using an externally updated Vencord version, which we do not provide support for!
+
+ Please either switch to an officially supported version of Vencord, or
+ contact your package maintainer for support instead.
+
+
+ You are using a fork of Vencord, which we do not provide support for!
+
+ Please either switch to an officially supported version of Vencord, or
+ contact your package maintainer for support instead.
+
+
,
+ onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
});
}
}
- }
+ },
+
+ ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
+ if (!isPluginDev(userId)) return null;
+ if (RelationshipStore.isFriend(userId)) return null;
+
+ return (
+
+ Please do not private message Vencord plugin developers for support!
+
+ Instead, use the Vencord support channel: {Parser.parse("https://discord.com/channels/1015060230222131221/1026515880080842772")}
+ {!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
+
+ );
+ }, { noop: true })
});
diff --git a/src/plugins/alwaysAnimate/index.ts b/src/plugins/alwaysAnimate/index.ts
index dbec3b4e3..20cb4f974 100644
--- a/src/plugins/alwaysAnimate/index.ts
+++ b/src/plugins/alwaysAnimate/index.ts
@@ -31,10 +31,10 @@ export default definePlugin({
// Some modules match the find but the replacement is returned untouched
noWarn: true,
replacement: {
- match: /canAnimate:.+?(?=([,}].*?\)))/g,
+ match: /canAnimate:.+?([,}].*?\))/g,
replace: (m, rest) => {
const destructuringMatch = rest.match(/}=.+/);
- if (destructuringMatch == null) return "canAnimate:!0";
+ if (destructuringMatch == null) return `canAnimate:!0${rest}`;
return m;
}
}
diff --git a/src/plugins/anonymiseFileNames/index.tsx b/src/plugins/anonymiseFileNames/index.tsx
index b424b7a59..526ccd12e 100644
--- a/src/plugins/anonymiseFileNames/index.tsx
+++ b/src/plugins/anonymiseFileNames/index.tsx
@@ -73,13 +73,13 @@ export default definePlugin({
{
find: "instantBatchUpload:function",
replacement: {
- match: /uploadFiles:(.{1,2}),/,
+ match: /uploadFiles:(\i),/,
replace:
"uploadFiles:(...args)=>(args[0].uploads.forEach(f=>f.filename=$self.anonymise(f)),$1(...args)),",
},
},
{
- find: "message.attachments",
+ find: 'addFilesTo:"message.attachments"',
replacement: {
match: /(\i.uploadFiles\((\i),)/,
replace: "$2.forEach(f=>f.filename=$self.anonymise(f)),$1"
diff --git a/src/plugins/automodContext/README.md b/src/plugins/automodContext/README.md
new file mode 100644
index 000000000..f70d71d90
--- /dev/null
+++ b/src/plugins/automodContext/README.md
@@ -0,0 +1,5 @@
+# AutomodContext
+
+Allows you to jump to the messages surrounding an automod hit
+
+![Visualization](https://github.com/Vendicated/Vencord/assets/61953774/d13740c8-2062-4553-b975-82fd3d6cc08b)
diff --git a/src/plugins/automodContext/index.tsx b/src/plugins/automodContext/index.tsx
new file mode 100644
index 000000000..5425c5526
--- /dev/null
+++ b/src/plugins/automodContext/index.tsx
@@ -0,0 +1,73 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import ErrorBoundary from "@components/ErrorBoundary";
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+import { findByPropsLazy } from "@webpack";
+import { Button, ChannelStore, Text } from "@webpack/common";
+
+const { selectChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel");
+
+function jumpToMessage(channelId: string, messageId: string) {
+ const guildId = ChannelStore.getChannel(channelId)?.guild_id;
+
+ selectChannel({
+ guildId,
+ channelId,
+ messageId,
+ jumpType: "INSTANT"
+ });
+}
+
+function findChannelId(message: any): string | null {
+ const { embeds: [embed] } = message;
+ const channelField = embed.fields.find(({ rawName }) => rawName === "channel_id");
+
+ if (!channelField) {
+ return null;
+ }
+
+ return channelField.rawValue;
+}
+
+export default definePlugin({
+ name: "AutomodContext",
+ description: "Allows you to jump to the messages surrounding an automod hit.",
+ authors: [Devs.JohnyTheCarrot],
+
+ patches: [
+ {
+ find: ".Messages.GUILD_AUTOMOD_REPORT_ISSUES",
+ replacement: {
+ match: /\.Messages\.ACTIONS.+?}\)(?=,(\(0.{0,40}\.dot.*?}\)),)/,
+ replace: (m, dot) => `${m},${dot},$self.renderJumpButton({message:arguments[0].message})`
+ }
+ }
+ ],
+
+ renderJumpButton: ErrorBoundary.wrap(({ message }: { message: any; }) => {
+ const channelId = findChannelId(message);
+
+ if (!channelId) {
+ return null;
+ }
+
+ return (
+
+ );
+ }, { noop: true })
+});
diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx
index 70e4070cd..38e1b8412 100644
--- a/src/plugins/betterFolders/index.tsx
+++ b/src/plugins/betterFolders/index.tsx
@@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
-import { FluxDispatcher, i18n } from "@webpack/common";
+import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
@@ -112,13 +112,13 @@ export default definePlugin({
replacement: [
// Create the isBetterFolders variable in the GuildsBar component
{
- match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
- replace: ",isBetterFolders"
+ match: /let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?(?=}=\i,)/,
+ replace: "$&,isBetterFolders"
},
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
{
- match: /(useStateFromStoresArray\).{0,25}let \i)=(\i\.\i.getGuildsTree\(\))/,
- replace: (_, rest, guildsTree) => `${rest}=$self.getGuildTree(!!arguments[0].isBetterFolders,${guildsTree},arguments[0].betterFoldersExpandedIds)`
+ match: /\[(\i)\]=(\(0,\i\.useStateFromStoresArray\).{0,40}getGuildsTree\(\).+?}\))(?=,)/,
+ replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0].isBetterFolders,betterFoldersOriginalTree,arguments[0].betterFoldersExpandedIds)`
},
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
{
@@ -127,7 +127,7 @@ export default definePlugin({
},
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
{
- match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/,
+ match: /unreadMentionsIndicatorBottom,.+?}\)\]/,
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
},
// Export the isBetterFolders variable to the folders component
@@ -252,19 +252,21 @@ export default definePlugin({
}
},
- getGuildTree(isBetterFolders: boolean, oldTree: any, expandedFolderIds?: Set) {
- if (!isBetterFolders || expandedFolderIds == null) return oldTree;
+ getGuildTree(isBetterFolders: boolean, originalTree: any, expandedFolderIds?: Set) {
+ return useMemo(() => {
+ if (!isBetterFolders || expandedFolderIds == null) return originalTree;
- const newTree = new GuildsTree();
- // Children is every folder and guild which is not in a folder, this filters out only the expanded folders
- newTree.root.children = oldTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id));
- // Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them
- newTree.nodes = Object.fromEntries(
- Object.entries(oldTree.nodes)
- .filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId))
- );
+ const newTree = new GuildsTree();
+ // Children is every folder and guild which is not in a folder, this filters out only the expanded folders
+ newTree.root.children = originalTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id));
+ // Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them
+ newTree.nodes = Object.fromEntries(
+ Object.entries(originalTree.nodes)
+ .filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId))
+ );
- return newTree;
+ return newTree;
+ }, [isBetterFolders, originalTree, expandedFolderIds]);
},
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
@@ -279,7 +281,7 @@ export default definePlugin({
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
return child => {
if (isBetterFolders) {
- return "onScroll" in child.props;
+ return child?.props?.onScroll != null;
}
return true;
};
diff --git a/src/plugins/betterNotes/index.tsx b/src/plugins/betterNotes/index.tsx
index 2183d98e2..cacdba5fd 100644
--- a/src/plugins/betterNotes/index.tsx
+++ b/src/plugins/betterNotes/index.tsx
@@ -17,6 +17,7 @@
*/
import { Settings } from "@api/Settings";
+import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types";
@@ -60,7 +61,7 @@ export default definePlugin({
find: ".popularApplicationCommandIds,",
replacement: {
match: /lastSection:(!?\i)}\),/,
- replace: "$&$self.patchPadding($1),"
+ replace: "$&$self.patchPadding({lastSection:$1}),"
}
}
],
@@ -80,10 +81,10 @@ export default definePlugin({
}
},
- patchPadding(lastSection: any) {
- if (!lastSection) return;
+ patchPadding: ErrorBoundary.wrap(({ lastSection }) => {
+ if (!lastSection) return null;
return (
-
+
);
- }
+ })
});
diff --git a/src/plugins/betterRoleContext/README.md b/src/plugins/betterRoleContext/README.md
index 3f3086bdb..e54e1e313 100644
--- a/src/plugins/betterRoleContext/README.md
+++ b/src/plugins/betterRoleContext/README.md
@@ -1,6 +1,6 @@
# BetterRoleContext
-Adds options to copy role color and edit role when right clicking roles in the user profile
+Adds options to copy role color, edit role and view role icon when right clicking roles in the user profile
-![](https://github.com/Vendicated/Vencord/assets/45497981/d1765e9e-7db2-4a3c-b110-139c59235326)
+![](https://github.com/Vendicated/Vencord/assets/45497981/354220a4-09f3-4c5f-a28e-4b19ca775190)
diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx
index 3db3494f9..ecb1ed400 100644
--- a/src/plugins/betterRoleContext/index.tsx
+++ b/src/plugins/betterRoleContext/index.tsx
@@ -4,9 +4,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+import { definePluginSettings } from "@api/Settings";
+import { ImageIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
-import { getCurrentGuild } from "@utils/discord";
-import definePlugin from "@utils/types";
+import { getCurrentGuild, openImageModal } from "@utils/discord";
+import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common";
@@ -34,10 +36,34 @@ function AppearanceIcon() {
);
}
+const settings = definePluginSettings({
+ roleIconFileFormat: {
+ type: OptionType.SELECT,
+ description: "File format to use when viewing role icons",
+ options: [
+ {
+ label: "png",
+ value: "png",
+ default: true
+ },
+ {
+ label: "webp",
+ value: "webp",
+ },
+ {
+ label: "jpg",
+ value: "jpg"
+ }
+ ]
+ }
+});
+
export default definePlugin({
name: "BetterRoleContext",
- description: "Adds options to copy role color / edit role when right clicking roles in the user profile",
- authors: [Devs.Ven],
+ description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile",
+ authors: [Devs.Ven, Devs.goodbee],
+
+ settings,
start() {
// DeveloperMode needs to be enabled for the context menu to be shown
@@ -63,6 +89,20 @@ export default definePlugin({
);
}
+ if (role.icon) {
+ children.push(
+ {
+ openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`);
+ }}
+ icon={ImageIcon}
+ />
+
+ );
+ }
+
if (PermissionStore.getGuildPermissionProps(guild).canManageRoles) {
children.push(
;
+waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m);
const settings = definePluginSettings({
disableFade: {
@@ -110,26 +111,33 @@ export default definePlugin({
{ // Load menu TOC eagerly
find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format",
replacement: {
- match: /(?<=(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,)(?=\1\(this)/,
- replace: "(async ()=>$2)(),"
+ match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/,
+ replace: "$&(async ()=>$2)(),"
},
predicate: () => settings.store.eagerLoad
},
{ // Settings cog context menu
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
replacement: {
- match: /\(0,\i.default\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/,
+ match: /\(0,\i.useDefaultUserSettingsSections\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/,
replace: "$self.wrapMenu($&)"
}
}
],
+ // This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
+ // without possibly also catching unrelated errors of children.
+ //
+ // Thus, we sanity check webpack modules & do this really hacky try catch to hopefully prevent hard crashes if something goes wrong.
+ // try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but
+ // not in children
Layer(props: LayerProps) {
- return (
- props.children as any}>
-
-
- );
+ if (!FocusLock || !ComponentDispatch || !Classes) {
+ new Logger("BetterSettings").error("Failed to find some components");
+ return props.children;
+ }
+
+ return ;
},
wrapMenu(list: SettingsEntry[]) {
diff --git a/src/plugins/colorSighted/index.ts b/src/plugins/colorSighted/index.ts
index d741aaae6..025cfb94e 100644
--- a/src/plugins/colorSighted/index.ts
+++ b/src/plugins/colorSighted/index.ts
@@ -34,9 +34,9 @@ export default definePlugin({
{
find: ".AVATAR_STATUS_MOBILE_16;",
replacement: {
- match: /(?<=fromIsMobile:\i=!0,.+?)status:(\i)/,
+ match: /(fromIsMobile:\i=!0,.+?)status:(\i)/,
// Rename field to force it to always use "online"
- replace: 'status_$:$1="online"'
+ replace: '$1status_$:$2="online"'
}
}
]
diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts
index e25e7cb30..b0efe8a08 100644
--- a/src/plugins/consoleShortcuts/index.ts
+++ b/src/plugins/consoleShortcuts/index.ts
@@ -19,10 +19,10 @@
import { Devs } from "@utils/constants";
import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
-import definePlugin from "@utils/types";
+import definePlugin, { StartAt } from "@utils/types";
import * as Webpack from "@webpack";
-import { extract, filters, findAll, search } from "@webpack";
-import { React, ReactDOM } from "@webpack/common";
+import { extract, filters, findAll, findModuleId, search } from "@webpack";
+import * as Common from "@webpack/common";
import type { ComponentType } from "react";
const WEB_ONLY = (f: string) => () => {
@@ -34,7 +34,7 @@ export default definePlugin({
description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.",
authors: [Devs.Ven],
- getShortcuts() {
+ getShortcuts(): Record {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
const cache = new Map();
@@ -64,16 +64,17 @@ export default definePlugin({
let fakeRenderWin: WeakRef | undefined;
const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps);
+
return {
- ...Vencord.Webpack.Common,
- wp: Vencord.Webpack,
- wpc: Webpack.wreq.c,
- wreq: Webpack.wreq,
+ ...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
+ wp: Webpack,
+ wpc: { getter: () => Webpack.cache },
+ wreq: { getter: () => Webpack.wreq },
wpsearch: search,
wpex: extract,
- wpexs: (code: string) => extract(Webpack.findModuleId(code)!),
+ wpexs: (code: string) => extract(findModuleId(code)!),
find,
- findAll,
+ findAll: findAll,
findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode),
@@ -82,10 +83,10 @@ export default definePlugin({
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName),
- PluginsApi: Vencord.Plugins,
- plugins: Vencord.Plugins.plugins,
- Settings: Vencord.Settings,
- Api: Vencord.Api,
+ PluginsApi: { getter: () => Vencord.Plugins },
+ plugins: { getter: () => Vencord.Plugins.plugins },
+ Settings: { getter: () => Vencord.Settings },
+ Api: { getter: () => Vencord.Api },
reload: () => location.reload(),
restart: IS_WEB ? WEB_ONLY("restart") : relaunch,
canonicalizeMatch,
@@ -115,21 +116,40 @@ export default definePlugin({
});
}
- ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
+ Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
}
};
},
+ startAt: StartAt.Init,
start() {
const shortcuts = this.getShortcuts();
- window.shortcutList = shortcuts;
- for (const [key, val] of Object.entries(shortcuts))
- window[key] = val;
+ window.shortcutList = {};
+
+ for (const [key, val] of Object.entries(shortcuts)) {
+ if (val.getter != null) {
+ Object.defineProperty(window.shortcutList, key, {
+ get: val.getter,
+ configurable: true,
+ enumerable: true
+ });
+
+ Object.defineProperty(window, key, {
+ get: () => window.shortcutList[key],
+ configurable: true,
+ enumerable: true
+ });
+ } else {
+ window.shortcutList[key] = val;
+ window[key] = val;
+ }
+ }
},
stop() {
delete window.shortcutList;
- for (const key in this.getShortcuts())
+ for (const key in this.getShortcuts()) {
delete window[key];
+ }
}
});
diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts
index f8c76d7f7..3297ca300 100644
--- a/src/plugins/crashHandler/index.ts
+++ b/src/plugins/crashHandler/index.ts
@@ -24,22 +24,20 @@ import { closeAllModals } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
import { maybePromptToUpdate } from "@utils/updater";
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
-import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
+import { DraftType, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
const CrashHandlerLogger = new Logger("CrashHandler");
-const { ModalStack, DraftManager, DraftType, closeExpressionPicker } = proxyLazyWebpack(() => {
- const modules = findBulk(
+
+const { ModalStack, DraftManager, closeExpressionPicker } = proxyLazyWebpack(() => {
+ const [ModalStack, DraftManager, ExpressionManager] = findBulk(
filters.byProps("pushLazy", "popAll"),
filters.byProps("clearDraft", "saveDraft"),
- filters.byProps("DraftType"),
- filters.byProps("closeExpressionPicker", "openExpressionPicker"),
- );
+ filters.byProps("closeExpressionPicker", "openExpressionPicker"),);
return {
- ModalStack: modules[0],
- DraftManager: modules[1],
- DraftType: modules[2]?.DraftType,
- closeExpressionPicker: modules[3]?.closeExpressionPicker,
+ ModalStack,
+ DraftManager,
+ closeExpressionPicker: ExpressionManager?.closeExpressionPicker,
};
});
@@ -104,7 +102,7 @@ export default definePlugin({
shouldAttemptRecover = false;
// This is enough to avoid a crash loop
- setTimeout(() => shouldAttemptRecover = true, 500);
+ setTimeout(() => shouldAttemptRecover = true, 1000);
} catch { }
try {
@@ -137,8 +135,11 @@ export default definePlugin({
try {
const channelId = SelectedChannelStore.getChannelId();
- DraftManager.clearDraft(channelId, DraftType.ChannelMessage);
- DraftManager.clearDraft(channelId, DraftType.FirstThreadMessage);
+ for (const key in DraftType) {
+ if (!Number.isNaN(Number(key))) continue;
+
+ DraftManager.clearDraft(channelId, DraftType[key]);
+ }
} catch (err) {
CrashHandlerLogger.debug("Failed to clear drafts.", err);
}
diff --git a/src/plugins/ctrlEnterSend/index.ts b/src/plugins/ctrlEnterSend/index.ts
new file mode 100644
index 000000000..4b9dd8e06
--- /dev/null
+++ b/src/plugins/ctrlEnterSend/index.ts
@@ -0,0 +1,68 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2023 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { definePluginSettings } from "@api/Settings";
+import { Devs } from "@utils/constants";
+import definePlugin, { OptionType } from "@utils/types";
+
+export default definePlugin({
+ name: "CtrlEnterSend",
+ authors: [Devs.UlyssesZhan],
+ description: "Use Ctrl+Enter to send messages (customizable)",
+ settings: definePluginSettings({
+ submitRule: {
+ description: "The way to send a message",
+ type: OptionType.SELECT,
+ options: [
+ {
+ label: "Ctrl+Enter (Enter or Shift+Enter for new line)",
+ value: "ctrl+enter"
+ },
+ {
+ label: "Shift+Enter (Enter for new line)",
+ value: "shift+enter"
+ },
+ {
+ label: "Enter (Shift+Enter for new line; Discord default)",
+ value: "enter"
+ }
+ ],
+ default: "ctrl+enter"
+ },
+ sendMessageInTheMiddleOfACodeBlock: {
+ description: "Whether to send a message in the middle of a code block",
+ type: OptionType.BOOLEAN,
+ default: true,
+ }
+ }),
+ patches: [
+ {
+ find: "KeyboardKeys.ENTER&&(!",
+ replacement: {
+ match: /(?<=(\i)\.which===\i\.KeyboardKeys.ENTER&&).{0,100}(\(0,\i\.hasOpenPlainTextCodeBlock\)\(\i\)).{0,100}(?=&&\(\i\.preventDefault)/,
+ replace: "$self.shouldSubmit($1, $2)"
+ }
+ }
+ ],
+ shouldSubmit(event: KeyboardEvent, codeblock: boolean): boolean {
+ let result = false;
+ switch (this.settings.store.submitRule) {
+ case "shift+enter":
+ result = event.shiftKey;
+ break;
+ case "ctrl+enter":
+ result = event.ctrlKey;
+ break;
+ case "enter":
+ result = !event.shiftKey && !event.ctrlKey;
+ break;
+ }
+ if (!this.settings.store.sendMessageInTheMiddleOfACodeBlock) {
+ result &&= !codeblock;
+ }
+ return result;
+ }
+});
diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx
index 334372e38..f1b2fbf53 100644
--- a/src/plugins/customRPC/index.tsx
+++ b/src/plugins/customRPC/index.tsx
@@ -17,13 +17,16 @@
*/
import { definePluginSettings, Settings } from "@api/Settings";
+import { ErrorCard } from "@components/ErrorCard";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import { isTruthy } from "@utils/guards";
+import { Margins } from "@utils/margins";
+import { classes } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
-import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
+import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, StatusSettingsStores, UserStore } from "@webpack/common";
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
@@ -386,17 +389,36 @@ async function setRpc(disable?: boolean) {
export default definePlugin({
name: "CustomRPC",
description: "Allows you to set a custom rich presence.",
- authors: [Devs.captain, Devs.AutumnVN],
+ authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev],
start: setRpc,
stop: () => setRpc(true),
settings,
settingsAboutComponent: () => {
const activity = useAwaiter(createActivity);
+ const gameActivityEnabled = StatusSettingsStores.ShowCurrentGame.useSetting();
const { profileThemeStyle } = useProfileThemeStyle({});
return (
<>
+ {!gameActivityEnabled && (
+
+ Notice
+ Game activity isn't enabled, people won't be able to see your custom rich presence!
+
+
+
+ )}
+
Go to Discord Developer Portal to create an application and
get the application ID.
@@ -407,7 +429,9 @@ export default definePlugin({
If you want to use image link, download your image and reupload the image to Imgur and get the image link by right-clicking the image and select "Copy image address".
-
+
+
+