mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-10 09:56:24 +00:00
bleh
This commit is contained in:
parent
20ed7dc96b
commit
63eb6358fb
7 changed files with 277 additions and 61 deletions
|
@ -220,6 +220,15 @@ export function migratePluginSettings(name: string, ...oldNames: string[]) {
|
|||
}
|
||||
}
|
||||
|
||||
export function migrateSettingsToArrays(pluginName: string, settings: Array<string>, stringSeparator: string = ",") {
|
||||
for (const setting of settings) {
|
||||
if (typeof SettingsStore.plain.plugins[pluginName][setting] !== "string") continue;
|
||||
logger.info(`Migrating setting ${setting} from ${pluginName} to list`);
|
||||
if (SettingsStore.plain.plugins[pluginName][setting] === "") SettingsStore.plain.plugins[pluginName][setting] = [];
|
||||
else SettingsStore.plain.plugins[pluginName][setting] = SettingsStore.plain.plugins[pluginName][setting].split(stringSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
export function definePluginSettings<
|
||||
Def extends SettingsDefinition,
|
||||
Checks extends SettingsChecks<Def>,
|
||||
|
|
|
@ -43,7 +43,8 @@ import {
|
|||
SettingNumericComponent,
|
||||
SettingSelectComponent,
|
||||
SettingSliderComponent,
|
||||
SettingTextComponent
|
||||
SettingTextComponent,
|
||||
SettingListComponent,
|
||||
} from "./components";
|
||||
import { openContributorModal } from "./ContributorModal";
|
||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||
|
@ -81,7 +82,11 @@ const Components: Record<OptionType, React.ComponentType<ISettingElementProps<an
|
|||
[OptionType.BOOLEAN]: SettingBooleanComponent,
|
||||
[OptionType.SELECT]: SettingSelectComponent,
|
||||
[OptionType.SLIDER]: SettingSliderComponent,
|
||||
[OptionType.COMPONENT]: SettingCustomComponent
|
||||
[OptionType.COMPONENT]: SettingCustomComponent,
|
||||
/* [OptionType.LIST]: SettingListComponent,
|
||||
[OptionType.USERS]: SettingListComponent,
|
||||
[OptionType.CHANNELS]: SettingListComponent,
|
||||
[OptionType.GUILDS]: SettingListComponent */
|
||||
};
|
||||
|
||||
export default function PluginModal({ plugin, onRestartNeeded, onClose, transitionState }: PluginModalProps) {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSelect } from "@utils/types";
|
||||
import { Forms, useState, useEffect, ChannelStore, UserStore, GuildStore } from "@webpack/common";
|
||||
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
export function SettingListComponent({ option, pluginSettings, definedSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [items, setItems] = useState<string[]>([]);
|
||||
const [newItem, setNewItem] = useState<string>("");
|
||||
|
||||
const addItem = () => {
|
||||
if (newItem.trim() !== "") {
|
||||
setItems([...items, newItem]);
|
||||
setNewItem("");
|
||||
}
|
||||
};
|
||||
|
||||
const removeItem = (index: number) => {
|
||||
setItems(items.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue) {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom16} type="description">{option.description}</Forms.FormText>
|
||||
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
|
@ -33,6 +33,7 @@ export interface ISettingElementProps<T extends PluginOptionBase> {
|
|||
export * from "../../Badge";
|
||||
export * from "./SettingBooleanComponent";
|
||||
export * from "./SettingCustomComponent";
|
||||
export * from "./SettingListComponent";
|
||||
export * from "./SettingNumericComponent";
|
||||
export * from "./SettingSelectComponent";
|
||||
export * from "./SettingSliderComponent";
|
||||
|
|
121
src/plugins/_api/settingLists.tsx
Normal file
121
src/plugins/_api/settingLists.tsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Menu, useState } from "@webpack/common";
|
||||
|
||||
function createContextMenu(name: "Guild" | "User" | "Channel", value: any) {
|
||||
return (
|
||||
<Menu.MenuItem
|
||||
id="vc-plugin-settings"
|
||||
label="Plugins"
|
||||
>
|
||||
{renderRegisteredPlugins(name, value)}
|
||||
</Menu.MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function renderRegisteredPlugins(name: "Guild" | "User" | "Channel", value: any) {
|
||||
const type = name === "Guild" ? OptionType.GUILDS : name === "User" ? OptionType.USERS : OptionType.CHANNELS;
|
||||
const plugins = registeredPlugins[type];
|
||||
|
||||
const [checkedItems, setCheckedItems] = useState<Record<string, boolean>>({});
|
||||
|
||||
const handleCheckboxClick = (plugin: string, setting: string) => {
|
||||
const key = `${plugin}-${setting}`;
|
||||
setCheckedItems(prevState => ({
|
||||
...prevState,
|
||||
[key]: !prevState[key]
|
||||
}));
|
||||
// @ts-ignore (cannot be undefined because settings have to exist for this to be called in the first place)
|
||||
const s = Vencord.Plugins.plugins[plugin].settings.store[setting];
|
||||
Vencord.Plugins.plugins[plugin].settings.store[setting] = s.includes(value.id)
|
||||
? s.filter(id => id !== value.id)
|
||||
: [...s, value.id];
|
||||
|
||||
};
|
||||
return Object.keys(plugins).map(plugin => (
|
||||
<Menu.MenuItem
|
||||
id={`vc-plugin-settings-${plugin}`}
|
||||
label={plugin}
|
||||
>
|
||||
{plugins[plugin].map(setting => (
|
||||
<Menu.MenuCheckboxItem
|
||||
id={`vc-plugin-settings-${plugin}-${setting}`}
|
||||
// @ts-ignore
|
||||
label={Vencord.Plugins.plugins[plugin].settings.def[setting].popoutText ?? setting}
|
||||
action={() => handleCheckboxClick(plugin, setting)}
|
||||
checked={checkedItems[`${plugin}-${setting}`]}
|
||||
/>
|
||||
))}
|
||||
</Menu.MenuItem>
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
function MakeContextCallback(name: "Guild" | "User" | "Channel"): NavContextMenuPatchCallback {
|
||||
return (children, props) => {
|
||||
const value = props[name.toLowerCase()];
|
||||
if (!value) return;
|
||||
if (props.label === getIntlMessage("CHANNEL_ACTIONS_MENU_LABEL")) return; // random shit like notification settings
|
||||
|
||||
const lastChild = children.at(-1);
|
||||
if (lastChild?.key === "developer-actions") {
|
||||
const p = lastChild.props;
|
||||
if (!Array.isArray(p.children))
|
||||
p.children = [p.children];
|
||||
|
||||
children = p.children;
|
||||
}
|
||||
children.splice(-1, 0,
|
||||
createContextMenu(name, value)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// {type: {plugin: [setting, setting, setting]}}
|
||||
const registeredPlugins: Record<OptionType.USERS | OptionType.GUILDS | OptionType.CHANNELS, Record<string, Array<string>>> = {
|
||||
[OptionType.USERS]: {},
|
||||
[OptionType.GUILDS]: {},
|
||||
[OptionType.CHANNELS]: {}
|
||||
};
|
||||
|
||||
|
||||
// TODO find a better name
|
||||
|
||||
export default definePlugin({
|
||||
name: "SettingListsAPI",
|
||||
description: "API to automatically add context menus for settings",
|
||||
authors: [Devs.Elvyra],
|
||||
contextMenus: {
|
||||
"channel-context": MakeContextCallback("Channel"),
|
||||
"thread-context": MakeContextCallback("Channel"),
|
||||
"guild-context": MakeContextCallback("Guild"),
|
||||
"user-context": MakeContextCallback("User")
|
||||
},
|
||||
|
||||
start() {
|
||||
for (const plugin of Object.values(Vencord.Plugins.plugins)) {
|
||||
if (!Vencord.Plugins.isPluginEnabled(plugin.name) || !plugin.settings) continue;
|
||||
const settings = plugin.settings.def;
|
||||
for (const settingKey of Object.keys(settings)) {
|
||||
const setting = settings[settingKey];
|
||||
if (setting.type === OptionType.USERS || setting.type === OptionType.GUILDS || setting.type === OptionType.CHANNELS) {
|
||||
if (!registeredPlugins[setting.type][plugin.name]) {
|
||||
registeredPlugins[setting.type][plugin.name] = [];
|
||||
}
|
||||
registeredPlugins[setting.type][plugin.name].push(settingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -20,7 +20,7 @@ import "./messageLogger.css";
|
|||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { updateMessage } from "@api/MessageUpdater";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings, migrateSettingsToArrays, Settings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
|
@ -36,6 +36,66 @@ import overlayStyle from "./deleteStyleOverlay.css?managed";
|
|||
import textStyle from "./deleteStyleText.css?managed";
|
||||
import { openHistoryModal } from "./HistoryModal";
|
||||
|
||||
migrateSettingsToArrays("MessageLogger", ["ignoreChannels", "ignoreGuilds", "ignoreUsers"]);
|
||||
|
||||
|
||||
const settings = definePluginSettings({
|
||||
deleteStyle: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The style of deleted messages",
|
||||
options: [
|
||||
{ label: "Red text", value: "text", default: true },
|
||||
{ label: "Red overlay", value: "overlay" }
|
||||
],
|
||||
onChange: () => addDeleteStyle()
|
||||
},
|
||||
logDeletes: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to log deleted messages",
|
||||
default: true,
|
||||
},
|
||||
collapseDeleted: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to collapse deleted messages, similar to blocked messages",
|
||||
default: false
|
||||
},
|
||||
logEdits: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to log edited messages",
|
||||
default: true,
|
||||
},
|
||||
inlineEdits: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to display edit history as part of message content",
|
||||
default: true
|
||||
},
|
||||
ignoreBots: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to ignore messages by bots",
|
||||
default: false
|
||||
},
|
||||
ignoreSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to ignore messages by yourself",
|
||||
default: false
|
||||
},
|
||||
ignoreUsers: {
|
||||
type: OptionType.USERS,
|
||||
description: "List of users to ignore",
|
||||
popoutText: "Ignore deleted messages from this user",
|
||||
},
|
||||
ignoreChannels: {
|
||||
type: OptionType.CHANNELS,
|
||||
description: "List of channel IDs to ignore",
|
||||
popoutText: "Ignore deleted messages in this channel"
|
||||
},
|
||||
ignoreGuilds: {
|
||||
type: OptionType.GUILDS,
|
||||
description: "List of guild IDs to ignore",
|
||||
popoutText: "Ignore deleted messages in this guild",
|
||||
},
|
||||
});
|
||||
|
||||
interface MLMessage extends Message {
|
||||
deleted?: boolean;
|
||||
editHistory?: { timestamp: Date; content: string; }[];
|
||||
|
@ -145,7 +205,7 @@ export default definePlugin({
|
|||
name: "MessageLogger",
|
||||
description: "Temporarily logs deleted and edited messages.",
|
||||
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi],
|
||||
dependencies: ["MessageUpdaterAPI"],
|
||||
dependencies: ["MessageUpdaterAPI", "SettingListsAPI"],
|
||||
|
||||
contextMenus: {
|
||||
"message": patchMessageContextMenu,
|
||||
|
@ -192,63 +252,7 @@ export default definePlugin({
|
|||
};
|
||||
},
|
||||
|
||||
options: {
|
||||
deleteStyle: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The style of deleted messages",
|
||||
default: "text",
|
||||
options: [
|
||||
{ label: "Red text", value: "text", default: true },
|
||||
{ label: "Red overlay", value: "overlay" }
|
||||
],
|
||||
onChange: () => addDeleteStyle()
|
||||
},
|
||||
logDeletes: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to log deleted messages",
|
||||
default: true,
|
||||
},
|
||||
collapseDeleted: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to collapse deleted messages, similar to blocked messages",
|
||||
default: false
|
||||
},
|
||||
logEdits: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to log edited messages",
|
||||
default: true,
|
||||
},
|
||||
inlineEdits: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to display edit history as part of message content",
|
||||
default: true
|
||||
},
|
||||
ignoreBots: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to ignore messages by bots",
|
||||
default: false
|
||||
},
|
||||
ignoreSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to ignore messages by yourself",
|
||||
default: false
|
||||
},
|
||||
ignoreUsers: {
|
||||
type: OptionType.STRING,
|
||||
description: "Comma-separated list of user IDs to ignore",
|
||||
default: ""
|
||||
},
|
||||
ignoreChannels: {
|
||||
type: OptionType.STRING,
|
||||
description: "Comma-separated list of channel IDs to ignore",
|
||||
default: ""
|
||||
},
|
||||
ignoreGuilds: {
|
||||
type: OptionType.STRING,
|
||||
description: "Comma-separated list of guild IDs to ignore",
|
||||
default: ""
|
||||
},
|
||||
},
|
||||
settings: settings,
|
||||
|
||||
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
|
||||
try {
|
||||
|
|
|
@ -167,6 +167,10 @@ export const enum OptionType {
|
|||
SELECT,
|
||||
SLIDER,
|
||||
COMPONENT,
|
||||
LIST,
|
||||
USERS, // List of users
|
||||
CHANNELS, // List of channels
|
||||
GUILDS, // List of guilds
|
||||
}
|
||||
|
||||
export type SettingsDefinition = Record<string, PluginSettingDef>;
|
||||
|
@ -183,6 +187,7 @@ export type PluginSettingDef = (
|
|||
| PluginSettingSliderDef
|
||||
| PluginSettingComponentDef
|
||||
| PluginSettingBigIntDef
|
||||
| PluginSettingListDef
|
||||
) & PluginSettingCommon;
|
||||
|
||||
export interface PluginSettingCommon {
|
||||
|
@ -259,6 +264,11 @@ export interface PluginSettingSliderDef {
|
|||
stickToMarkers?: boolean;
|
||||
}
|
||||
|
||||
export interface PluginSettingListDef{
|
||||
type: OptionType.LIST | OptionType.CHANNELS | OptionType.GUILDS | OptionType.USERS;
|
||||
popoutText?: string;
|
||||
}
|
||||
|
||||
export interface IPluginOptionComponentProps {
|
||||
/**
|
||||
* Run this when the value changes.
|
||||
|
|
Loading…
Reference in a new issue