diff options
author | megumin <megumin.bakaretsurie@gmail.com> | 2022-10-19 20:57:27 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-19 21:57:27 +0200 |
commit | 1f50f78912caeeff1c65182d61b72e6ec95b9899 (patch) | |
tree | 8b1bc59c606617d7a8c39951950dc3c3e7bfd074 | |
parent | efab399309a44f8ea6a783abd13c49abb8cd6e38 (diff) | |
download | Vencord-1f50f78912caeeff1c65182d61b72e6ec95b9899.tar.gz Vencord-1f50f78912caeeff1c65182d61b72e6ec95b9899.tar.bz2 Vencord-1f50f78912caeeff1c65182d61b72e6ec95b9899.zip |
feat: settings sliders (#120)
Co-authored-by: Ven <vendicated@riseup.net>
-rw-r--r-- | src/components/PluginSettings/PluginModal.tsx | 28 | ||||
-rw-r--r-- | src/components/PluginSettings/components/SettingBooleanComponent.tsx | 10 | ||||
-rw-r--r-- | src/components/PluginSettings/components/SettingNumericComponent.tsx | 10 | ||||
-rw-r--r-- | src/components/PluginSettings/components/SettingSelectComponent.tsx | 10 | ||||
-rw-r--r-- | src/components/PluginSettings/components/SettingSliderComponent.tsx | 49 | ||||
-rw-r--r-- | src/components/PluginSettings/components/SettingTextComponent.tsx | 10 | ||||
-rw-r--r-- | src/components/PluginSettings/components/index.ts | 1 | ||||
-rw-r--r-- | src/plugins/moyai.ts | 20 | ||||
-rw-r--r-- | src/utils/types.ts | 24 | ||||
-rw-r--r-- | src/webpack/common.tsx | 1 |
10 files changed, 123 insertions, 40 deletions
diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index a324300..3ef36b4 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -15,10 +15,9 @@ import { SettingInputComponent, SettingNumericComponent, SettingSelectComponent, + SettingSliderComponent, } from "./components"; -const { FormSection, FormText, FormTitle } = Forms; - const UserSummaryItem = lazyWebpack(filters.byCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const AvatarStyles = lazyWebpack(filters.byProps(["moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"])); const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; @@ -80,7 +79,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti function renderSettings() { if (!pluginSettings || !plugin.options) { - return <FormText>There are no settings for this plugin.</FormText>; + return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>; } const options: JSX.Element[] = []; @@ -110,6 +109,11 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti } case OptionType.BOOLEAN: { options.push(<SettingBooleanComponent key={key} option={setting} {...props} />); + break; + } + case OptionType.SLIDER: { + options.push(<SettingSliderComponent key={key} option={setting} {...props} />); + break; } } } @@ -142,9 +146,9 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti <Text variant="heading-md/bold">{plugin.name}</Text> </ModalHeader> <ModalContent style={{ marginBottom: 8, marginTop: 8 }}> - <FormSection> - <FormTitle tag="h3">About {plugin.name}</FormTitle> - <FormText>{plugin.description}</FormText> + <Forms.FormSection> + <Forms.FormTitle tag="h3">About {plugin.name}</Forms.FormTitle> + <Forms.FormText>{plugin.description}</Forms.FormText> <div style={{ marginTop: 8, marginBottom: 8, width: "fit-content" }}> <UserSummaryItem users={authors} @@ -157,20 +161,20 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti renderMoreUsers={renderMoreUsers} /> </div> - </FormSection> + </Forms.FormSection> {!!plugin.settingsAboutComponent && ( <div style={{ marginBottom: 8 }}> - <FormSection> + <Forms.FormSection> <ErrorBoundary message="An error occurred while rendering this plugin's custom InfoComponent"> <plugin.settingsAboutComponent /> </ErrorBoundary> - </FormSection> + </Forms.FormSection> </div> )} - <FormSection> - <FormTitle tag="h3">Settings</FormTitle> + <Forms.FormSection> + <Forms.FormTitle tag="h3">Settings</Forms.FormTitle> {renderSettings()} - </FormSection> + </Forms.FormSection> </ModalContent> <ModalFooter> <Flex> diff --git a/src/components/PluginSettings/components/SettingBooleanComponent.tsx b/src/components/PluginSettings/components/SettingBooleanComponent.tsx index 62dd4d5..939ca70 100644 --- a/src/components/PluginSettings/components/SettingBooleanComponent.tsx +++ b/src/components/PluginSettings/components/SettingBooleanComponent.tsx @@ -2,8 +2,6 @@ import { ISettingElementProps } from "."; import { PluginOptionBoolean } from "../../../utils/types"; import { Forms, React, Select } from "../../../webpack/common"; -const { FormSection, FormTitle, FormText } = Forms; - export function SettingBooleanComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) { const def = pluginSettings[id] ?? option.default; @@ -31,8 +29,8 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange, } return ( - <FormSection> - <FormTitle>{option.description}</FormTitle> + <Forms.FormSection> + <Forms.FormTitle>{option.description}</Forms.FormTitle> <Select isDisabled={option.disabled?.() ?? false} options={options} @@ -44,8 +42,8 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange, serialize={v => String(v)} {...option.componentProps} /> - {error && <FormText style={{ color: "var(--text-danger)" }}>{error}</FormText>} - </FormSection> + {error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>} + </Forms.FormSection> ); } diff --git a/src/components/PluginSettings/components/SettingNumericComponent.tsx b/src/components/PluginSettings/components/SettingNumericComponent.tsx index fe3fea1..a54d281 100644 --- a/src/components/PluginSettings/components/SettingNumericComponent.tsx +++ b/src/components/PluginSettings/components/SettingNumericComponent.tsx @@ -2,8 +2,6 @@ import { ISettingElementProps } from "."; import { OptionType, PluginOptionNumber } from "../../../utils/types"; import { Forms, React, TextInput } from "../../../webpack/common"; -const { FormSection, FormTitle, FormText } = Forms; - const MAX_SAFE_NUMBER = BigInt(Number.MAX_SAFE_INTEGER); export function SettingNumericComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) { @@ -33,8 +31,8 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange, } return ( - <FormSection> - <FormTitle>{option.description}</FormTitle> + <Forms.FormSection> + <Forms.FormTitle>{option.description}</Forms.FormTitle> <TextInput type="number" pattern="-?[0-9]+" @@ -44,7 +42,7 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange, disabled={option.disabled?.() ?? false} {...option.componentProps} /> - {error && <FormText style={{ color: "var(--text-danger)" }}>{error}</FormText>} - </FormSection> + {error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>} + </Forms.FormSection> ); } diff --git a/src/components/PluginSettings/components/SettingSelectComponent.tsx b/src/components/PluginSettings/components/SettingSelectComponent.tsx index 703ffbb..20ff7a5 100644 --- a/src/components/PluginSettings/components/SettingSelectComponent.tsx +++ b/src/components/PluginSettings/components/SettingSelectComponent.tsx @@ -2,8 +2,6 @@ import { ISettingElementProps } from "."; import { PluginOptionSelect } from "../../../utils/types"; import { Forms, React, Select } from "../../../webpack/common"; -const { FormSection, FormTitle, FormText } = Forms; - export function SettingSelectComponent({ option, pluginSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) { const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value; @@ -25,8 +23,8 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr } return ( - <FormSection> - <FormTitle>{option.description}</FormTitle> + <Forms.FormSection> + <Forms.FormTitle>{option.description}</Forms.FormTitle> <Select isDisabled={option.disabled?.() ?? false} options={option.options} @@ -38,7 +36,7 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr serialize={v => String(v)} {...option.componentProps} /> - {error && <FormText style={{ color: "var(--text-danger)" }}>{error}</FormText>} - </FormSection> + {error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>} + </Forms.FormSection> ); } diff --git a/src/components/PluginSettings/components/SettingSliderComponent.tsx b/src/components/PluginSettings/components/SettingSliderComponent.tsx new file mode 100644 index 0000000..64466c1 --- /dev/null +++ b/src/components/PluginSettings/components/SettingSliderComponent.tsx @@ -0,0 +1,49 @@ +import { ISettingElementProps } from "."; +import { PluginOptionSlider } from "../../../utils/types"; +import { Forms, React, Slider } from "../../../webpack/common"; + +export function makeRange(start: number, end: number, step = 1) { + const ranges: number[] = []; + for (let value = start; value <= end; value += step) { + ranges.push(Math.round(value * 100) / 100); + } + return ranges; +} + +export function SettingSliderComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) { + const def = pluginSettings[id] ?? option.default; + + const [error, setError] = React.useState<string | null>(null); + + React.useEffect(() => { + onError(error !== null); + }, [error]); + + function handleChange(newValue: number): void { + let isValid = (option.isValid && option.isValid(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>{option.description}</Forms.FormTitle> + <Slider + disabled={option.disabled?.() ?? false} + markers={option.markers} + minValue={option.markers[0]} + maxValue={option.markers[option.markers.length - 1]} + initialValue={def} + onValueChange={handleChange} + onValueRender={(v: number) => String(v.toFixed(2))} + stickToMarkers={option.stickToMarkers ?? true} + {...option.componentProps} + /> + </Forms.FormSection> + ); +} + diff --git a/src/components/PluginSettings/components/SettingTextComponent.tsx b/src/components/PluginSettings/components/SettingTextComponent.tsx index 0bfe3fb..898f66b 100644 --- a/src/components/PluginSettings/components/SettingTextComponent.tsx +++ b/src/components/PluginSettings/components/SettingTextComponent.tsx @@ -2,8 +2,6 @@ import { ISettingElementProps } from "."; import { PluginOptionString } from "../../../utils/types"; import { Forms, React, TextInput } from "../../../webpack/common"; -const { FormSection, FormTitle, FormText } = Forms; - export function SettingInputComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) { const [state, setState] = React.useState(pluginSettings[id] ?? option.default ?? null); const [error, setError] = React.useState<string | null>(null); @@ -23,8 +21,8 @@ export function SettingInputComponent({ option, pluginSettings, id, onChange, on } return ( - <FormSection> - <FormTitle>{option.description}</FormTitle> + <Forms.FormSection> + <Forms.FormTitle>{option.description}</Forms.FormTitle> <TextInput type="text" value={state} @@ -33,7 +31,7 @@ export function SettingInputComponent({ option, pluginSettings, id, onChange, on disabled={option.disabled?.() ?? false} {...option.componentProps} /> - {error && <FormText style={{ color: "var(--text-danger)" }}>{error}</FormText>} - </FormSection> + {error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>} + </Forms.FormSection> ); } diff --git a/src/components/PluginSettings/components/index.ts b/src/components/PluginSettings/components/index.ts index d1fe7d6..a1748b0 100644 --- a/src/components/PluginSettings/components/index.ts +++ b/src/components/PluginSettings/components/index.ts @@ -15,3 +15,4 @@ export * from "./SettingBooleanComponent"; export * from "./SettingNumericComponent"; export * from "./SettingSelectComponent"; export * from "./SettingTextComponent"; +export * from "./SettingSliderComponent"; diff --git a/src/plugins/moyai.ts b/src/plugins/moyai.ts index 581f960..66cd68c 100644 --- a/src/plugins/moyai.ts +++ b/src/plugins/moyai.ts @@ -1,8 +1,11 @@ -import definePlugin from "../utils/types"; -import { Devs } from "../utils/constants"; import { Message, ReactionEmoji } from "discord-types/general"; -import { FluxDispatcher, SelectedChannelStore } from "../webpack/common"; + +import { makeRange } from "../components/PluginSettings/components/SettingSliderComponent"; +import { Devs } from "../utils/constants"; import { sleep } from "../utils/misc"; +import definePlugin, { OptionType } from "../utils/types"; +import { Settings } from "../Vencord"; +import { FluxDispatcher, SelectedChannelStore } from "../webpack/common"; interface IMessageCreate { type: "MESSAGE_CREATE"; @@ -67,6 +70,16 @@ export default definePlugin({ FluxDispatcher.unsubscribe("MESSAGE_CREATE", this.onMessage); FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD", this.onReaction); }, + + options: { + volume: { + description: "Volume of the 🗿🗿🗿", + type: OptionType.SLIDER, + markers: makeRange(0, 1, 0.1), + default: 0.5, + stickToMarkers: false, + } + } }); function countOccurrences(sourceString: string, subString: string) { @@ -101,5 +114,6 @@ function getMoyaiCount(message: string) { function boom() { const audioElement = document.createElement("audio"); audioElement.src = MOYAI_URL; + audioElement.volume = Settings.plugins.Moyai.volume; audioElement.play(); } diff --git a/src/utils/types.ts b/src/utils/types.ts index 5ed95e4..1318799 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -70,13 +70,15 @@ export enum OptionType { BIGINT, BOOLEAN, SELECT, + SLIDER, } export type PluginOptionsItem = | PluginOptionString | PluginOptionNumber | PluginOptionBoolean - | PluginOptionSelect; + | PluginOptionSelect + | PluginOptionSlider; export interface PluginOptionBase { description: string; @@ -132,4 +134,24 @@ export interface PluginOptionSelectOption { default?: boolean; } +export interface PluginOptionSlider extends PluginOptionBase { + type: OptionType.SLIDER; + /** + * All the possible values in the slider. Needs at least two values. + */ + markers: number[]; + /** + * Default value to use + */ + default: number; + /** + * If false, allow users to select values in-between your markers. + */ + stickToMarkers?: boolean; + /** + * Prevents the user from saving settings if this is false or a string + */ + isValid?(value: number): number; +} + export type IpcRes<V = any> = { ok: true; value: V; } | { ok: false, error: any; }; diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 8077a9f..44cb2b5 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -31,6 +31,7 @@ export let TextInput: any; export let Text: (props: TextProps) => JSX.Element; export const Select = lazyWebpack(filters.byCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems")); +export const Slider = lazyWebpack(filters.byCode("closestMarkerIndex", "stickToMarkers")); export let Parser: any; export let Alerts: { |