{
diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx
index c2e50cedd..963750fa3 100644
--- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx
+++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx
@@ -21,7 +21,7 @@ import { Flex } from "@components/Flex";
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
import { getUniqueUsername } from "@utils/discord";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
-import { ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
+import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
import type { Guild } from "discord-types/general";
import { settings } from "..";
@@ -112,7 +112,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
{
- if ((settings.store as any).unsafeViewAsRole && permission.type === PermissionType.Role)
+ if (permission.type === PermissionType.Role)
ContextMenuApi.openContextMenu(e, () => (
));
+ else if (permission.type === PermissionType.User) {
+ ContextMenuApi.openContextMenu(e, () => (
+
+ ));
+ }
}}
>
{(permission.type === PermissionType.Role || permission.type === PermissionType.Owner) && (
@@ -200,24 +208,53 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
aria-label="Role Options"
>
{
- const role = GuildStore.getRole(guild.id, roleId);
- if (!role) return;
+ Clipboard.copy(roleId);
+ }}
+ />
- onClose();
+ {(settings.store as any).unsafeViewAsRole && (
+ {
+ const role = GuildStore.getRole(guild.id, roleId);
+ if (!role) return;
- FluxDispatcher.dispatch({
- type: "IMPERSONATE_UPDATE",
- guildId: guild.id,
- data: {
- type: "ROLES",
- roles: {
- [roleId]: role
+ onClose();
+
+ FluxDispatcher.dispatch({
+ type: "IMPERSONATE_UPDATE",
+ guildId: guild.id,
+ data: {
+ type: "ROLES",
+ roles: {
+ [roleId]: role
+ }
}
- }
- });
+ });
+ }
+ }
+ />
+ )}
+
+ );
+}
+
+function UserContextMenu({ userId, onClose }: { userId: string; onClose: () => void; }) {
+ return (
+
+ {
+ Clipboard.copy(userId);
}}
/>
diff --git a/src/plugins/silentTyping/index.tsx b/src/plugins/silentTyping/index.tsx
index 8b59c6ace..2a6a64283 100644
--- a/src/plugins/silentTyping/index.tsx
+++ b/src/plugins/silentTyping/index.tsx
@@ -18,10 +18,11 @@
import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatButtons";
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
+import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
-import { FluxDispatcher, React } from "@webpack/common";
+import { FluxDispatcher, Menu, React } from "@webpack/common";
const settings = definePluginSettings({
showIcon: {
@@ -30,6 +31,11 @@ const settings = definePluginSettings({
description: "Show an icon for toggling the plugin",
restartNeeded: true,
},
+ contextMenu: {
+ type: OptionType.BOOLEAN,
+ description: "Add option to toggle the functionality in the chat input context menu",
+ default: true
+ },
isEnabled: {
type: OptionType.BOOLEAN,
description: "Toggle functionality",
@@ -56,13 +62,37 @@ const SilentTypingToggle: ChatBarButton = ({ isMainChat }) => {
);
};
+
+const ChatBarContextCheckbox: NavContextMenuPatchCallback = children => {
+ const { isEnabled, contextMenu } = settings.use(["isEnabled", "contextMenu"]);
+ if (!contextMenu) return;
+
+ const group = findGroupChildrenByChildId("submit-button", children);
+
+ if (!group) return;
+
+ const idx = group.findIndex(c => c?.props?.id === "submit-button");
+
+ group.splice(idx + 1, 0,
+ settings.store.isEnabled = !settings.store.isEnabled}
+ />
+ );
+};
+
+
export default definePlugin({
name: "SilentTyping",
- authors: [Devs.Ven, Devs.Rini],
+ authors: [Devs.Ven, Devs.Rini, Devs.ImBanana],
description: "Hide that you are typing",
dependencies: ["CommandsAPI", "ChatInputButtonAPI"],
settings,
-
+ contextMenus: {
+ "textarea-context": ChatBarContextCheckbox
+ },
patches: [
{
find: '.dispatch({type:"TYPING_START_LOCAL"',
diff --git a/src/plugins/themeAttributes/README.md b/src/plugins/themeAttributes/README.md
index 110eca574..87cb803c5 100644
--- a/src/plugins/themeAttributes/README.md
+++ b/src/plugins/themeAttributes/README.md
@@ -15,6 +15,7 @@ This allows themes to more easily theme those elements or even do things that ot
### Chat Messages
- `data-author-id` contains the id of the author
+- `data-author-username` contains the username of the author
- `data-is-self` is a boolean indicating whether this is the current user's message
![image](https://github.com/Vendicated/Vencord/assets/45497981/34bd5053-3381-402f-82b2-9c812cc7e122)
diff --git a/src/plugins/themeAttributes/index.ts b/src/plugins/themeAttributes/index.ts
index 8afc2121f..b8ceac621 100644
--- a/src/plugins/themeAttributes/index.ts
+++ b/src/plugins/themeAttributes/index.ts
@@ -36,10 +36,12 @@ export default definePlugin({
],
getMessageProps(props: { message: Message; }) {
- const authorId = props.message?.author?.id;
+ const author = props.message?.author;
+ const authorId = author?.id;
return {
"data-author-id": authorId,
- "data-is-self": authorId && authorId === UserStore.getCurrentUser()?.id
+ "data-author-username": author?.username,
+ "data-is-self": authorId && authorId === UserStore.getCurrentUser()?.id,
};
}
});
diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts
index fbda6861c..dd83eb7c5 100644
--- a/src/plugins/xsOverlay.desktop/index.ts
+++ b/src/plugins/xsOverlay.desktop/index.ts
@@ -1,6 +1,6 @@
/*
* Vencord, a Discord client mod
- * Copyright (c) 2023 Vendicated and contributors
+ * Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@@ -13,10 +13,7 @@ import { findByProps } from "@webpack";
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
-const enum ChannelTypes {
- DM = 1,
- GROUP_DM = 3
-}
+const { ChannelTypes } = findByProps("ChannelTypes");
interface Message {
guild_id: string,
@@ -72,14 +69,35 @@ interface Call {
}
const MuteStore = findByProps("isSuppressEveryoneEnabled");
+const Notifs = findByProps("makeTextChatNotification");
const XSLog = new Logger("XSOverlay");
const settings = definePluginSettings({
- ignoreBots: {
+ botNotifications: {
type: OptionType.BOOLEAN,
- description: "Ignore messages from bots",
+ description: "Allow bot notifications",
default: false
},
+ serverNotifications: {
+ type: OptionType.BOOLEAN,
+ description: "Allow server notifications",
+ default: true
+ },
+ dmNotifications: {
+ type: OptionType.BOOLEAN,
+ description: "Allow Direct Message notifications",
+ default: true
+ },
+ groupDmNotifications: {
+ type: OptionType.BOOLEAN,
+ description: "Allow Group DM notifications",
+ default: true
+ },
+ callNotifications: {
+ type: OptionType.BOOLEAN,
+ description: "Allow call notifications",
+ default: true
+ },
pingColor: {
type: OptionType.STRING,
description: "User mention color",
@@ -100,6 +118,11 @@ const settings = definePluginSettings({
description: "Notif duration (secs)",
default: 1.0,
},
+ timeoutPerCharacter: {
+ type: OptionType.NUMBER,
+ description: "Duration multiplier per character",
+ default: 0.5
+ },
opacity: {
type: OptionType.SLIDER,
description: "Notif opacity",
@@ -124,7 +147,7 @@ export default definePlugin({
settings,
flux: {
CALL_UPDATE({ call }: { call: Call; }) {
- if (call?.ringing?.includes(UserStore.getCurrentUser().id)) {
+ if (call?.ringing?.includes(UserStore.getCurrentUser().id) && settings.store.callNotifications) {
const channel = ChannelStore.getChannel(call.channel_id);
sendOtherNotif("Incoming call", `${channel.name} is calling you...`);
}
@@ -134,7 +157,7 @@ export default definePlugin({
try {
if (optimistic) return;
const channel = ChannelStore.getChannel(message.channel_id);
- if (!shouldNotify(message, channel)) return;
+ if (!shouldNotify(message, message.channel_id)) return;
const pingColor = settings.store.pingColor.replaceAll("#", "").trim();
const channelPingColor = settings.store.channelPingColor.replaceAll("#", "").trim();
@@ -194,6 +217,7 @@ export default definePlugin({
finalMsg = finalMsg.replace(/<@!?(\d{17,20})>/g, (_, id) => `@${UserStore.getUser(id)?.username || "unknown-user"}`);
}
+ // color role mentions (unity styling btw lol)
if (message.mention_roles.length > 0) {
for (const roleId of message.mention_roles) {
const role = GuildStore.getRole(channel.guild_id, roleId);
@@ -213,6 +237,7 @@ export default definePlugin({
}
}
+ // color channel mentions
if (channelMatches) {
for (const cMatch of channelMatches) {
let channelId = cMatch.split("<#")[1];
@@ -221,6 +246,7 @@ export default definePlugin({
}
}
+ if (shouldIgnoreForChannelType(channel)) return;
sendMsgNotif(titleString, finalMsg, message);
} catch (err) {
XSLog.error(`Failed to catch MESSAGE_CREATE: ${err}`);
@@ -229,13 +255,20 @@ export default definePlugin({
}
});
+function shouldIgnoreForChannelType(channel: Channel) {
+ if (channel.type === ChannelTypes.DM && settings.store.dmNotifications) return false;
+ if (channel.type === ChannelTypes.GROUP_DM && settings.store.groupDmNotifications) return false;
+ else return !settings.store.serverNotifications;
+}
+
function sendMsgNotif(titleString: string, content: string, message: Message) {
+ const timeout = Math.max(settings.store.timeout, content.length * settings.store.timeoutPerCharacter);
fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => {
const msgData = {
messageType: 1,
index: 0,
- timeout: settings.store.timeout,
- height: calculateHeight(cleanMessage(content)),
+ timeout,
+ height: calculateHeight(content),
opacity: settings.store.opacity,
volume: settings.store.volume,
audioPath: settings.store.soundPath,
@@ -254,7 +287,7 @@ function sendOtherNotif(content: string, titleString: string) {
messageType: 1,
index: 0,
timeout: settings.store.timeout,
- height: calculateHeight(cleanMessage(content)),
+ height: calculateHeight(content),
opacity: settings.store.opacity,
volume: settings.store.volume,
audioPath: settings.store.soundPath,
@@ -267,13 +300,11 @@ function sendOtherNotif(content: string, titleString: string) {
Native.sendToOverlay(msgData);
}
-function shouldNotify(message: Message, channel: Channel) {
+function shouldNotify(message: Message, channel: string) {
const currentUser = UserStore.getCurrentUser();
if (message.author.id === currentUser.id) return false;
- if (message.author.bot && settings.store.ignoreBots) return false;
- if (MuteStore.allowAllMessages(channel) || message.mention_everyone && !MuteStore.isSuppressEveryoneEnabled(message.guild_id)) return true;
-
- return message.mentions.some(m => m.id === currentUser.id);
+ if (message.author.bot && !settings.store.botNotifications) return false;
+ return Notifs.shouldNotify(message, channel);
}
function calculateHeight(content: string) {
@@ -282,7 +313,3 @@ function calculateHeight(content: string) {
if (content.length <= 300) return 200;
return 250;
}
-
-function cleanMessage(content: string) {
- return content.replace(new RegExp("<[^>]*>", "g"), "");
-}
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index a0c4752dc..09c27d15f 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -418,6 +418,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "Kyuuhachi",
id: 236588665420251137n,
},
+ nin0dev: {
+ name: "nin0dev",
+ id: 886685857560539176n
+ },
Elvyra: {
name: "Elvyra",
id: 708275751816003615n,
@@ -461,6 +465,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
GabiRP: {
name: "GabiRP",
id: 507955112027750401n
+ },
+ ImBanana: {
+ name: "Im_Banana",
+ id: 635250116688871425n
}
} satisfies Record);