diff --git a/src/equicordplugins/amITyping/index.ts b/src/equicordplugins/amITyping/index.ts new file mode 100644 index 00000000..7b61c102 --- /dev/null +++ b/src/equicordplugins/amITyping/index.ts @@ -0,0 +1,24 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "AmITyping", + description: "Shows you if other people can see you typing.", + authors: [EquicordDevs.MrDiamond], + + patches: [ + { + find: "\"handleDismissInviteEducation\"", + replacement: { + match: /\i\.default\.getCurrentUser\(\)/, + replace: "\"\"" + } + } + ] +}); diff --git a/src/equicordplugins/betterBanReasons/index.tsx b/src/equicordplugins/betterBanReasons/index.tsx new file mode 100644 index 00000000..853cd1b0 --- /dev/null +++ b/src/equicordplugins/betterBanReasons/index.tsx @@ -0,0 +1,100 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { Button, Forms, i18n, TextInput } from "@webpack/common"; + +function ReasonsComponent() { + const { reasons } = settings.use(["reasons"]); + + return ( + + {reasons.map((reason: string, index: number) => ( +
+ { + reasons[index] = v; + settings.store.reasons = [...reasons]; + }} + placeholder="Reason" + /> + +
+ ))} + +
+ ); +} + +const settings = definePluginSettings({ + reasons: { + description: "Your custom reasons", + type: OptionType.COMPONENT, + default: [], + component: ReasonsComponent, + }, + textInputDefault: { + type: OptionType.BOOLEAN, + description: 'Shows a text input instead of a select menu by default. (Equivalent to clicking the "Other" option)' + } +}); + +export default definePlugin({ + name: "BetterBanReasons", + description: "Create custom reasons to use in the Discord ban modal, and/or show a text input by default instead of the options.", + authors: [Devs.Inbestigator], + patches: [ + { + find: "Messages.BAN_MULTIPLE_CONFIRM_TITLE", + replacement: [{ + match: /\[\{name:\i\.\i\.Messages\.BAN_REASON_OPTION_SPAM_ACCOUNT.+?\}\]/, + replace: "$self.getReasons()" + }, + { + match: /useState\(0\)(?=.{0,100}targetUserId:)/, + replace: "useState($self.getDefaultState())" + }] + } + ], + getReasons() { + const reasons = settings.store.reasons.length + ? settings.store.reasons + : [ + i18n.Messages.BAN_REASON_OPTION_SPAM_ACCOUNT, + i18n.Messages.BAN_REASON_OPTION_HACKED_ACCOUNT, + i18n.Messages.BAN_REASON_OPTION_BREAKING_RULES + ]; + return reasons.map(s => ({ name: s, value: s })); + }, + getDefaultState() { + return settings.store.textInputDefault ? 1 : 0; + }, + settings, +}); diff --git a/src/equicordplugins/betterBanReasons/style.css b/src/equicordplugins/betterBanReasons/style.css new file mode 100644 index 00000000..02fb4bbf --- /dev/null +++ b/src/equicordplugins/betterBanReasons/style.css @@ -0,0 +1,11 @@ +.vc-bbr-reason-wrapper { + display: grid; + padding: 0; + padding-bottom: 0.5rem; + gap: 0.5rem; + grid-template-columns: 6fr 1fr; +} + +.vc-bbr-remove-button { + height: 100%; +} diff --git a/src/equicordplugins/betterMicrophone.desktop/components/MicrophoneSettingsModal.tsx b/src/equicordplugins/betterMicrophone.desktop/components/MicrophoneSettingsModal.tsx new file mode 100644 index 00000000..b031fc0d --- /dev/null +++ b/src/equicordplugins/betterMicrophone.desktop/components/MicrophoneSettingsModal.tsx @@ -0,0 +1,331 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +import { Flex } from "@components/Flex"; +import { Switch } from "@components/Switch"; +import { ModalSize } from "@utils/modal"; +import { Card, Forms, Select, Slider, TextInput, useEffect, useState } from "@webpack/common"; +import { SelectOption } from "@webpack/types"; + +import { + ProfilableStore, + SettingsModal, + SettingsModalCard, + SettingsModalCardItem, + SettingsModalCardRow, + SettingsModalProfilesCard, + validateNumberInput, + validateTextInputNumber +} from "../../philsPluginLibrary"; +import { Styles } from "../../philsPluginLibrary/styles"; +import { MicrophoneProfile, MicrophoneStore } from "../stores"; + +const simpleVoiceBitrates: readonly SelectOption[] = [ + { + label: "Normal", + value: 96 + }, + { + label: "Medium-High", + value: 160 + }, + { + label: "High", + value: 320 + }, + { + label: "Very-High", + value: 512 + } +] as const; + +export interface MicrophoneSettingsModalProps extends React.ComponentProps { + microphoneStore: ProfilableStore; + showInfo?: boolean; +} + +export const MicrophoneSettingsModal = (props: MicrophoneSettingsModalProps) => { + const { microphoneStore, showInfo } = props; + + const { + currentProfile, + simpleMode, + setSimpleMode, + deleteProfile, + duplicateProfile, + getCurrentProfile, + getDefaultProfiles, + getProfile, + getProfiles, + isCurrentProfileADefaultProfile, + profiles, + saveProfile, + setChannels, + setChannelsEnabled, + setCurrentProfile, + setFreq, + setFreqEnabled, + setPacsize, + setPacsizeEnabled, + setRate, + setRateEnabled, + setVoiceBitrate, + setVoiceBitrateEnabled + } = microphoneStore.use(); + + const { + name, + channels, + channelsEnabled, + freq, + freqEnabled, + pacsize, + pacsizeEnabled, + rate, + rateEnabled, + voiceBitrate, + voiceBitrateEnabled + } = currentProfile; + + const [isSaving, setIsSaving] = useState(false); + + const [rateInput, setRateInput] = useState(rate ? rate.toString() : ""); + const [freqInput, setFreqInput] = useState(freq ? freq.toString() : ""); + const [pacsizeInput, setPacsizeInput] = useState(pacsize ? pacsize.toString() : ""); + const [channelsInput, setChannelsInput] = useState(channels ? channels.toString() : ""); + + useEffect(() => { + setRateInput(rate ? rate.toString() : ""); + setFreqInput(freq ? freq.toString() : ""); + setPacsizeInput(pacsize ? pacsize.toString() : ""); + setChannelsInput(channels ? channels.toString() : ""); + }, [rate, freq, pacsize, channels]); + + const simpleToggle = + + Simple + setSimpleMode(checked)} /> + ; + + const settingsCardVoiceBitrateSimple = + setVoiceBitrateEnabled(status) + }}> + + ({ + label: name, + value: id + }))} + isSelected={value => audioSource === value} + select={value => setAudioSource(value)} + serialize={() => ""} + {...props} + > + ); +}; diff --git a/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx b/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx new file mode 100644 index 00000000..d12493a9 --- /dev/null +++ b/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx @@ -0,0 +1,38 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +import { Button } from "@webpack/common"; +import React from "react"; + +import { openScreenshareModal } from "../modals"; + +export interface OpenScreenshareSettingsButtonProps { + title?: string; +} + +export const OpenScreenshareSettingsButton = (props: OpenScreenshareSettingsButtonProps) => { + return ( + + ); +}; diff --git a/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx b/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx new file mode 100644 index 00000000..ac529445 --- /dev/null +++ b/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx @@ -0,0 +1,467 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +import { Flex } from "@components/Flex"; +import { Switch } from "@components/Switch"; +import { ModalSize, openModalLazy } from "@utils/modal"; +import { Button, Card, Forms, React, Select, Slider, TextInput, useEffect, useState } from "@webpack/common"; +import { SelectOption } from "@webpack/types"; + +import { MicrophoneSettingsModal } from "../../betterMicrophone.desktop/components"; +import { + MediaEngineStore, + openURL, + ProfilableStore, + SettingsModal, + SettingsModalCard, + SettingsModalCardItem, + SettingsModalCardRow, + SettingsModalProfilesCard, + types, + validateNumberInput, + validateTextInputNumber +} from "../../philsPluginLibrary"; +import { Styles } from "../../philsPluginLibrary/styles"; +import { PluginInfo } from "../constants"; +import { ScreenshareAudioProfile, ScreenshareAudioStore, ScreenshareProfile, ScreenshareStore } from "../stores"; + +const simpleResolutions: readonly (SelectOption & { value: types.Resolution; })[] = [ + { + label: "480p", + value: { + height: 480, + width: 720 + } + }, + { + label: "720p", + value: { + height: 720, + width: 1280 + } + }, + { + label: "1080p", + value: { + height: 1080, + width: 1920 + } + }, + { + label: "1440p", + value: { + height: 1440, + width: 2560 + } + }, + { + label: "2160p", + value: { + height: 2160, + width: 3840 + } + } +] as const; + +const simpleVideoBitrates: readonly SelectOption[] = [ + { + label: "Low", + value: 2500 + }, + { + label: "Medium", + value: 5000 + }, + { + label: "Medium-High", + value: 7500 + }, + { + label: "High", + value: 10000 + } +] as const; + +export interface ScreenshareSettingsModalProps extends React.ComponentProps { + screenshareStore: ProfilableStore; + screenshareAudioStore?: ProfilableStore; + onAudioDone?: () => void; +} + +export const ScreenshareSettingsModal = (props: ScreenshareSettingsModalProps) => { + const { screenshareStore, screenshareAudioStore, onAudioDone } = props; + + const { + currentProfile, + profiles, + simpleMode, + setVideoBitrateEnabled, + setVideoCodec, + setVideoCodecEnabled, + setFramerate, + setFramerateEnabled, + setHeight, + setKeyframeInterval, + setKeyframeIntervalEnabled, + setResolutionEnabled, + setVideoBitrate, + setWidth, + setCurrentProfile, + getProfile, + saveProfile, + setHdrEnabled, + setSimpleMode, + deleteProfile, + duplicateProfile, + getCurrentProfile, + getProfiles + } = screenshareStore.use(); + + + const { + name, + framerate, + framerateEnabled, + height, + keyframeInterval, + keyframeIntervalEnabled, + resolutionEnabled, + videoBitrate, + videoBitrateEnabled, + videoCodec, + videoCodecEnabled, + width, + hdrEnabled + } = currentProfile; + + const [videoCodecs, setVideoCodecs] = useState([]); + + const [isSaving, setIsSaving] = useState(false); + + const [textinputWidth, setTextinputWidth] = useState(width ? width.toString() : ""); + const [textinputHeight, setTextinputHeight] = useState(height ? height.toString() : ""); + const [textinputFramerate, setTextinputFramerate] = useState(framerate ? framerate.toString() : ""); + const [textinputKeyframeInterval, setTextinputKeyframeInterval] = useState(keyframeInterval ? keyframeInterval.toString() : ""); + + useEffect(() => { + setTextinputWidth(width ? width.toString() : ""); + setTextinputHeight(height ? height.toString() : ""); + setTextinputFramerate(framerate ? framerate.toString() : ""); + setTextinputKeyframeInterval(keyframeInterval ? keyframeInterval.toString() : ""); + }, [width, height, framerate, keyframeInterval]); + + useEffect(() => { + (async () => { + const mediaEngine = MediaEngineStore.getMediaEngine(); + + const stringifiedCodecs: types.CodecCapabilities[] = JSON.parse( + await new Promise(res => mediaEngine.getCodecCapabilities(res)) + ); + + setVideoCodecs(stringifiedCodecs); + })(); + }, []); + + const settingsCardResolutionSimple = + setResolutionEnabled(status) + }}> + + void setVideoBitrate(value)} + isSelected={(value: number) => videoBitrate === value} + serialize={() => ""} /> + + ; + + const settingsCardResolution = + setResolutionEnabled(status), + disabled: isSaving + }}> + + validateTextInputNumber(value) && setTextinputWidth(value)} + onBlur={e => { + const result = validateNumberInput(e.target.value); + setWidth(result); + setTextinputWidth(result ? result.toString() : ""); + }} /> + + + validateTextInputNumber(value) && setTextinputHeight(value)} + onBlur={e => { + const result = validateNumberInput(e.target.value); + setHeight(result); + setTextinputHeight(result ? result.toString() : ""); + }} /> + + ; + + const settingsCardItemFramerate = + + validateTextInputNumber(value) && setTextinputFramerate(value)} + onBlur={e => { + const result = validateNumberInput(e.target.value); + setFramerate(result); + setTextinputFramerate(result ? result.toString() : ""); + }} /> + ; + + const settingsCardFramerateProps: React.ComponentProps = { + title: "Framerate", + switchEnabled: true, + switchProps: { + checked: framerateEnabled ?? false, + disabled: isSaving, + onChange: status => setFramerateEnabled(status) + } + }; + + const settingsCardFramerate = + + {settingsCardItemFramerate} + ; + + const settingsCardFramerateSimple = + + {settingsCardItemFramerate} + ; + + const settingsCardKeyframeInterval = + setKeyframeIntervalEnabled(status) + }}> + + validateTextInputNumber(value) && setTextinputKeyframeInterval(value)} + onBlur={e => { + const result = validateNumberInput(e.target.value); + setKeyframeInterval(result); + setTextinputKeyframeInterval(result ? result.toString() : ""); + }} /> + + ; + + const settingsCardVideoBitrate = + setVideoBitrateEnabled(status) + }}> + +
+ setVideoBitrate(value)} + initialValue={videoBitrate || 500} + minValue={500} + maxValue={10000} + markers={[500, 10000]} + onValueRender={value => `${value.toFixed(0)}kb/s`} /> +
+
+
; + + const settingsCardAudioProps: React.ComponentProps = { + title: "Audio Settings" + }; + + const settingsCardItemAudio = + +