mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-10 09:56:24 +00:00
add lists to plugin modals (super shitty)
also fixes some bugs lol
This commit is contained in:
parent
826162952c
commit
4f2a87a610
6 changed files with 113 additions and 31 deletions
|
@ -40,11 +40,11 @@ import {
|
|||
ISettingElementProps,
|
||||
SettingBooleanComponent,
|
||||
SettingCustomComponent,
|
||||
SettingListComponent,
|
||||
SettingNumericComponent,
|
||||
SettingSelectComponent,
|
||||
SettingSliderComponent,
|
||||
SettingTextComponent,
|
||||
SettingListComponent,
|
||||
} from "./components";
|
||||
import { openContributorModal } from "./ContributorModal";
|
||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||
|
@ -83,10 +83,10 @@ const Components: Record<OptionType, React.ComponentType<ISettingElementProps<an
|
|||
[OptionType.SELECT]: SettingSelectComponent,
|
||||
[OptionType.SLIDER]: SettingSliderComponent,
|
||||
[OptionType.COMPONENT]: SettingCustomComponent,
|
||||
/* [OptionType.LIST]: SettingListComponent,
|
||||
[OptionType.LIST]: SettingListComponent,
|
||||
[OptionType.USERS]: SettingListComponent,
|
||||
[OptionType.CHANNELS]: SettingListComponent,
|
||||
[OptionType.GUILDS]: SettingListComponent */
|
||||
[OptionType.GUILDS]: SettingListComponent
|
||||
};
|
||||
|
||||
export default function PluginModal({ plugin, onRestartNeeded, onClose, transitionState }: PluginModalProps) {
|
||||
|
|
|
@ -16,14 +16,39 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
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 { OptionType, PluginOptionList } from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Button,ChannelStore, Forms, GuildStore, React, useState } from "@webpack/common";
|
||||
|
||||
import { ISettingElementProps } from ".";
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
export function SettingListComponent({ option, pluginSettings, definedSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
|
||||
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
||||
|
||||
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" />
|
||||
</svg>;
|
||||
};
|
||||
|
||||
interface UserMentionComponentProps {
|
||||
id: string;
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
}
|
||||
|
||||
export function SettingListComponent({
|
||||
option,
|
||||
pluginSettings,
|
||||
definedSettings,
|
||||
onChange,
|
||||
onError,
|
||||
id
|
||||
}: ISettingElementProps<PluginOptionList>) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [items, setItems] = useState<string[]>([]);
|
||||
|
@ -34,31 +59,75 @@ export function SettingListComponent({ option, pluginSettings, definedSettings,
|
|||
setItems([...items, newItem]);
|
||||
setNewItem("");
|
||||
}
|
||||
pluginSettings[id] = items;
|
||||
};
|
||||
|
||||
if (items.length === 0 && pluginSettings[id].length !== 0) {
|
||||
setItems(pluginSettings[id]);
|
||||
}
|
||||
|
||||
const removeItem = (index: number) => {
|
||||
setItems(items.filter((_, i) => i !== index));
|
||||
pluginSettings[id] = items;
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function wrapChannel(id: string) {
|
||||
const channel = ChannelStore.getChannel(id) as Channel;
|
||||
if (!channel) {
|
||||
return "Unknown Channel";
|
||||
}
|
||||
return (GuildStore.getGuild(channel.guild_id)?.name ?? "Unknown Guild") + " - " + channel.name;
|
||||
}
|
||||
|
||||
// FIXME make channels and guilds nicer!
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom16} type="description">{option.description}</Forms.FormText>
|
||||
<ErrorBoundary noop>
|
||||
{items.map((item, index) => (
|
||||
<React.Fragment key={`${item}-${index}`}>
|
||||
<Flex
|
||||
flexDirection="row"
|
||||
style={{
|
||||
gap: "1px",
|
||||
}}
|
||||
>
|
||||
{option.type === OptionType.USERS ? (
|
||||
<UserMentionComponent
|
||||
userId={item}
|
||||
className="mention"
|
||||
/>
|
||||
) : option.type === OptionType.CHANNELS ? (
|
||||
<span style={{ color: "white" }}>{wrapChannel(item)}</span>
|
||||
) : option.type === OptionType.GUILDS ? (
|
||||
<span style={{ color: "white" }}>
|
||||
{GuildStore.getGuild(item)?.name || "Unknown Guild"}
|
||||
</span>
|
||||
// TODO add logo to guild and channel?
|
||||
) : (
|
||||
<span>{item}</span>
|
||||
)}
|
||||
<Button
|
||||
size={Button.Sizes.MIN}
|
||||
onClick={() => removeItem(index)}
|
||||
style={
|
||||
{ background: "none", }
|
||||
}
|
||||
>
|
||||
<CloseIcon />
|
||||
</Button>
|
||||
</Flex>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
</ErrorBoundary>
|
||||
|
||||
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
|
|
|
@ -8,7 +8,7 @@ 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";
|
||||
import { Menu, React } from "@webpack/common";
|
||||
|
||||
function createContextMenu(name: "Guild" | "User" | "Channel", value: any) {
|
||||
return (
|
||||
|
@ -26,16 +26,24 @@ 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 [checkedItems, setCheckedItems] = React.useState<Record<string, boolean>>(
|
||||
Object.fromEntries(
|
||||
Object.keys(plugins).flatMap(plugin =>
|
||||
plugins[plugin].map(setting => [`${plugin}-${setting}-${value.id}`, Vencord.Plugins.plugins[plugin].settings?.store[setting].includes(value.id)])
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const handleCheckboxClick = (plugin: string, setting: string) => {
|
||||
const key = `${plugin}-${setting}`;
|
||||
const key = `${plugin}-${setting}-${value.id}`;
|
||||
setCheckedItems(prevState => ({
|
||||
...prevState,
|
||||
[key]: !prevState[key]
|
||||
}));
|
||||
// @ts-ignore (can't be undefined because settings have to exist for this to be called in the first place)
|
||||
// @ts-ignore settings must be defined otherwise we wouldn't be here
|
||||
const s = Vencord.Plugins.plugins[plugin].settings.store[setting];
|
||||
// @ts-ignore
|
||||
Vencord.Plugins.plugins[plugin].settings.store[setting] = s.includes(value.id)
|
||||
? s.filter(id => id !== value.id)
|
||||
: [...s, value.id];
|
||||
|
@ -49,10 +57,9 @@ function renderRegisteredPlugins(name: "Guild" | "User" | "Channel", value: any)
|
|||
{plugins[plugin].map(setting => (
|
||||
<Menu.MenuCheckboxItem
|
||||
id={`vc-plugin-settings-${plugin}-${setting}`}
|
||||
// @ts-ignore
|
||||
label={Vencord.Plugins.plugins[plugin].settings.def[setting].popoutText ?? setting}
|
||||
label={Vencord.Plugins.plugins[plugin].settings?.def[setting].popoutText ?? setting}
|
||||
action={() => handleCheckboxClick(plugin, setting)}
|
||||
checked={checkedItems[`${plugin}-${setting}`]}
|
||||
checked={checkedItems[`${plugin}-${setting}-${value.id}`]}
|
||||
/>
|
||||
))}
|
||||
</Menu.MenuItem>
|
||||
|
@ -93,7 +100,7 @@ const registeredPlugins: Record<OptionType.USERS | OptionType.GUILDS | OptionTyp
|
|||
|
||||
export default definePlugin({
|
||||
name: "SettingListsAPI",
|
||||
description: "API to automatically add context menus for settings",
|
||||
description: "API that automatically adds context menus for User/Guild/Channel arrays of plugins",
|
||||
authors: [Devs.Elvyra],
|
||||
contextMenus: {
|
||||
"channel-context": MakeContextCallback("Channel"),
|
||||
|
@ -108,7 +115,7 @@ export default definePlugin({
|
|||
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 ((setting.type === OptionType.USERS || setting.type === OptionType.GUILDS || setting.type === OptionType.CHANNELS) && !setting.hidePopout) {
|
||||
if (!registeredPlugins[setting.type][plugin.name]) {
|
||||
registeredPlugins[setting.type][plugin.name] = [];
|
||||
}
|
||||
|
|
|
@ -86,12 +86,12 @@ const settings = definePluginSettings({
|
|||
},
|
||||
ignoreChannels: {
|
||||
type: OptionType.CHANNELS,
|
||||
description: "List of channel IDs to ignore",
|
||||
description: "List of channels to ignore",
|
||||
popoutText: "Ignore deleted messages in this channel"
|
||||
},
|
||||
ignoreGuilds: {
|
||||
type: OptionType.GUILDS,
|
||||
description: "List of guild IDs to ignore",
|
||||
description: "List of guilds to ignore",
|
||||
popoutText: "Ignore deleted messages in this guild",
|
||||
},
|
||||
});
|
||||
|
|
|
@ -74,15 +74,18 @@ const settings = definePluginSettings({
|
|||
stringRules: {
|
||||
type: OptionType.LIST,
|
||||
hidden: true,
|
||||
description: ""
|
||||
},
|
||||
regexRules: {
|
||||
type: OptionType.LIST,
|
||||
hidden: true,
|
||||
description: ""
|
||||
},
|
||||
migrated: {
|
||||
type: OptionType.BOOLEAN,
|
||||
hidden: true,
|
||||
default: false,
|
||||
description: ""
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -232,8 +235,8 @@ function applyRules(content: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
if (settings.store.stringRulesregexRules) {
|
||||
for (const rule of settings.store.stringRulesregexRules) {
|
||||
if (settings.store.regexRules) {
|
||||
for (const rule of settings.store.regexRules) {
|
||||
if (!rule.find) continue;
|
||||
if (rule.onlyIfIncludes && !content.includes(rule.onlyIfIncludes)) continue;
|
||||
|
||||
|
|
|
@ -267,6 +267,7 @@ export interface PluginSettingSliderDef {
|
|||
export interface PluginSettingListDef{
|
||||
type: OptionType.LIST | OptionType.CHANNELS | OptionType.GUILDS | OptionType.USERS;
|
||||
popoutText?: string;
|
||||
hidePopout?: boolean;
|
||||
}
|
||||
|
||||
export interface IPluginOptionComponentProps {
|
||||
|
@ -302,6 +303,7 @@ type PluginSettingType<O extends PluginSettingDef> = O extends PluginSettingStri
|
|||
O extends PluginSettingSelectDef ? O["options"][number]["value"] :
|
||||
O extends PluginSettingSliderDef ? number :
|
||||
O extends PluginSettingComponentDef ? any :
|
||||
O extends PluginSettingListDef ? any[] :
|
||||
never;
|
||||
type PluginSettingDefaultType<O extends PluginSettingDef> = O extends PluginSettingSelectDef ? (
|
||||
O["options"] extends { default?: boolean; }[] ? O["options"][number]["value"] : undefined
|
||||
|
@ -361,6 +363,7 @@ export type PluginOptionBoolean = PluginSettingBooleanDef & PluginSettingCommon
|
|||
export type PluginOptionSelect = PluginSettingSelectDef & PluginSettingCommon & IsDisabled & IsValid<PluginSettingSelectOption>;
|
||||
export type PluginOptionSlider = PluginSettingSliderDef & PluginSettingCommon & IsDisabled & IsValid<number>;
|
||||
export type PluginOptionComponent = PluginSettingComponentDef & PluginSettingCommon;
|
||||
export type PluginOptionList = PluginSettingListDef & PluginSettingCommon;
|
||||
|
||||
export type PluginNative<PluginExports extends Record<string, (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any>> = {
|
||||
[key in keyof PluginExports]:
|
||||
|
|
Loading…
Reference in a new issue