Quoter: Fix & Shrink + Remove Old Finds

This commit is contained in:
thororen1234 2025-09-26 01:32:01 -04:00
parent 1f062fee89
commit 262336f436
No known key found for this signature in database
21 changed files with 161 additions and 300 deletions

View file

@ -40,13 +40,12 @@ import { openWarningModal } from "./PluginModal";
import { StockPluginsCard, UserPluginsCard } from "./PluginStatCards";
// Avoid circular dependency
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("plugins"));
const { stopPlugin } = proxyLazy(() => require("plugins"));
export const cl = classNameFactory("vc-plugins-");
export const logger = new Logger("PluginSettings", "#a6d189");
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
function showErrorToast(message: string) {
Toasts.show({

View file

@ -22,17 +22,6 @@ import definePlugin, { OptionType } from "@utils/types";
import { VoiceState } from "@vencord/discord-types";
import { ChannelStore, FluxDispatcher, GuildMemberStore, Toasts, UserStore, VoiceStateStore } from "@webpack/common";
interface ChannelState {
userId: string;
channelId: string;
deaf: boolean;
mute: boolean;
stream: boolean;
selfDeaf: boolean;
selfMute: boolean;
selfStream: boolean;
}
const settings = definePluginSettings({
port: {
type: OptionType.NUMBER,

View file

@ -178,12 +178,10 @@ function shouldHideBadgeOnUserProfiles(): boolean {
function shouldHideQuestPopup(quest: Quest | null): boolean {
const {
disableQuestsPopupAboveAccountPanel,
disableQuestsEverything,
triggerQuestsRerender
disableQuestsEverything
} = settings.use([
"disableQuestsPopupAboveAccountPanel",
"disableQuestsEverything",
"triggerQuestsRerender"
"disableQuestsEverything"
]);
const noProgress = !quest?.userStatus?.progress || Object.keys(quest?.userStatus?.progress || {}).length === 0;
@ -251,16 +249,12 @@ function QuestTileContextMenu(children: React.ReactNode[], props: { quest: any;
export function getQuestTileClasses(originalClasses: string, quest: Quest, color: number | null | undefined, gradient: string | undefined): string {
const {
ignoredQuestIDs,
ignoredQuestProfile,
restyleQuestsUnclaimed,
restyleQuestsClaimed,
restyleQuestsIgnored,
restyleQuestsExpired,
restyleQuestsGradient
} = settings.use([
"ignoredQuestIDs",
"ignoredQuestProfile",
"restyleQuestsUnclaimed",
"restyleQuestsClaimed",
"restyleQuestsIgnored",
@ -326,7 +320,7 @@ export function getQuestTileClasses(originalClasses: string, quest: Quest, color
}
function makeDesktopCompatible(quests: Quest[]): void {
const { makeMobileQuestsDesktopCompatible, triggerQuestsRerender } = settings.use(["makeMobileQuestsDesktopCompatible", "triggerQuestsRerender"]);
const { makeMobileQuestsDesktopCompatible } = settings.use(["makeMobileQuestsDesktopCompatible", "triggerQuestsRerender"]);
if (makeMobileQuestsDesktopCompatible) {
quests.forEach(quest => {
@ -358,27 +352,17 @@ function makeDesktopCompatible(quests: Quest[]): void {
function sortQuests(quests: Quest[], skip?: boolean): Quest[] {
const {
ignoredQuestIDs,
ignoredQuestProfile,
reorderQuests,
unclaimedSubsort,
claimedSubsort,
ignoredSubsort,
expiredSubsort,
completeVideoQuestsInBackground,
completeGameQuestsInBackground,
triggerQuestsRerender
expiredSubsort
} = settings.use([
"ignoredQuestIDs",
"ignoredQuestProfile",
"reorderQuests",
"unclaimedSubsort",
"claimedSubsort",
"ignoredSubsort",
"expiredSubsort",
"completeVideoQuestsInBackground",
"completeGameQuestsInBackground",
"triggerQuestsRerender"
"expiredSubsort"
]);
makeDesktopCompatible(quests);
@ -468,16 +452,6 @@ function sortQuests(quests: Quest[], skip?: boolean): Quest[] {
}
export function getQuestTileStyle(quest: Quest | null): Record<string, string> {
const {
restyleQuests,
ignoredQuestIDs,
ignoredQuestProfile
} = settings.use([
"restyleQuests",
"ignoredQuestIDs",
"ignoredQuestProfile"
]);
const style: Record<string, string> = {};
let themeColor: RGB | null = null;
@ -801,7 +775,6 @@ function disguiseHomeButton(location: string): boolean {
}
function useQuestRerender(): number {
const { triggerQuestsRerender } = settings.use(["triggerQuestsRerender"]);
const [renderTrigger, setRenderTrigger] = useState(0);
useEffect(() => addRerenderCallback(() => setRenderTrigger(prev => prev + 1)), []);
return renderTrigger;
@ -818,12 +791,10 @@ function getLastFilterChoices(): { group: string; filter: string; }[] | null {
}
function setLastSortChoice(sort: string): void {
const { rememberQuestPageFilters } = settings.use(["rememberQuestPageFilters"]);
settings.store.lastQuestPageSort = sort;
}
function setLastFilterChoices(filters: { group: string; filter: string; }[]): void {
const { rememberQuestPageFilters } = settings.use(["rememberQuestPageFilters"]);
if (!filters || !Object.keys(filters).length || !Object.values(filters).every(f => f.group && f.filter)) { return; }
settings.store.lastQuestPageFilters = JSON.parse(JSON.stringify(filters)).reduce((acc, item) => ({ ...acc, [item.filter]: item }), {});
}

View file

@ -364,7 +364,6 @@ function validateQuestButtonSetting() {
function validateDisableQuestSetting() {
const {
disableQuestsEverything,
disableQuestsDiscoveryTab,
disableQuestsFetchingQuests,
disableQuestsPopupAboveAccountPanel,
@ -372,7 +371,6 @@ function validateDisableQuestSetting() {
disableQuestsGiftInventoryRelocationNotice,
disableFriendsListActiveNowPromotion
} = settings.use([
"disableQuestsEverything",
"disableQuestsDiscoveryTab",
"disableQuestsFetchingQuests",
"disableQuestsPopupAboveAccountPanel",

View file

@ -1,10 +1,10 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { findGroupChildrenByChildId } from "@api/ContextMenu";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord";
@ -20,44 +20,28 @@ enum ImageStyle {
inspirational
}
const messagePatch: NavContextMenuPatchCallback = (children, { message }) => {
recentmessage = message;
if (!message.content) return;
const buttonElement =
<Menu.MenuItem
id="vc-quote"
label="Quote"
icon={QuoteIcon}
action={async () => {
openModal(props => <QuoteModal {...props} />);
}}
/>;
const group = findGroupChildrenByChildId("copy-text", children);
if (!group) {
children.push(buttonElement);
return;
}
group.splice(
group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, buttonElement
);
};
let recentmessage: Message;
let grayscale;
let setStyle: ImageStyle = ImageStyle.inspirational;
let customMessage: string = "";
enum userIDOptions {
displayName,
userName,
userId
}
interface QuoteImageOptions {
avatarUrl: string;
quoteOld: string;
grayScale: boolean;
imageStyle: ImageStyle;
author: {
username: string;
globalName?: string;
id: string;
};
userIdentifier: userIDOptions;
}
const settings = definePluginSettings({
userIdentifier:
{
userIdentifier: {
type: OptionType.SELECT,
description: "What the author's name should be displayed as",
options: [
@ -71,14 +55,28 @@ const settings = definePluginSettings({
export default definePlugin({
name: "Quoter",
description: "Adds the ability to create an inspirational quote image from a message",
authors: [Devs.Samwich],
authors: [Devs.Samwich, Devs.thororen],
settings,
contextMenus: {
"message": messagePatch
},
settings
"message": (children, { message }) => {
if (!message.content) return;
const buttonElement = (
<Menu.MenuItem
id="vc-quote"
label="Quote"
icon={QuoteIcon}
action={() => openModal(props => <QuoteModal message={message} {...props} />)}
/>
);
const group = findGroupChildrenByChildId("copy-text", children);
if (!group) children.push(buttonElement);
else group.splice(group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, buttonElement);
}
}
});
function sizeUpgrade(url) {
function sizeUpgrade(url: string) {
const u = new URL(url);
u.searchParams.set("size", "512");
return u.toString();
@ -87,68 +85,49 @@ function sizeUpgrade(url) {
const preparingSentence: string[] = [];
const lines: string[] = [];
async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: boolean): Promise<Blob> {
async function createQuoteImage(options: QuoteImageOptions): Promise<Blob> {
const { avatarUrl, quoteOld, grayScale, imageStyle, author, userIdentifier } = options;
const quote = FixUpQuote(quoteOld);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Cant get 2d rendering context :(");
if (!ctx) {
throw new Error("Cant get 2d rendering context :(");
}
let name: string = "";
switch (settings.store.userIdentifier) {
let name = "";
switch (userIdentifier) {
case userIDOptions.displayName:
// @ts-ignore
const meow = recentmessage.author.globalName;
if (meow) {
name = meow;
}
else {
name = recentmessage.author.username;
}
name = author.globalName || author.username;
break;
case userIDOptions.userName:
name = recentmessage.author.username;
name = author.username;
break;
case userIDOptions.userId:
name = recentmessage.author.id;
name = author.id;
break;
default:
name = "MAN WTF HAPPENED";
break;
name = "Unknown";
}
switch (setStyle) {
switch (imageStyle) {
case ImageStyle.inspirational:
const cardWidth = 1200;
const cardHeight = 600;
canvas.width = cardWidth;
canvas.height = cardHeight;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, cardWidth, cardHeight);
const avatarBlob = await fetchImageAsBlob(avatarUrl);
const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/refs/heads/main/icons/quoter/quoter.png");
const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/main/icons/quoter/quoter.png");
const avatar = new Image();
const fade = new Image();
const avatarPromise = new Promise<void>(resolve => {
avatar.onload = () => resolve();
avatar.src = URL.createObjectURL(avatarBlob);
});
const fadePromise = new Promise<void>(resolve => {
fade.onload = () => resolve();
fade.src = URL.createObjectURL(fadeBlob);
});
await Promise.all([avatarPromise, fadePromise]);
await Promise.all([
new Promise<void>(resolve => { avatar.onload = () => resolve(); avatar.src = URL.createObjectURL(avatarBlob); }),
new Promise<void>(resolve => { fade.onload = () => resolve(); fade.src = URL.createObjectURL(fadeBlob); })
]);
ctx.drawImage(avatar, 0, 0, cardHeight, cardHeight);
@ -164,42 +143,69 @@ async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale:
ctx.fillStyle = "#fff";
ctx.font = "italic 20px Georgia";
const quoteWidth = cardWidth / 2 - 50;
const quoteX = ((cardWidth - cardHeight));
const quoteX = cardWidth - cardHeight;
const quoteY = cardHeight / 2 - 10;
wrapText(ctx, `"${quote}"`, quoteX, quoteY, quoteWidth, 20, preparingSentence, lines);
const wrappedTextHeight = lines.length * 25;
ctx.font = "bold 16px Georgia";
const authorNameX = (cardHeight * 1.5) - (ctx.measureText(`- ${name}`).width / 2) - 30;
const authorNameX = cardHeight * 1.5 - ctx.measureText(`- ${name}`).width / 2 - 30;
const authorNameY = quoteY + wrappedTextHeight + 30;
ctx.fillText(`- ${name}`, authorNameX, authorNameY);
preparingSentence.length = 0;
lines.length = 0;
return await canvasToBlob(canvas);
}
}
function registerStyleChange(style) {
setStyle = style;
GeneratePreview();
function generateFileNamePreview(message: string) {
const words = message.split(" ");
return words.length >= 6 ? words.slice(0, 6).join(" ") : words.join(" ");
}
function QuoteModal(props: ModalProps) {
function QuoteModal({ message, ...props }: ModalProps & { message: Message; }) {
const [gray, setGray] = useState(true);
useEffect(() => {
grayscale = gray;
GeneratePreview();
}, [gray]);
const [style, setStyle] = useState(ImageStyle.inspirational);
const [quoteImage, setQuoteImage] = useState<Blob | null>(null);
const { userIdentifier } = settings.store;
const safeContent = message.content ? message.content : "";
const safeContent = recentmessage && recentmessage.content ? recentmessage.content : "";
const generateImage = async () => {
const image = await createQuoteImage({
avatarUrl: sizeUpgrade(message.author.getAvatarURL()),
quoteOld: safeContent,
grayScale: gray,
imageStyle: style,
author: message.author,
userIdentifier
});
setQuoteImage(image);
document.getElementById("quoterPreview")?.setAttribute("src", URL.createObjectURL(image));
};
const [custom, setCustom] = useState(safeContent);
useEffect(() => {
customMessage = custom;
GeneratePreview();
}, [custom]);
useEffect(() => { generateImage(); }, [gray, style, safeContent]);
const Export = () => {
if (!quoteImage) return;
const link = document.createElement("a");
const preview = generateFileNamePreview(safeContent);
link.href = URL.createObjectURL(quoteImage);
link.download = `${preview} - ${message.author.username}.png`;
link.click();
link.remove();
};
const SendInChat = () => {
if (!quoteImage) return;
const preview = generateFileNamePreview(safeContent);
const file = new File([quoteImage], `${preview} - ${message.author.username}.png`, { type: "image/png" });
// @ts-expect-error typing issue
UploadHandler.promptToUpload([file], getCurrentChannel(), 0);
props.onClose?.();
};
return (
<ModalRoot {...props} size={ModalSize.MEDIUM}>
@ -210,59 +216,27 @@ function QuoteModal(props: ModalProps) {
<ModalCloseButton onClick={props.onClose} />
</ModalHeader>
<ModalContent scrollbarType="none">
<img alt="" src="" id={"quoterPreview"} style={{ borderRadius: "20px", width: "100%" }}></img>
<br></br><br></br>
<img alt="" src="" id="quoterPreview" style={{ borderRadius: "20px", width: "100%" }} />
<br /><br />
<br /><br />
<Switch value={gray} onChange={setGray}>Grayscale</Switch>
<Select look={1}
options={Object.keys(ImageStyle).filter(key => isNaN(parseInt(key, 10))).map(key => ({
label: key.charAt(0).toUpperCase() + key.slice(1),
value: ImageStyle[key as keyof typeof ImageStyle]
}))}
select={v => registerStyleChange(v)} isSelected={v => v === setStyle}
serialize={v => v}></Select>
<Select
look={1}
options={Object.keys(ImageStyle)
.filter(key => isNaN(parseInt(key, 10)))
.map(key => ({
label: key.charAt(0).toUpperCase() + key.slice(1),
value: ImageStyle[key as keyof typeof ImageStyle]
}))}
select={v => setStyle(v)}
isSelected={v => v === style}
serialize={v => v}
/>
<br />
<Button color={Button.Colors.BRAND_NEW} size={Button.Sizes.SMALL} onClick={() => Export()} style={{ display: "inline-block", marginRight: "5px" }}>Export</Button>
<Button color={Button.Colors.BRAND_NEW} size={Button.Sizes.SMALL} onClick={() => SendInChat(props.onClose)} style={{ display: "inline-block" }}>Send</Button>
<Button color={Button.Colors.BRAND_NEW} size={Button.Sizes.SMALL} onClick={async () => await Export()} style={{ display: "inline-block", marginRight: "5px" }}>Export</Button>
<Button color={Button.Colors.BRAND_NEW} size={Button.Sizes.SMALL} onClick={async () => await SendInChat()} style={{ display: "inline-block" }}>Send</Button>
</ModalContent>
<br></br>
</ModalRoot>
);
}
async function SendInChat(onClose) {
const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale);
const preview = generateFileNamePreview(recentmessage.content);
const imageName = `${preview} - ${recentmessage.author.username}`;
const file = new File([image], `${imageName}.png`, { type: "image/png" });
// @ts-expect-error typing issue
UploadHandler.promptToUpload([file], getCurrentChannel(), 0);
onClose();
}
async function Export() {
const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale);
const link = document.createElement("a");
link.href = URL.createObjectURL(image);
const preview = generateFileNamePreview(recentmessage.content);
const imageName = `${preview} - ${recentmessage.author.username}`;
link.download = `${imageName}.png`;
link.click();
link.remove();
}
async function GeneratePreview() {
const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale);
document.getElementById("quoterPreview")?.setAttribute("src", URL.createObjectURL(image));
}
function generateFileNamePreview(message) {
const words = message.split(" ");
let preview;
if (words.length >= 6) {
preview = words.slice(0, 6).join(" ");
} else {
preview = words.join(" ");
}
return preview;
}

View file

@ -7,61 +7,39 @@
import { UserStore } from "@webpack/common";
export function canvasToBlob(canvas: HTMLCanvasElement): Promise<Blob> {
return new Promise<Blob>(resolve => {
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) {
resolve(blob);
} else {
throw new Error("Failed to create Blob");
}
blob ? resolve(blob) : reject(new Error("Failed to create Blob"));
}, "image/png");
});
}
export function wrapText(context: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number, preparingSentence: string[], lines: string[]) {
const words = text.split(" ");
for (let i = 0; i < words.length; i++) {
const workSentence = preparingSentence.join(" ") + " " + words[i];
if (context.measureText(workSentence).width > maxWidth) {
export function wrapText(ctx: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number, preparingSentence: string[], lines: string[]) {
text.split(" ").forEach(word => {
const workSentence = preparingSentence.join(" ") + " " + word;
if (ctx.measureText(workSentence).width > maxWidth) {
lines.push(preparingSentence.join(" "));
preparingSentence = [words[i]];
preparingSentence = [word];
} else {
preparingSentence.push(words[i]);
preparingSentence.push(word);
}
}
});
lines.push(preparingSentence.join(" "));
lines.forEach(element => {
const lineWidth = context.measureText(element).width;
const xOffset = (maxWidth - lineWidth) / 2;
lines.forEach(line => {
const xOffset = (maxWidth - ctx.measureText(line).width) / 2;
y += lineHeight;
context.fillText(element, x + xOffset, y);
ctx.fillText(line, x + xOffset, y);
});
}
export async function fetchImageAsBlob(url: string): Promise<Blob> {
if (!url) throw new Error("Invalid URL: URL is empty or undefined");
try {
if (!url) throw new Error("Invalid URL: URL is empty or undefined");
const response = await fetch(url, {
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Accept": "image/*"
}
});
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
return blob;
const response = await fetch(`https://corsproxy.io/?url=${encodeURIComponent(url)}`);
if (!response.ok) throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
return await response.blob();
} catch (error) {
console.error("Error fetching image:", error);
return new Blob([
@ -76,31 +54,18 @@ export async function fetchImageAsBlob(url: string): Promise<Blob> {
}
}
export function FixUpQuote(quote) {
const emojiRegex = /<a?:(\w+):(\d+)>/g;
quote = quote.replace(emojiRegex, "");
const mentionRegex = /<@(.*)>/;
let result = quote;
export function FixUpQuote(quote: string) {
try {
const matches = mentionRegex.exec(quote);
if (matches) {
matches.forEach(match => {
quote = quote.replace(/<a?:(\w+):(\d+)>/g, "");
const mentionMatch = /<@(.*?)>/.exec(quote);
if (mentionMatch) {
mentionMatch.forEach(match => {
try {
const userId = match.replace("<@", "").replace(">", "");
const user = UserStore.getUser(userId);
if (user && user.username) {
result = result.replace(match, `@${user.username}`);
}
} catch (err) {
console.error("Error processing mention:", err);
}
const user = UserStore.getUser(match.replace(/[<@>]/g, ""));
if (user?.username) quote = quote.replace(match, `@${user.username}`);
} catch { }
});
}
} catch (err) {
console.error("Error in FixUpQuote:", err);
}
return result;
} catch { }
return quote;
}

View file

@ -175,7 +175,7 @@ export default definePlugin({
}
],
flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
VOICE_STATE_UPDATES() {
const currentUserId = UserStore.getCurrentUser().id;
const myChannelId = VoiceStateStore.getVoiceStateForUser(currentUserId)?.channelId;
if (!myChannelId || !settings.store.leaveEmpty) return;

View file

@ -46,7 +46,7 @@ export default definePlugin({
if (!repliedMessage || repliedMessage.author.id !== user.id) return;
const whitelist = settings.store.replyPingWhitelist.split(",").map(id => id.trim());
const isWhitelisted = settings.store.replyPingWhitelist.includes(message.author.id);
const isWhitelisted = whitelist.includes(message.author.id);
if (isWhitelisted || settings.store.alwaysPingOnReply) {
if (!message.mentions.some(mention => mention.id === user.id)) {

View file

@ -5,7 +5,6 @@
*/
import { definePluginSettings } from "@api/Settings";
import { getUserSettingLazy } from "@api/UserSettings";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
@ -103,8 +102,6 @@ interface SFMR {
item: Item;
}
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
const applicationId = "1325126169179197500";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";

View file

@ -12,7 +12,6 @@ import { ApplicationAssetUtils, FluxDispatcher } from "@webpack/common";
import { BanchoStatusEnum, GameState, Modes, TosuApi, UserLoginStatus } from "./type";
const socketId = "tosu";
const OSU_APP_ID = "367827983903490050";
const OSU_LARGE_IMAGE = "373344233077211136";
const OSU_STARDARD_SMALL_IMAGE = "373370493127884800";

View file

@ -17,7 +17,6 @@
*/
import { definePluginSettings } from "@api/Settings";
import { getUserSettingLazy } from "@api/UserSettings";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
@ -44,8 +43,6 @@ const enum NameFormat {
AlbumName = "album"
}
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
const applicationId = "1108588077900898414";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";

View file

@ -312,7 +312,7 @@ export default definePlugin({
oldMsg === newMsg,
);
const { showEditDiffs, inlineEdits } = settings.use(["showEditDiffs", "inlineEdits"]);
const { inlineEdits } = settings.use(["inlineEdits"]);
return inlineEdits && (
<React.Fragment key={disabledDiffMessages.has(messageId) ? `diff-off-${messageId}` : `diff-on-${messageId}`}>

View file

@ -22,7 +22,6 @@ import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
const EMOTE = "<:luna:1035316192220553236>";
const DATA_KEY = "MessageTags_TAGS";
const MessageTagsMarker = Symbol("MessageTags");
interface Tag {

View file

@ -147,7 +147,7 @@ export default definePlugin({
},
filterStream(channelStream: [ChannelStreamGroupProps | ChannelStreamMessageProps | ChannelStreamDividerProps]) {
const { alsoHideIgnoredUsers, disableNotifications, hideBlockedUserReplies, allowAutoModMessages, defaultHideUsers, overrideUsers } = settings.use();
const { alsoHideIgnoredUsers } = settings.use();
const newChannelStream = channelStream.map(item => {
if (item.type === "MESSAGE_GROUP_BLOCKED" || (item.type === "MESSAGE_GROUP_IGNORED" && alsoHideIgnoredUsers)) {

View file

@ -17,11 +17,6 @@ export interface Category {
collapsed?: boolean;
}
const CATEGORY_BASE_KEY = "PinDMsCategories-";
const CATEGORY_MIGRATED_PINDMS_KEY = "PinDMsMigratedPinDMs";
const CATEGORY_MIGRATED_KEY = "PinDMsMigratedOldCategories";
const OLD_CATEGORY_KEY = "BetterPinDMsCategories-";
let forceUpdateDms: (() => void) | undefined = undefined;
export let currentUserCategories: Category[] = [];

View file

@ -80,7 +80,6 @@ const enum ChannelFlags {
const ChatScrollClasses = findByPropsLazy("auto", "managedReactiveScroller");
const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent");
const ChannelBeginHeader = findComponentByCodeLazy("#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}");
const TagComponent = findComponentByCodeLazy("#{intl::FORUM_TAG_A11Y_FILTER_BY_TAG}");

View file

@ -354,7 +354,7 @@ function renderUsername(
const isVoice = type === "voiceChannel";
const config = hookless ? settings.store : settings.use();
const { messages, replies, mentions, memberList, profilePopout, reactions, discriminators, hideDefaultAtSign, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, animateGradients, includedNames, friendNameColor, nicknameColor, displayNameColor, usernameColor, triggerNameRerender } = config;
const { messages, replies, mentions, memberList, profilePopout, reactions, discriminators, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, animateGradients, includedNames, friendNameColor, nicknameColor, displayNameColor, usernameColor } = config;
const canUseGradient = ((author as GuildMember)?.guildId ? (GuildStore.getGuild((author as GuildMember).guildId) ?? {}).premiumFeatures?.features.includes("ENHANCED_ROLE_COLORS") : false);
const textMutedValue = getComputedStyle(document.documentElement)?.getPropertyValue("--text-muted")?.trim() || "#72767d";
@ -921,22 +921,22 @@ export default definePlugin({
],
flux: {
RELATIONSHIP_UPDATE(data) {
RELATIONSHIP_UPDATE() {
// Allows rerendering when changing friend names.
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
},
RUNNING_STREAMER_TOOLS_CHANGE(data) {
RUNNING_STREAMER_TOOLS_CHANGE() {
// Allows rerendering when toggling streamer mode.
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
},
ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED(data) {
ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED() {
// Allows rerendering when toggling reduced motion.
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
},
ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION(data) {
ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION() {
// Allows rerendering when toggling reduced motion.
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
}

View file

@ -121,21 +121,11 @@ const SilentTypingChatToggle: ChatBarButtonFactory = ({ channel, type }) => {
const {
enabledGlobally,
chatIcon,
defaultHidden,
enabledLocations,
disabledLocations,
chatIconLeftClickAction,
chatIconMiddleClickAction,
chatIconRightClickAction,
defaultHidden
} = settings.use([
"enabledGlobally",
"chatIcon",
"defaultHidden",
"enabledLocations",
"disabledLocations",
"chatIconLeftClickAction",
"chatIconMiddleClickAction",
"chatIconRightClickAction",
"defaultHidden"
]);
const validChat = ["normal", "sidebar"].some(x => type.analyticsName === x);
@ -238,16 +228,12 @@ const ChatBarContextCheckbox: NavContextMenuPatchCallback = children => {
chatIcon,
chatContextMenu,
enabledGlobally,
defaultHidden,
hideChatBoxTypingIndicators,
hideMembersListTypingIndicators
defaultHidden
} = settings.use([
"chatIcon",
"chatContextMenu",
"enabledGlobally",
"defaultHidden",
"hideChatBoxTypingIndicators",
"hideMembersListTypingIndicators"
"defaultHidden"
]);
if (!chatContextMenu) return;

View file

@ -8,17 +8,13 @@ import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc";
import { Channel } from "@vencord/discord-types";
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
import { findByPropsLazy } from "@webpack";
import { ChannelRouter, ChannelStore, Parser, PermissionsBits, PermissionStore, React, showToast, Text, Toasts, Tooltip, useMemo, UserStore, UserSummaryItem, useStateFromStores, VoiceStateStore } from "@webpack/common";
import { PropsWithChildren } from "react";
const cl = classNameFactory("vc-uvs-");
const { selectVoiceChannel } = findByPropsLazy("selectVoiceChannel", "selectChannel");
const { useChannelName } = mapMangledModuleLazy("#{intl::GROUP_DM_ALONE}", {
useChannelName: filters.byCode("()=>null==")
});
const ActionButtonClasses = findByPropsLazy("actionButton", "highlight");
type IconProps = Omit<React.ComponentPropsWithoutRef<"div">, "children"> & {

View file

@ -24,10 +24,8 @@ import { Queue } from "@utils/Queue";
import { useForceUpdater } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { CustomEmoji, Message, ReactionEmoji, User } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, useEffect, useLayoutEffect, UserSummaryItem } from "@webpack/common";
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
let Scroll: any = null;
const queue = new Queue();
let reactions: Record<string, ReactionCacheEntry>;

View file

@ -90,7 +90,6 @@ interface NotificationObject {
}
const notificationsShouldNotify = findByCodeLazy(".SUPPRESS_NOTIFICATIONS))return!1");
const logger = new Logger("XSOverlay");
const settings = definePluginSettings({
webSocketPort: {