Merge branch 'dev'

This commit is contained in:
thororen1234 2025-07-04 10:22:52 -04:00
commit 8702dc5a36
No known key found for this signature in database
15 changed files with 171 additions and 68 deletions

View file

@ -5,7 +5,7 @@
*/
import { classNameFactory } from "@api/Styles";
import { getIntlMessage } from "@utils/discord";
import { getGuildAcronym, getIntlMessage } from "@utils/discord";
import { classes } from "@utils/misc";
import { closeModal, openModal } from "@utils/modal";
import { findComponentByCodeLazy } from "@webpack";
@ -47,7 +47,7 @@ function BookmarkIcon({ bookmark }: { bookmark: Bookmark | BookmarkFolder; }) {
className={cl("bookmark-icon")}
/>
: <div className={cl("bookmark-icon")}>
<Text variant="text-xxs/semibold" tag="span">{guild.acronym}</Text>
<Text variant="text-xxs/semibold" tag="span">{getGuildAcronym(guild)}</Text>
</div>;
if (channel?.recipients?.length) {

View file

@ -5,7 +5,7 @@
*/
import { classNameFactory } from "@api/Styles";
import { getIntlMessage, getUniqueUsername } from "@utils/discord";
import { getGuildAcronym, getIntlMessage, getUniqueUsername } from "@utils/discord";
import { classes } from "@utils/misc";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Avatar, ChannelStore, ContextMenuApi, GuildStore, PresenceStore, ReadStateStore, Text, TypingStore, useDrag, useDrop, useRef, UserStore, useStateFromStores } from "@webpack/common";
@ -37,7 +37,7 @@ const GuildIcon = ({ guild }: { guild: Guild; }) => {
className={cl("icon")}
/>
: <div className={cl("guild-acronym-icon")}>
<Text variant="text-xs/semibold" tag="span">{guild.acronym}</Text>
<Text variant="text-xs/semibold" tag="span">{getGuildAcronym(guild)}</Text>
</div>;
};

View file

@ -4,17 +4,34 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import definePlugin, { OptionType } from "@utils/types";
import { Constants, PermissionsBits, PermissionStore, React, RestAPI, useCallback, useEffect, useState } from "@webpack/common";
const showIcon = () => {
const [show, setShow] = useState(false);
const validKeycodes = [
"Backspace", "Tab", "Enter", "ShiftLeft", "ShiftRight", "ControlLeft", "ControlRight", "AltLeft", "AltRight", "Pause", "CapsLock",
"Escape", "Space", "PageUp", "PageDown", "End", "Home", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "PrintScreen", "Insert",
"Delete", "Digit0", "Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9", "KeyA", "KeyB", "KeyC",
"KeyD", "KeyE", "KeyF", "KeyG", "KeyH", "KeyI", "KeyJ", "KeyK", "KeyL", "KeyM", "KeyN", "KeyO", "KeyP", "KeyQ", "KeyR", "KeyS", "KeyT",
"KeyU", "KeyV", "KeyW", "KeyX", "KeyY", "KeyZ", "MetaLeft", "MetaRight", "ContextMenu", "Numpad0", "Numpad1", "Numpad2", "Numpad3",
"Numpad4", "Numpad5", "Numpad6", "Numpad7", "Numpad8", "Numpad9", "NumpadMultiply", "NumpadAdd", "NumpadSubtract", "NumpadDecimal",
"NumpadDivide", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "NumLock", "ScrollLock",
];
const handleKeys = useCallback(e => {
const keysHeld = e.ctrlKey && e.altKey;
setShow(keysHeld);
}, []);
function showIcon() {
const [show, setShow] = useState(false);
const { keyBind, reqCtrl, reqShift, reqAlt } = settings.store;
const handleKeys = useCallback((e: KeyboardEvent) => {
const isMatchingKey =
e.code === keyBind &&
(!reqCtrl || e.ctrlKey) &&
(!reqShift || e.shiftKey) &&
(!reqAlt || e.altKey);
setShow(isMatchingKey);
}, [keyBind, reqCtrl, reqShift, reqAlt]);
useEffect(() => {
window.addEventListener("keydown", handleKeys);
@ -27,12 +44,38 @@ const showIcon = () => {
}, [handleKeys]);
return show;
};
}
// TY ToggleVideoBind
const settings = definePluginSettings({
keyBind: {
description: "The key to toggle trash when pressed.",
type: OptionType.STRING,
default: "KeyZ",
isValid: (value: string) => validKeycodes.includes(value),
},
reqCtrl: {
description: "Require control to be held.",
type: OptionType.BOOLEAN,
default: true,
},
reqShift: {
description: "Require shift to be held.",
type: OptionType.BOOLEAN,
default: true,
},
reqAlt: {
description: "Require alt to be held.",
type: OptionType.BOOLEAN,
default: false,
},
});
export default definePlugin({
name: "FastDeleteChannels",
description: "Adds a trash icon to delete channels when holding ctrl + alt",
description: "Adds a trash icon to delete channels",
authors: [EquicordDevs.thororen],
settings,
patches: [
// TY TypingIndicator
{

View file

@ -5,6 +5,7 @@
*/
import { classNameFactory } from "@api/Styles";
import { getGuildAcronym } from "@utils/discord";
import { classes } from "@utils/misc";
import {
closeModal,
@ -25,14 +26,8 @@ import { HiddenServersStore } from "../HiddenServersStore";
const cl = classNameFactory("vc-hideservers-");
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
function HiddenServersModal({
modalProps,
close,
}: {
modalProps: ModalProps;
close(): void;
}) {
const servers = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail());
function HiddenServersModal({ modalProps, close }: { modalProps: ModalProps; close(): void; }) {
const guilds = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail());
return (
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
<ModalHeader>
@ -43,23 +38,26 @@ function HiddenServersModal({
</ModalHeader>
<ModalContent>
<HiddenServersMenu servers={servers} />
<HiddenServersMenu guilds={guilds} />
</ModalContent>
</ModalRoot>
);
}
export function HiddenServersMenu({ servers }: { servers: Guild[]; }) {
export function HiddenServersMenu({ guilds }: { guilds: Guild[]; }) {
return <div className={cl("list")}>
{servers.length > 0 ? (
servers.map(server => (
<div key={server.id} className={cl("row")}>
{guilds.length > 0 ? (
guilds.map(guild => (
<div key={guild.id} className={cl("row")}>
<div className={cl("guildicon")}>
{server.icon
? <img height="48" width="48"
{guild.icon
? <img
alt=""
height="48"
width="48"
src={IconUtils.getGuildIconURL({
id: server.id,
icon: server.icon,
id: guild.id,
icon: guild.icon,
canAnimate: true,
size: 240,
})} />
@ -69,17 +67,17 @@ export function HiddenServersMenu({ servers }: { servers: Guild[]; }) {
IconClasses.childWrapper,
IconClasses.acronym
)}>
{server.acronym}
{getGuildAcronym(guild)}
</div>
}
</div>
<Forms.FormTitle className={cl("name")}>
{server.name}
{guild.name}
</Forms.FormTitle>
<Button
className={"row-button"}
color={Button.Colors.PRIMARY}
onClick={() => HiddenServersStore.removeHidden(server.id)}
onClick={() => HiddenServersStore.removeHidden(guild.id)}
>
Remove
</Button>

View file

@ -30,7 +30,7 @@ export default definePluginSettings({
description: "Remove hidden servers",
component: () => {
const detail = useStateFromStores([HiddenServersStore], () => HiddenServersStore.hiddenGuildsDetail());
return <HiddenServersMenu servers={detail} />;
return <HiddenServersMenu guilds={detail} />;
}
},
resetHidden: {

View file

@ -43,7 +43,7 @@ export default definePlugin({
{
find: ".DELETE,onClick(){let",
replacement: {
match: /let \i=(\i).guild.toString\(\)/,
match: /let\{name:\i\}=\i\.guild/,
replace: "$self.HandleGuildDeleteModal($1);$&"
}
}

View file

@ -47,13 +47,13 @@ export const playSound = id => {
export async function downloadAudio(id: string): Promise<void> {
const filename = id + settings.store.FileType;
const data = await fetch(`https://cdn.discordapp.com/soundboard-sounds/${id}`).then(e => e.arrayBuffer());
const original = await fetch(`https://cdn.discordapp.com/soundboard-sounds/${id}`).then(res => res.arrayBuffer());
if (IS_DISCORD_DESKTOP) {
const data = new Uint8Array(original);
DiscordNative.fileManager.saveWithDialog(data, filename);
} else {
saveFile(new File([data], filename, { type: "audio/ogg" }));
saveFile(new File([original], filename, { type: "audio/ogg" }));
}
}

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
@ -56,8 +56,9 @@ function handleKeydown({ code, ctrlKey, shiftKey, altKey }: KeyboardEvent) {
});
}
migratePluginSettings("ToggleVideoBind", "toggleVideoBind");
export default definePlugin({
name: "toggleVideoBind",
name: "ToggleVideoBind",
description: "Adds a customizable bind to toggle webcam.",
authors: [EquicordDevs.mochienya],
settings,

View file

@ -9,7 +9,7 @@ import { Devs } from "@utils/constants";
import { isTruthy } from "@utils/guards";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { ApplicationAssetUtils, ChannelStore, FluxDispatcher, GuildStore, PresenceStore, RelationshipStore, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
import { ApplicationAssetUtils, ChannelStore, FluxDispatcher, GuildStore, IconUtils, PresenceStore, RelationshipStore, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
import { FluxStore } from "@webpack/types";
import { Channel } from "discord-types/general";
@ -169,6 +169,12 @@ const settings = definePluginSettings({
if (value && value < 0) return "End timestamp must be greater than 0.";
return true;
}
},
secretStuff: {
type: OptionType.STRING,
description: "A list of server/channel ids separated by commas, which are hidden from the RPC.",
onChange: onChange,
default: ""
}
});
@ -227,6 +233,10 @@ function getChannelIconURL(channel: Channel): string {
return chino;
}
function getSecretStuff(): string[] {
return settings.store.secretStuff.split(",").map(id => id.trim()).filter(Boolean);
}
async function createActivity(): Promise<Activity | undefined> {
const {
@ -260,7 +270,15 @@ async function createActivity(): Promise<Activity | undefined> {
const currentUser = UserStore.getCurrentUser();
if (userAvatarAsSmallImage) imageSmall = currentUser.getAvatarURL(undefined, undefined, true) || chino;
if (!channelId) {
const secretStuff = getSecretStuff();
const isSecret = secretStuff.includes(channelId) || secretStuff.includes(guildId);
if (isSecret) {
appName = "Hidden Channel/Guild";
details = "#secret";
state = "hehe";
imageBig = chino;
} else if (!channelId) {
appName = "Friends List";
details = `${onlineFriendCount()} online / ${totalFriendCount()} total`;
state = `${GuildStore.getGuildCount()} servers`;
@ -272,7 +290,7 @@ async function createActivity(): Promise<Activity | undefined> {
if (guild) {
details = guild.name;
state = memberCount();
imageBig = guild.getIconURL(128, true) || chino;
imageBig = IconUtils.getGuildIconURL({ id: guild.id, icon: guild.icon, canAnimate: true, size: 512 }) || chino;
if (guild.vanityURLCode) {
buttonOneText = `Join ${guild.name.slice(0, 26)}`;
buttonOneURL = `https://discord.gg/${guild.vanityURLCode}`;
@ -301,7 +319,7 @@ async function createActivity(): Promise<Activity | undefined> {
appName = `#${channel.name}`;
details = guild.name;
state = memberCount();
imageBig = guild.getIconURL(128, true) || chino;
imageBig = IconUtils.getGuildIconURL({ id: guild.id, icon: guild.icon, canAnimate: true, size: 512 }) || chino;
if (guild.vanityURLCode) {
buttonOneText = `Join ${guild.name.slice(0, 31 - 5)}`;
buttonOneURL = `https://discord.gg/${guild.vanityURLCode}`;

View file

@ -20,12 +20,13 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co
import { migratePluginSettings } from "@api/Settings";
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Devs } from "@utils/constants";
import { getGuildAcronym } from "@utils/discord";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
import definePlugin from "@utils/types";
import { findByCodeLazy } from "@webpack";
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, StickerStore, Toasts, Tooltip, UserStore } from "@webpack/common";
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, IconUtils, Menu, PermissionsBits, PermissionStore, React, RestAPI, StickerStore, Toasts, Tooltip, UserStore } from "@webpack/common";
import { Guild } from "discord-types/general";
import { Promisable } from "type-fest";
@ -249,13 +250,18 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
width: "100%",
height: "100%",
}}
src={g.getIconURL(512, true)}
src={IconUtils.getGuildIconURL({
id: g.id,
icon: g.icon,
canAnimate: true,
size: 512
})}
alt={g.name}
/>
) : (
<Forms.FormText
style={{
fontSize: getFontSize(g.acronym),
fontSize: getFontSize(getGuildAcronym(g)),
width: "100%",
overflow: "hidden",
whiteSpace: "nowrap",
@ -263,7 +269,7 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
cursor: isCloning ? "not-allowed" : "pointer",
}}
>
{g.acronym}
{getGuildAcronym(g)}
</Forms.FormText>
)}
</div>

View file

@ -18,7 +18,7 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { getIntlMessage, hasGuildFeature } from "@utils/discord";
import definePlugin from "@utils/types";
import { Constants, GuildStore, PermissionStore, RestAPI } from "@webpack/common";
@ -27,8 +27,8 @@ function showDisableInvites(guildId: string) {
if (!guild) return false;
return (
// @ts-ignore
!guild.hasFeature("INVITES_DISABLED") &&
// @ts-expect-error
!hasGuildFeature(guild, "INVITES_DISABLED") &&
PermissionStore.getGuildPermissionProps(guild).canManageRoles
);
}

View file

@ -7,6 +7,7 @@
import { DataStore } from "@api/index";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { hasGuildFeature } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { ChannelStore, GuildStore } from "@webpack/common";
@ -57,7 +58,7 @@ export default definePlugin({
{
find: "SUMMARIZEABLE.has",
replacement: {
match: /\i\.hasFeature\(\i\.\i\.SUMMARIES_ENABLED\w+?\)/g,
match: /\i\.features\.has\(\i\.\i\.SUMMARIES_ENABLED\w+?\)/g,
replace: "true"
}
},
@ -109,7 +110,8 @@ export default definePlugin({
const channel = ChannelStore.getChannel(channelId);
// SUMMARIES_ENABLED feature is not in discord-types
const guild = GuildStore.getGuild(channel.guild_id);
// @ts-ignore
return guild.hasFeature("SUMMARIES_ENABLED_GA");
// @ts-expect-error
return hasGuildFeature(guild, "SUMMARIES_ENABLED_GA");
}
});

View file

@ -7,7 +7,7 @@
import "./styles.css";
import { classNameFactory } from "@api/Styles";
import { openImageModal, openUserProfile } from "@utils/discord";
import { getGuildAcronym, openImageModal, openUserProfile } from "@utils/discord";
import { classes } from "@utils/misc";
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
@ -107,7 +107,7 @@ function GuildInfoModal({ guild }: GuildProps) {
width: 512,
})}
/>
: <div aria-hidden className={classes(IconClasses.childWrapper, IconClasses.acronym)}>{guild.acronym}</div>
: <div aria-hidden className={classes(IconClasses.childWrapper, IconClasses.acronym)}>{getGuildAcronym(guild)}</div>
}
<div className={cl("name-and-description")}>
@ -374,7 +374,7 @@ function MutualServerIcons({ member }: { member: MemberWithMutuals; }) {
{iconUrl ? (
<img src={iconUrl} alt="" />
) : (
<div className={cl("guild-acronym")}>{guild.acronym}</div>
<div className={cl("guild-acronym")}>{getGuildAcronym(guild)}</div>
)}
</div>
))}

View file

@ -48,6 +48,11 @@ const settings = definePluginSettings({
default: false,
description: "Use friend names in place of usernames (overrides Display Names option if applicable)"
},
showGradient: {
type: OptionType.BOOLEAN,
default: false,
description: "Whether to show gradient for suffix",
},
memberList: {
type: OptionType.BOOLEAN,
default: false,
@ -66,10 +71,11 @@ const settings = definePluginSettings({
description: "Show usernames in emoji reactions",
restartNeeded: true
},
showGradient: {
userProfilePopout: {
type: OptionType.BOOLEAN,
default: false,
description: "Whether to show gradient for suffix",
description: "Show usernames in user profile popout",
restartNeeded: true
},
});
@ -95,6 +101,16 @@ export default definePlugin({
replace: "$self.renderUsername(arguments[0])"
}
},
{
find: "#{intl::REACTION_TOOLTIP_1}",
predicate: () => settings.store.emojiReactions,
replacement: [
{
match: /\i\.\i\.getName\((\i),null==.{0,15},(\i)\)/,
replace: "$self.getUsername($2,$1)"
},
]
},
{
find: "._areActivitiesExperimentallyHidden=(",
predicate: () => settings.store.memberList,
@ -108,21 +124,27 @@ export default definePlugin({
predicate: () => settings.store.voiceChannelList,
replacement: [
{
match: /(?<=children:\[)null!=\i\?\i:\i\.\i\.getName\((\i)\)(?=.*?contextGuildId:(\i))/,
match: /null!=\i\?\i:\i\.\i\.getName\((\i)\)(?=.*?contextGuildId:(\i))/,
replace: "$self.getUsername($1,$2)"
},
]
},
{
find: "#{intl::REACTION_TOOLTIP_1}",
predicate: () => settings.store.emojiReactions,
replacement: [
{
match: /\i\.\i\.getName\((\i),null==.{0,15},(\i)\)/g,
replace: "$self.getUsername($2,$1)"
},
]
find: ".hasAvatarForGuild(null==",
predicate: () => settings.store.userProfilePopout,
replacement: {
match: /(?<=user:(\i).{0,15}\}\),nickname:)\i(?=.*?guildId:(null==\i\?void 0:\i\.id))/,
replace: "$self.getUsername($1,$2)"
}
},
{
find: "friendRequestBanner})",
predicate: () => settings.store.userProfilePopout,
replacement: {
match: /(?<=user:(\i).{0,15}guildId:(\i).*?nickname:)\i/,
replace: "$self.getUsername($1,$2)"
}
}
],
settings,
getUsername,

View file

@ -19,6 +19,7 @@
import { MessageObject } from "@api/MessageEvents";
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import { Channel, Guild, Message, User } from "discord-types/general";
import GuildFeatures from "discord-types/other/Constants";
import { Except } from "type-fest";
import { runtimeHashMessageKey } from "./intlHash";
@ -218,3 +219,15 @@ export function getEmojiURL(id: string, animated: boolean, size: number) {
const url = IconUtils.getEmojiURL({ id, animated, size });
return animated ? url.replace(".webp", ".gif") : url;
}
// Discord has a similar function in their code
export function getGuildAcronym(guild: Guild): string {
return guild.name
.replaceAll("'s ", " ")
.replace(/\w+/g, m => m[0])
.replace(/\s/g, "");
}
export function hasGuildFeature(guild: Guild, feature: keyof GuildFeatures["GuildFeatures"]): boolean {
return guild.features?.has(feature) ?? false;
}