port console janitor and other stuff

This commit is contained in:
Elvyra 2025-01-09 22:38:15 +01:00
parent 8af0bd52ad
commit b04d43dd8b
5 changed files with 57 additions and 135 deletions

View file

@ -224,7 +224,7 @@ export function migratePluginSettings(name: string, ...oldNames: string[]) {
}
}
export function migrateSettingsToArrays(pluginName: string, settings: string[], stringSeparator: string = ",") {
export function migrateSettingsToArrays(pluginName: string, settings: string[], stringSeparator: string | ((input: string) => string[]) = ",") {
const { plugins } = SettingsStore.plain;
for (const setting of settings) {
@ -232,7 +232,8 @@ export function migrateSettingsToArrays(pluginName: string, settings: string[],
logger.info(`Migrating setting ${setting} from ${pluginName} to list`);
// @ts-ignore
if (plugins[pluginName][setting] === "") plugins[pluginName][setting] = plugins[pluginName][setting].default ?? [];
else plugins[pluginName][setting] = plugins[pluginName][setting].split(stringSeparator);
else if (typeof stringSeparator === "string") plugins[pluginName][setting] = plugins[pluginName][setting].split(stringSeparator);
else plugins[pluginName][setting] = stringSeparator(plugins[pluginName][setting]);
}
}

View file

@ -6,13 +6,26 @@
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { debounce } from "@shared/debounce";
import SearchModal from "@components/SearchModal";
import { Margins } from "@utils/margins";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { closeModal, openModal } from "@utils/modal";
import { wordsFromCamel, wordsToTitle } from "@utils/text";
import { OptionType, PluginOptionArray } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Avatar, Button, ChannelStore, Flex, Forms, GuildStore, Heading, IconUtils, React, Text, TextInput, useCallback, useEffect, useRef, useState } from "@webpack/common";
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
import {
Avatar,
Button,
ChannelStore,
Flex,
Forms,
GuildStore,
IconUtils,
React,
Text,
TextInput,
useEffect,
useState,
} from "@webpack/common";
import { Channel, Guild } from "discord-types/general";
import { ISettingElementProps } from ".";
@ -21,28 +34,30 @@ const cl = classNameFactory("vc-plugin-modal-");
// TODO add interfaces for the stuff from the modal instead of using any
// TODO also add types to all the vars :husk: :husk:
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
const getDMChannelIcon = findByCodeLazy(".getChannelIconURL({");
const GroupDMAvatars = findComponentByCodeLazy(".AvatarSizeSpecs[", "getAvatarURL");
const SearchBarModule = findByPropsLazy("SearchBar", "Checkbox");
const SearchBarWrapper = findByPropsLazy("SearchBar", "Item");
const SearchHandler = findByCodeLazy("createSearchContext", "setLimit");
const CloseIcon = () => {
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="18" height="18">
<path d="M17.3 18.7a1 1 0 0 0 1.4-1.4L13.42 12l5.3-5.3a1 1 0 0 0-1.42-1.4L12 10.58l-5.3-5.3a1 1 0 0 0-1.4 1.42L10.58 12l-5.3 5.3a1 1 0 1 0 1.42 1.4L12 13.42l5.3 5.3Z" />
<path
d="M17.3 18.7a1 1 0 0 0 1.4-1.4L13.42 12l5.3-5.3a1 1 0 0 0-1.42-1.4L12 10.58l-5.3-5.3a1 1 0 0 0-1.4 1.42L10.58 12l-5.3 5.3a1 1 0 1 0 1.42 1.4L12 13.42l5.3 5.3Z"/>
</svg>;
};
const CheckMarkIcon = () => {
return <svg width="24" height="24" viewBox="0 0 24 24">
<path fill="currentColor" d="M21.7 5.3a1 1 0 0 1 0 1.4l-12 12a1 1 0 0 1-1.4 0l-6-6a1 1 0 1 1 1.4-1.4L9 16.58l11.3-11.3a1 1 0 0 1 1.4 0Z"></path>
<path fill="currentColor"
d="M21.7 5.3a1 1 0 0 1 0 1.4l-12 12a1 1 0 0 1-1.4 0l-6-6a1 1 0 1 1 1.4-1.4L9 16.58l11.3-11.3a1 1 0 0 1 1.4 0Z"/>
</svg>;
};
const SearchIcon = () => {
return <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
return <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"
strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="16" y1="16" x2="22" y2="22"></line>
</svg>;
@ -88,112 +103,15 @@ export function SettingArrayComponent({
setItems(pluginSettings[id]);
}
function SearchModal({ modalProps, close, val }: { modalProps: ModalProps; close(): void; val?: string; }) {
const [searchText, setSearchText] = useState<string>(val || "");
const [searchState, setSearchState] = useState({
results: [],
query: searchText
});
// channels:0, guilds:1, users:2
const [results, setResults] = useState<Record<string, any[]>>({
"channels": [],
"guilds": [],
"users": []
});
const [selected, setSelected] = useState<any[]>([]);
const onConfirm = () => {
};
const searchHandlerRef = useRef<typeof SearchHandler | null>(null);
useEffect(() => {
const handler = new SearchHandler((results, query) => {
setSearchState({
results,
query
});
});
searchHandlerRef.current = handler;
return () => {
handler.destroy();
};
}, []);
const search = useCallback(debounce(() => {
if (searchHandlerRef.current) {
searchHandlerRef.current.search(searchText.trim());
setResults({
"channels": [...searchHandlerRef.current._groupDMResults, ...searchHandlerRef.current._textChannelResults, ...searchHandlerRef.current._voiceChannelResults],
"guilds": searchHandlerRef.current._guildResults,
"users": searchHandlerRef.current._userResults
});
}
}, 300), [searchText]);
useEffect(() => {
search();
}, [searchText, search]);
return (
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
<ModalHeader
className={cl("search-modal-header")}
direction={Flex.Direction.VERTICAL}
align={Flex.Align.START}
justify={Flex.Justify.BETWEEN}
>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between", width: "100%", marginBottom: "8px" }}>
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
<Heading variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Search for {
option.type === OptionType.USERS ? "Users" : option.type === OptionType.CHANNELS ? "Channels" : "Guilds"
}</Heading>
<Heading variant="heading-sm/normal" style={{ color: "var(--header-muted)" }}>All selected items will be added to {wordsToTitle(wordsFromCamel(id))}</Heading>
</div>
<ModalCloseButton onClick={close} />
</div>
<SearchBarWrapper.SearchBar
size={SearchBarModule.SearchBar.Sizes.MEDIUM}
placeholder={"Search for a" + (option.type === OptionType.USERS ? " user" : option.type === OptionType.CHANNELS ? " channel" : " guild")}
query={searchText}
onChange={setSearchText}
autofocus={true}
/>
</ModalHeader>
<ModalContent>
</ModalContent>
<ModalFooter>
<Button
color={Button.Colors.BRAND}
onClick={onConfirm}
>
Confirm
</Button>
<Button
color={Button.Colors.TRANSPARENT}
look={Button.Looks.LINK}
onClick={close}
>
Cancel
</Button>
</ModalFooter>
</ModalRoot >
);
}
function openSearchModal(val?: string) {
const key = openModal(modalProps => (
<SearchModal
modalProps={modalProps}
close={() => closeModal(key)}
val={val}
input={val}
subText={"All selected items will be added to " + wordsToTitle(wordsFromCamel(id))}
searchType={option.type === OptionType.USERS ? "USERS" : option.type === OptionType.CHANNELS ? "CHANNELS" : "GUILDS"}
onSubmit={v => console.log(v)}
/>
));
}
@ -262,37 +180,36 @@ export function SettingArrayComponent({
switch (type) {
case 2:
return <svg style={{ color: " var(--channel-icon)" }} className={cl("icon")} aria-hidden="true" role="img" width="16" height="16" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1V3ZM15.1 20.75c-.58.14-1.1-.33-1.1-.92v-.03c0-.5.37-.92.85-1.05a7 7 0 0 0 0-13.5A1.11 1.11 0 0 1 14 4.2v-.03c0-.6.52-1.06 1.1-.92a9 9 0 0 1 0 17.5Z"></path>
<path fill="currentColor" d="M15.16 16.51c-.57.28-1.16-.2-1.16-.83v-.14c0-.43.28-.8.63-1.02a3 3 0 0 0 0-5.04c-.35-.23-.63-.6-.63-1.02v-.14c0-.63.59-1.1 1.16-.83a5 5 0 0 1 0 9.02Z"></path>
<path fill="currentColor" d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1V3ZM15.1 20.75c-.58.14-1.1-.33-1.1-.92v-.03c0-.5.37-.92.85-1.05a7 7 0 0 0 0-13.5A1.11 1.11 0 0 1 14 4.2v-.03c0-.6.52-1.06 1.1-.92a9 9 0 0 1 0 17.5Z"/>
<path fill="currentColor" d="M15.16 16.51c-.57.28-1.16-.2-1.16-.83v-.14c0-.43.28-.8.63-1.02a3 3 0 0 0 0-5.04c-.35-.23-.63-.6-.63-1.02v-.14c0-.63.59-1.1 1.16-.83a5 5 0 0 1 0 9.02Z"/>
</svg>;
case 5:
return <svg style={{ color: " var(--channel-icon)" }} className={cl("icon")} aria-hidden="true" role="img" width="16" height="16" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" fillRule="evenodd" d="M19.56 2a3 3 0 0 0-2.46 1.28 3.85 3.85 0 0 1-1.86 1.42l-8.9 3.18a.5.5 0 0 0-.34.47v10.09a3 3 0 0 0 2.27 2.9l.62.16c1.57.4 3.15-.56 3.55-2.12a.92.92 0 0 1 1.23-.63l2.36.94c.42.27.79.62 1.07 1.03A3 3 0 0 0 19.56 22h.94c.83 0 1.5-.67 1.5-1.5v-17c0-.83-.67-1.5-1.5-1.5h-.94Zm-8.53 15.8L8 16.7v1.73a1 1 0 0 0 .76.97l.62.15c.5.13 1-.17 1.12-.67.1-.41.29-.78.53-1.1Z" clipRule="evenodd"></path>
<path fill="currentColor" d="M2 10c0-1.1.9-2 2-2h.5c.28 0 .5.22.5.5v7a.5.5 0 0 1-.5.5H4a2 2 0 0 1-2-2v-4Z"></path>
<path fill="currentColor" fillRule="evenodd" d="M19.56 2a3 3 0 0 0-2.46 1.28 3.85 3.85 0 0 1-1.86 1.42l-8.9 3.18a.5.5 0 0 0-.34.47v10.09a3 3 0 0 0 2.27 2.9l.62.16c1.57.4 3.15-.56 3.55-2.12a.92.92 0 0 1 1.23-.63l2.36.94c.42.27.79.62 1.07 1.03A3 3 0 0 0 19.56 22h.94c.83 0 1.5-.67 1.5-1.5v-17c0-.83-.67-1.5-1.5-1.5h-.94Zm-8.53 15.8L8 16.7v1.73a1 1 0 0 0 .76.97l.62.15c.5.13 1-.17 1.12-.67.1-.41.29-.78.53-1.1Z" clipRule="evenodd"/>
<path fill="currentColor" d="M2 10c0-1.1.9-2 2-2h.5c.28 0 .5.22.5.5v7a.5.5 0 0 1-.5.5H4a2 2 0 0 1-2-2v-4Z"/>
</svg>;
case 13:
return <svg style={{ color: " var(--channel-icon)" }} className={cl("icon")} aria-hidden="true" role="img" width="16" height="16" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" d="M19.61 18.25a1.08 1.08 0 0 1-.07-1.33 9 9 0 1 0-15.07 0c.26.42.25.97-.08 1.33l-.02.02c-.41.44-1.12.43-1.46-.07a11 11 0 1 1 18.17 0c-.33.5-1.04.51-1.45.07l-.02-.02Z"></path>
<path fill="currentColor" d="M16.83 15.23c.43.47 1.18.42 1.45-.14a7 7 0 1 0-12.57 0c.28.56 1.03.6 1.46.14l.05-.06c.3-.33.35-.81.17-1.23A4.98 4.98 0 0 1 12 7a5 5 0 0 1 4.6 6.94c-.17.42-.13.9.18 1.23l.05.06Z"></path>
<path fill="currentColor" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM6.33 20.03c-.25.72.12 1.5.8 1.84a10.96 10.96 0 0 0 9.73 0 1.52 1.52 0 0 0 .8-1.84 6 6 0 0 0-11.33 0Z"></path>
<path fill="currentColor" d="M19.61 18.25a1.08 1.08 0 0 1-.07-1.33 9 9 0 1 0-15.07 0c.26.42.25.97-.08 1.33l-.02.02c-.41.44-1.12.43-1.46-.07a11 11 0 1 1 18.17 0c-.33.5-1.04.51-1.45.07l-.02-.02Z"/>
<path fill="currentColor" d="M16.83 15.23c.43.47 1.18.42 1.45-.14a7 7 0 1 0-12.57 0c.28.56 1.03.6 1.46.14l.05-.06c.3-.33.35-.81.17-1.23A4.98 4.98 0 0 1 12 7a5 5 0 0 1 4.6 6.94c-.17.42-.13.9.18 1.23l.05.06Z"/>
<path fill="currentColor" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM6.33 20.03c-.25.72.12 1.5.8 1.84a10.96 10.96 0 0 0 9.73 0 1.52 1.52 0 0 0 .8-1.84 6 6 0 0 0-11.33 0Z"/>
</svg>;
case 15:
return <svg style={{ color: " var(--channel-icon)" }} className={cl("icon")} aria-hidden="true" role="img" width="16" height="16" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" d="M18.91 12.98a5.45 5.45 0 0 1 2.18 6.2c-.1.33-.09.68.1.96l.83 1.32a1 1 0 0 1-.84 1.54h-5.5A5.6 5.6 0 0 1 10 17.5a5.6 5.6 0 0 1 5.68-5.5c1.2 0 2.32.36 3.23.98Z"></path>
<path fill="currentColor" d="M19.24 10.86c.32.16.72-.02.74-.38L20 10c0-4.42-4.03-8-9-8s-9 3.58-9 8c0 1.5.47 2.91 1.28 4.11.14.21.12.49-.06.67l-1.51 1.51A1 1 0 0 0 2.4 18h5.1a.5.5 0 0 0 .49-.5c0-4.2 3.5-7.5 7.68-7.5 1.28 0 2.5.3 3.56.86Z"></path>
<path fill="currentColor" d="M18.91 12.98a5.45 5.45 0 0 1 2.18 6.2c-.1.33-.09.68.1.96l.83 1.32a1 1 0 0 1-.84 1.54h-5.5A5.6 5.6 0 0 1 10 17.5a5.6 5.6 0 0 1 5.68-5.5c1.2 0 2.32.36 3.23.98Z"/>
<path fill="currentColor" d="M19.24 10.86c.32.16.72-.02.74-.38L20 10c0-4.42-4.03-8-9-8s-9 3.58-9 8c0 1.5.47 2.91 1.28 4.11.14.21.12.49-.06.67l-1.51 1.51A1 1 0 0 0 2.4 18h5.1a.5.5 0 0 0 .49-.5c0-4.2 3.5-7.5 7.68-7.5 1.28 0 2.5.3 3.56.86Z"/>
</svg>;
default: // Text channel icon
return <svg style={{ color: " var(--channel-icon)" }} className={cl("icon")} aria-hidden="true" role="img" width="16" height="16" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" fillRule="evenodd" d="M10.99 3.16A1 1 0 1 0 9 2.84L8.15 8H4a1 1 0 0 0 0 2h3.82l-.67 4H3a1 1 0 1 0 0 2h3.82l-.8 4.84a1 1 0 0 0 1.97.32L8.85 16h4.97l-.8 4.84a1 1 0 0 0 1.97.32l.86-5.16H20a1 1 0 1 0 0-2h-3.82l.67-4H21a1 1 0 1 0 0-2h-3.82l.8-4.84a1 1 0 1 0-1.97-.32L15.15 8h-4.97l.8-4.84ZM14.15 14l.67-4H9.85l-.67 4h4.97Z" clipRule="evenodd"></path>
<path fill="currentColor" fillRule="evenodd" d="M10.99 3.16A1 1 0 1 0 9 2.84L8.15 8H4a1 1 0 0 0 0 2h3.82l-.67 4H3a1 1 0 1 0 0 2h3.82l-.8 4.84a1 1 0 0 0 1.97.32L8.85 16h4.97l-.8 4.84a1 1 0 0 0 1.97.32l.86-5.16H20a1 1 0 1 0 0-2h-3.82l.67-4H21a1 1 0 1 0 0-2h-3.82l.8-4.84a1 1 0 1 0-1.97-.32L15.15 8h-4.97l.8-4.84ZM14.15 14l.67-4H9.85l-.67 4h4.97Z" clipRule="evenodd"/>
</svg>;
}
};
// collapsible guild list with channels in it
const channels: Record<string, Channel[]> = {};
const dmChannels: Channel[] = [];
const elements: React.JSX.Element[] = [];
@ -311,14 +228,14 @@ export function SettingArrayComponent({
channels[channel.guild_id].push(channel);
}
const userMention = channel => {
const userMention = (channel: Channel) => {
return <UserMentionComponent
userId={channel.recipients[0]}
className="mention"
/>;
};
const gdmComponent = channel => {
const gdmComponent = (channel: Channel) => {
return <span style={{ display: "inline-flex", alignItems: "center" }}>
{channel.recipients.length >= 2 && channel.icon == null ? (
<GroupDMAvatars recipients={channel.recipients} size="SIZE_16" />

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { definePluginSettings, migrateSettingsToArrays } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType, StartAt } from "@utils/types";
@ -24,6 +24,8 @@ const NoopLogger = {
const logAllow = new Set();
migrateSettingsToArrays("consoleJanitor", ["whitelistedLoggers"], s => s.split(";").map(x => x.trim()));
const settings = definePluginSettings({
disableLoggers: {
type: OptionType.BOOLEAN,
@ -38,12 +40,12 @@ const settings = definePluginSettings({
restartNeeded: true
},
whitelistedLoggers: {
type: OptionType.STRING,
description: "Semi colon separated list of loggers to allow even if others are hidden",
default: "GatewaySocket; Routing/Utils",
onChange(newVal: string) {
type: OptionType.ARRAY,
description: "List of loggers to allow even if others are hidden",
default: ["GatewaySocket", "Routing/Utils"],
onChange(newVal: string[]) {
logAllow.clear();
newVal.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
newVal.forEach(logAllow.add.bind(logAllow));
}
}
});
@ -57,7 +59,7 @@ export default definePlugin({
startAt: StartAt.Init,
start() {
logAllow.clear();
this.settings.store.whitelistedLoggers?.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow));
this.settings.store.whitelistedLoggers.forEach(logAllow.add.bind(logAllow));
},
NoopLogger: () => NoopLogger,

View file

@ -93,7 +93,7 @@ const ChatBarIcon: ChatBarButton = ({ isMainChat }) => {
};
migrateSettingsToArrays("InvisibleChat", ["savedPasswords"]);
migrateSettingsToArrays("InvisibleChat", ["savedPasswords"], s => s.split(",").map(s => s.trim()));
const settings = definePluginSettings({
savedPasswords: {
@ -209,7 +209,7 @@ export function isCorrectPassword(result: string): boolean {
}
export async function iteratePasswords(message: Message): Promise<string | false> {
const passwords = settings.store.savedPasswords.map(s => s.trim());
const passwords = settings.store.savedPasswords;
if (!message?.content || !passwords?.length) return false;

View file

@ -279,6 +279,8 @@ export interface PluginSettingArrayDef {
popoutText?: string;
hidePopout?: boolean;
default?: any[];
onChange?(newValue: any[]): void;
}
export interface IPluginOptionComponentProps {