diff --git a/README.md b/README.md index b8ccc067..49e1105b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-151 additional plugins +155 additional plugins ### All Platforms - AllCallTimers by MaxHerbold & D3SOX @@ -55,6 +55,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - FixFileExtensions by thororen - FollowVoiceUser by TheArmagan - FrequentQuickSwitcher by Samwich +- FriendCodes by HypedDomi - FriendshipRanks by Samwich - FullVcPfp by mochie - FriendTags by Samwich @@ -71,6 +72,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - HomeTyping by Samwich - HopOn by ImLvna - Husk by nin0dev +- IconViewer by iamme - Identity by Samwich - IgnoreCalls by TheArmagan - IgnoreTerms by D3SOX @@ -126,6 +128,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - StatsfmRPC by Crxaw & vmohammad - Slap by Korbo - SoundBoardLogger by Moxxie, fres, echo, maintained by thororen +- SpotifyLyrics by Joona - StatusPresets by iamme - SteamStatusSync by niko - StickerBlocker by Samwich @@ -145,6 +148,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - UwUifier by echo - VCSupport by thororen - VCNarratorCustom by Loukios, ported by example-git +- VCPanelSettings by nin0dev - VencordRPC by AutumnVN - VideoSpeed by Samwich - ViewRawVariant (ViewRaw2) by Kyuuhachi diff --git a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx new file mode 100644 index 00000000..b1832579 --- /dev/null +++ b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx @@ -0,0 +1,131 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { findByPropsLazy } from "@webpack"; +import { Button, Clipboard, Flex, Forms, Parser, Text, useEffect, useState } from "@webpack/common"; + +import { FriendInvite } from "./types"; + +const FormStyles = findByPropsLazy("header", "title", "emptyState"); +const { createFriendInvite, getAllFriendInvites, revokeFriendInvites } = findByPropsLazy("createFriendInvite"); + +function CopyButton({ copyText, copiedText, onClick }) { + const [copied, setCopied] = useState(false); + + const handleButtonClick = (e: React.MouseEvent) => { + setCopied(true); + setTimeout(() => setCopied(false), 1000); + onClick(e); + }; + + return ( + + ); +} + +function FriendInviteCard({ invite }: { invite: FriendInvite; }) { + return ( +
+ +
+ + {invite.code} + + + Expires {Parser.parse(``)} • {invite.uses}/{invite.max_uses} uses + +
+ + Clipboard.copy(`https://discord.gg/${invite.code}`)} + /> + +
+
+ ); +} + +export default function FriendCodesPanel() { + const [invites, setInvites] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(true); + getAllFriendInvites() + .then(setInvites) + .then(() => setLoading(false)); + }, []); + + return ( + <> +
+ + Your Friend Codes + + + +

{`Friend Codes - ${invites.length}`}

+ + + + +
+
+ {loading ? ( + + Loading... + + ) : invites.length === 0 ? ( + + You don't have any friend codes yet + + ) : ( +
+ {invites.map(invite => ( + + ))} +
+ )} + + ); +} diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx new file mode 100644 index 00000000..bff6668c --- /dev/null +++ b/src/equicordplugins/friendCodes/index.tsx @@ -0,0 +1,29 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import definePlugin from "@utils/types"; + +import FriendCodesPanel from "./FriendCodesPanel"; +import { Devs } from "@utils/constants"; + +export default definePlugin({ + name: "FriendCodes", + description: "Generate FriendCodes to easily add friends", + authors: [Devs.HypedDomi], + patches: [ + { + find: "#{intl::ADD_FRIEND})}),(", + replacement: { + match: /\.Fragment[^]*?children:\[[^]*?}\)/, + replace: "$&,$self.FriendCodesPanel" + } + } + ], + + get FriendCodesPanel() { + return ; + } +}); diff --git a/src/equicordplugins/friendCodes/styles.css b/src/equicordplugins/friendCodes/styles.css new file mode 100644 index 00000000..96aeef83 --- /dev/null +++ b/src/equicordplugins/friendCodes/styles.css @@ -0,0 +1,34 @@ +.vc-friend-codes-card { + padding: 20px; + margin-bottom: var(--custom-margin-margin-small); + border-width: 1px; + border-style: solid; + border-radius: 5px; + border-color: var(--background-tertiary); + background-color: var(--background-secondary); +} + +.vc-friend-codes-card-title span { + color: var(--header-secondary); + font-family: var(--font-primary); + font-size: 14px; + font-weight: 400; +} + +.vc-friend-codes-info-header { + margin-top: 16px; + margin-bottom: 8px; + color: var(--header-secondary); + text-transform: uppercase; + font-size: 12px; + line-height: 16px; + letter-spacing: .02em; + font-family: var(--font-display); + font-weight: 600; +} + +.vc-friend-codes-text { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/equicordplugins/friendCodes/types.ts b/src/equicordplugins/friendCodes/types.ts new file mode 100644 index 00000000..ce5c1066 --- /dev/null +++ b/src/equicordplugins/friendCodes/types.ts @@ -0,0 +1,26 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export interface FriendInvite { + channel: null; + code: string; + created_at: string; + expires_at: string; + inviter: { + avatar: string; + avatar_decoration_data: unknown; + clan: unknown; + discriminator: string; + global_name: string; + id: string; + public_flags: number; + username: string; + }; + max_age: number; + max_uses: number; + type: number; + uses: number; +} diff --git a/src/equicordplugins/iconViewer/IconModal.tsx b/src/equicordplugins/iconViewer/IconModal.tsx new file mode 100644 index 00000000..48f1c153 --- /dev/null +++ b/src/equicordplugins/iconViewer/IconModal.tsx @@ -0,0 +1,112 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { CodeBlock } from "@components/CodeBlock"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal +} from "@utils/modal"; +import { Button, FluxDispatcher, TooltipContainer, useCallback, useEffect, useState } from "@webpack/common"; +import * as t from "@webpack/types"; + +import { IconsFinds } from "./names"; +import { openRawModal } from "./rawModal"; +import { openSaveModal } from "./saveModal"; +import { ModalHeaderTitle } from "./subComponents"; +import { _cssColors, cssColors, iconSizes } from "./utils"; + +const defaultColor = 209; + + +function ModalComponent(props: { iconName: string; Icon: t.Icon; } & ModalProps) { + const [color, SetColor] = useState(defaultColor); + + const onKeyDown = useCallback((e: KeyboardEvent) => { + if (e.key === "ArrowLeft" || e.key === "ArrowRight") { + e.preventDefault(); + if (e.key === "ArrowLeft") { + SetColor(color + -1); + } else if (e.key === "ArrowRight") { + SetColor(color + 1); + } + } + }, [color]); + + const onColorChange = useCallback((e: { type: string; color: string; }) => { + SetColor(_cssColors.indexOf(e.color)); + }, [color]); + + useEffect(() => { + document.addEventListener("keydown", onKeyDown); + // @ts-ignore + FluxDispatcher.subscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); + return () => { + document.removeEventListener("keydown", onKeyDown); + // @ts-ignore + FluxDispatcher.unsubscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); + }; + }, [onKeyDown]); + if (color < 0 || color >= cssColors.length) { + SetColor(0); + } + const { iconName, Icon } = props; + return ( + + + + + + {IconsFinds[iconName] ? +
+ +
+ : null + } +
+
+ +
+
+ {iconSizes.map((size, idx) => + + + + )} +
+
+
+ + + + +
); +} + +export function openIconModal(iconName: string, Icon: t.Icon) { + openModal(props => ); +} + diff --git a/src/equicordplugins/iconViewer/IconsTab.css b/src/equicordplugins/iconViewer/IconsTab.css new file mode 100644 index 00000000..e5ed1266 --- /dev/null +++ b/src/equicordplugins/iconViewer/IconsTab.css @@ -0,0 +1,117 @@ +.vc-icon-modal-codeblock { + margin-left: 10%; + margin-top: 30px; +} + +.vc-icon-icon { + margin-left: 5%; +} + +.vc-icon-modal-main-container { + display: flex; +} + +.vc-ic-unordered-list li { + margin-left: 5%; + list-style: disc; +} + +.vc-icon-other-icon-sizes { + height: 32px; + display: flex; + margin-top: 15%; + margin-left: 5%; +} + +.vc-ic-icon-modal-root { + height: 450px; + width: 700px; +} + +.vc-icons-tab-grid-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); + gap: 8px; +} + +.vc-icon-modal-size-ex-icon { + margin-right: 5%; +} + +.vc-icon-modal-icon { + height: 164px; + width: 164px; +} + +.vc-icon-tab-search-bar-grid { + display: grid; + height: 50px; + gap: 10px; + grid-template-columns: 1fr 10px; +} + +.vc-icon-display-box { + height: 164px; + width: 164px; + margin-top: 5%; + margin-left: 15%; + background-image: repeating-linear-gradient( + 45deg, + #ffffff1a 0, + #ffffff1a 10px, + #0000001a 10px, + #0000001a 20px + ); + border-radius: 10px; +} + +.vc-icon-container { + margin-top: 5px; + padding: 15px; + border-radius: 5px; + border: 3px solid transparent; + box-sizing: border-box; +} + +.vc-icon-container:hover { + border-radius: 5px; + border: 3px solid var(--background-tertiary); + box-sizing: border-box; +} + +.vc-icon-title { + font-size: 0.8em; + margin-top: 0; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.vc-icon-modal-color-tooltip:hover { + background-color: var(--info-help-background); +} + +.vc-save-modal { + margin-top: 10%; + display: grid; + grid-template-columns: auto 1fr; + gap: 10px; +} + +.vc-save-select-option-1 { + margin-bottom: 5%; +} + +.vc-save-select-option-2 { + margin-top: 5%; +} + +.vc-ic-modals-root { + border-radius: 25px; +} + +.vc-ic-modals-footer { + border-bottom-left-radius: 25px; + border-bottom-right-radius: 25px; +} diff --git a/src/equicordplugins/iconViewer/IconsTab.tsx b/src/equicordplugins/iconViewer/IconsTab.tsx new file mode 100644 index 00000000..b6049c58 --- /dev/null +++ b/src/equicordplugins/iconViewer/IconsTab.tsx @@ -0,0 +1,88 @@ +/* + * 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 . +*/ + +import "./IconsTab.css"; + +import { SettingsTab, wrapTab } from "@components/VencordSettings/shared"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { Button, Clickable, Forms, React, TextInput, TooltipContainer } from "@webpack/common"; +import * as t from "@webpack/types"; + +import { openIconModal } from "./IconModal"; +import { getNameByIcon } from "./names"; +import { findAllByCode, IconsDef } from "./utils"; + +export let Icons: IconsDef | null = null; + +function searchMatch(search: string, name: string, Icon: t.Icon, searchbyFunction: boolean): boolean { + if (search === "") return true; + if (searchbyFunction) { + return String(Icon).includes(search); + } + const words = name.replace(/([A-Z]([a-z]+)?)/g, " $1").toLowerCase().split(" "); + const searchKeywords = search.toLowerCase().split(" "); + return searchKeywords.every(keyword => words.includes(keyword)) || words.every(keyword => searchKeywords.includes(keyword)) || name.toLowerCase().includes(search.toLowerCase()); +} + + +function RenderIcons({ search, searchbyFunction }: { search: string; searchbyFunction: boolean; }) { + if (Icons === null) { + const OrgIcons = Array.from(new Set(findAllByCode("[\"size\",\"width\",\"height\",\"color\",\"colorClass\"]"))); + Icons = Object.fromEntries(Object.keys(OrgIcons).map(k => [String(getNameByIcon(OrgIcons[k], k)), OrgIcons[k]])) as IconsDef; + } + return
+ {Object.entries(Icons).map(([iconName, Icon], index) => + searchMatch(search, iconName, Icon, searchbyFunction) && +
+ openIconModal(iconName, Icon)}> +
+ +
+
+ {iconName} +
+
+ )}
; +} + +function IconsTab() { + const [search, setSearch] = React.useState(""); + const [searchByFunction, setSearchByFunction] = React.useState(false); + const MemoRenderIcons = React.memo(RenderIcons); + + return ( + +
+ + + + +
+ +
+ ); +} + +export default wrapTab(IconsTab, "IconsTab"); diff --git a/src/equicordplugins/iconViewer/index.tsx b/src/equicordplugins/iconViewer/index.tsx new file mode 100644 index 00000000..fe1a840e --- /dev/null +++ b/src/equicordplugins/iconViewer/index.tsx @@ -0,0 +1,54 @@ +/* + * 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, { StartAt } from "@utils/types"; +import { SettingsRouter } from "@webpack/common"; + +import IconsTab from "./IconsTab"; +import { SettingsAbout } from "./subComponents"; + + +export default definePlugin({ + name: "IconViewer", + description: "Adds a new tab to settings, to preview all icons", + authors: [EquicordDevs.iamme], + dependencies: ["Settings"], + startAt: StartAt.WebpackReady, + toolboxActions: { + "Open Icons Tab"() { + SettingsRouter.open("VencordDiscordIcons"); + }, + }, + settingsAboutComponent: SettingsAbout, + start() { + const customSettingsSections = ( + Vencord.Plugins.plugins.Settings as any as { + customSections: ((ID: Record) => any)[]; + } + ).customSections; + + const IconViewerSection = () => ({ + section: "VencordDiscordIcons", + label: "Icons", + element: IconsTab, + className: "vc-discord-icons", + id: "IconViewer" + }); + + customSettingsSections.push(IconViewerSection); + }, + stop() { + const customSettingsSections = ( + Vencord.Plugins.plugins.Settings as any as { + customSections: ((ID: Record) => any)[]; + } + ).customSections; + + const i = customSettingsSections.findIndex(section => section({}).id === "IconViewer"); + if (i !== -1) customSettingsSections.splice(i, 1); + }, +}); diff --git a/src/equicordplugins/iconViewer/names.ts b/src/equicordplugins/iconViewer/names.ts new file mode 100644 index 00000000..4c110b3f --- /dev/null +++ b/src/equicordplugins/iconViewer/names.ts @@ -0,0 +1,150 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import * as t from "@webpack/types"; + +// name: pattern +export const IconsFinds = { + Discord: "1.6 5.64-2.87", + XboxNeutral: "8.68-.62c.89-.81 1.5", + PlaystationNeutral: "2.04Zm-9.35", + TwitterNeutral: "M13.86 10.47", // even discord calls it twitter lmao + InstagramNeutral: "4.12.07Zm.1 2c-", + YoutubeNeutral: "11.5s0 3.95.5 5.85a3.", + FacebookNeutral: "2.8V12h2.8V9", + NintendoSwitchNeutral: "14V2.32c0", + Pencil: "0-2.82 0l-1.38 1.38a1", + AngleBrackets: "0-.4.8v1.98c0", + NitroWheel: "2h3a1 1 0 1 1 0 2H5.5", + Bill: "75.34.75.75C6 7.99 5 9", + Chat: "2.2 22H12Z\",", + ChatVoice: "22H12Zm2-5.26c0", + ChatX: ".23.46-.48.47L12 22H2.", + ChatSmile: "04-.61A10 10 0 1 1 22 ", + ChatRetry: "14.07.3.09.44.04a7", + ChatPlus: "1.24-.37V12a10 10 0 1 ", + Bug: "1.1.27.1.37 0a6.66 6.6", + B: "9V2.9c0-.5.4-.9.9-.9h7", + Eye: "19.1 21 12 21c-7.11 0-", + EyeSlash: "2.63-2.63c", + EyePlus: "3.32ZM19 14a1 ", + Heart: "0C9.43 20.48 1 15.09 1", + Star: ".73-2.25h6.12l1.9-5.83Z", + StarOutline: "3.1h5.26l1.62", + StarShooting: "1.35l2.95 2.14", + QrCode: "0v3ZM20", + Friends: "12h1a8", + PlusSmall: "0v-5h5a1", + CircleQuestion: "10.58l-3.3-3.3a1", + Pin: "1-.06-.63L6.16", + PinUpright: "5H8v4.35l-3.39", + PinUprightSlash: "1.56ZM11.08 ", + ArrowsLeftRight: "18.58V3a1", + XSmall: "13.42l5.3", + XLarge: "13.42l7.3 7.3Z", + XSmallBold: "12l4.94-4.94a1.5", + XLargeBold: "12l6.94-6.94a1.5", + Lock: "3Zm9-3v3H9V6a3", + LockUnlocked: "1-1.33-1.5ZM14", + Video: "1.45-.9V7.62a1", + VideoSlash: "1.4l20-20ZM9.2", + VideoLock: "1.32-.5V7.62a1", + Fire: "14Zm9.26-.84a.57.57", + Warning: "3.15H3.29c-1.74", + Download: "1.42l3.3 3.3V3a1", + Upload: "0ZM3 20a1 1", + // QuestionMark: "0ZM5.5 7a1.5" Unknown name + Quest: "10.47a.76.76", + Play: "4.96v14.08c0", + Emoji: " 0 0 0 0 22ZM6.5", + Gif: "3H5Zm2.18 13.8", + Trash: "2.81h8.36a3", + Bell: "9.5v2.09c0", + Screen: "0-3-3H5ZM13.5", + ScreenArrow: "3V5Zm16", + ScreenStream: " 2-2h3a2", + ScreenSystemRequirements: "3V5Zm3", // a guess + ScreenSlash: "5.8ZM17.15", + ScreenX: "1-3-3V5Zm6.3.3a1", + Plus: "0v8H3a1 1 0 1 0 0 2h8v8a1", + Id: "15h2.04V7.34H6V17Zm4", + Tv: "0-3-3H4ZM6 20a1", + Crown: "1.18l.82.82-3.61", + React: "04-4ZM16.96 4.08c", + Camera: "1.34 1.71 1.34H20a3", + Sticker: "1-.58.82l-4.24", + StageX: "13.07-1.38ZM16.7", + StageLock: "7.14-3.85ZM18.98", + Stage: "20.03c-.25.72.12", + ConnectionFine: "1 0 1 1-2 0A17 17 ", + ConnectionAverage: "\"M3 7a1 1 0 0", + ConnectionBad: "\"M2 13a1 1 0 0", + ConnectionUnknown: "15.86-.6.9-.2.02", + ChatWarning: ".54.5H2.2a1", + ChatCheck: "22H12c.22", + Hammer: "1.42ZM7.76", + StickerSmall: "1-.5.5H7a4", + StickerSad: "1.66-1.12 5.5", + StickerDeny: "\"M21.76 14.83a", // a guess + MagnifyingGlassPlus: "M11 7a1 1 0", + MagnifyingGlassMinus: "3v12H5.5a1.5 1.5", + // MagnifyingGlass: "???", // not quite possible + ChatArrowRight: "2.43l.06", + Bookmark: "1-1.67.74l", + ChannelList: "1-1-1ZM2 8a1", + ChannelListMagnifyingGlass: "2h18a1 1 0 1 0 0-2H3ZM2", + Activities: "1h3a3 3 0 0 0 3-3Z\"", + ActivitiesPlus: "14.35v1.29a", + AnnouncementsLock: "1-2.46-1.28 3.86", + AnnouncementsWarning: "1-2.46-1.28 3.85", + Announcements: ".42.27.79.62", + ShieldLock: "2.83v2.67a.5.5", + ShieldUser: "9.77V6.75c0-.57.17", + ShieldAt: "14.42-.35.75", + Shield: "M4.27 5.22A2.66", // a guess + Slash: "1-.43-.76L15.78", + SlashBox: "0-3-3H5Zm12.79", + Apps: "2.95H20a2 2 0", + CheckmarkLarge: "1.4l-12 12a1", + CheckmarkLargeBold: "2.12-2.12L9", + CheckmarkSmallBold: "13.88l6.94-6.94a1.5", + CheckmarkSmall: "1-1.4 0l-4-4a1", + DoubleCheckmark: "1.4l4.5 4.5a1", + NewUser: "0-.92h-.03a2", // a guess + UserCheck: "0l1.8-1.8c.17", + User: "2.9.06.24.26.", + UserMinus: "3-3h5.02c.38", + UserPlus: "2.07ZM12", + UserPlay: "0-3.61-.71h-.94Z", + UserBox: "0-3-3H5Zm10 6a3", // a guess + Settings: "0ZM16 12a4", + SettingsInfo: "10Zm1-4a1", + Hashtag: "8 4.84a1", // a guess + HashtagLocked: "2.02.31.03", // a guess + HashtagWarning: "8h1.26Z", // a guess + HashtagPlay: "52.88H9.85l", // a guess + Flag: "5.85v7.3a2", + Language: "5.43h3.85l", + Lightbulb: "8.5ZM15.1 19c.5", + Key: "23-.24ZM10 16a2", + InBox: "3H5ZM4 5.5C4", + BookmarkOutline: "0-1-1ZM7 2a3", + Food: "7.58V8a1 1" +}; + +// 13l4.91-8.05a1.8 + +export const namePatterns = new Map(Object.entries(IconsFinds).map(([name, pattern]) => [name, pattern])); + +export function getNameByIcon(Icon: t.Icon, defaultName: any) { + for (const [name, pattern] of namePatterns) { + if (String(Icon).includes(pattern)) { + namePatterns.delete(name); // remove pattern from map after being found prevent overshadowing + return name; + } + } + return defaultName; +} diff --git a/src/equicordplugins/iconViewer/rawModal.tsx b/src/equicordplugins/iconViewer/rawModal.tsx new file mode 100644 index 00000000..7bcfa3af --- /dev/null +++ b/src/equicordplugins/iconViewer/rawModal.tsx @@ -0,0 +1,70 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { CodeBlock } from "@components/CodeBlock"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal +} from "@utils/modal"; +import { Button, Toasts } from "@webpack/common"; +import * as t from "@webpack/types"; + +import { ModalHeaderTitle } from "./subComponents"; + + + +function ModalComponent(props: { func: Function; iconName: string; color: number; } & ModalProps) { + const { func, iconName, color } = props; + return ( + + + + + +
+ +
+
+ + + + +
); +} + +export function openRawModal(iconName: string, Icon: t.Icon, colorIndex: number) { + openModal(props => ); +} + diff --git a/src/equicordplugins/iconViewer/saveModal.tsx b/src/equicordplugins/iconViewer/saveModal.tsx new file mode 100644 index 00000000..46270f6a --- /dev/null +++ b/src/equicordplugins/iconViewer/saveModal.tsx @@ -0,0 +1,153 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal +} from "@utils/modal"; +import { Button, Forms, Select, TextInput, useCallback, useEffect, useState } from "@webpack/common"; +import * as t from "@webpack/types"; + +import { ModalHeaderTitle } from "./subComponents"; +import { convertComponentToHtml, cssColors, iconSizesInPx, saveIcon } from "./utils"; + +type IDivElement = React.DetailedHTMLProps, HTMLDivElement>; + +export function NumericComponent({ onChange, value, className, style }: { onChange: (value: number) => void, value: number; className?: string; style?: React.CSSProperties; }) { + const handleChange = (value: string) => { + const newValue = Number(value); + if (isNaN(newValue)) return; + onChange(newValue); + }; + + return ( +
+ +
+ ); +} + +export function SelectComponent({ option, onChange, onError, className }: IDivElement & { option: any, onChange: (value: any) => void, onError: (msg: string | null) => void; className?: string; }) { + const [state, setState] = useState(option.options?.find(o => o.default)?.value ?? null); + const [error, setError] = useState(null); + + useEffect(() => onError(error), [error]); + + const handleChange = (newValue: any) => { + const isValid = option.isValid?.call({}, newValue) ?? true; + if (!isValid) setError("Invalid input provided."); + else { + setError(null); + setState(newValue); + onChange(newValue); + } + }; + + return (
+ { + return { value: device.id, label: settings.store.showOutputDeviceHeader ? device.name : `🔊 ${device.name}` }; + })} + serialize={identity} + isSelected={value => value === outputDevice} + select={id => { + FluxDispatcher.dispatch({ + type: "AUDIO_SET_OUTPUT_DEVICE", + id + }); + }}> + + + + ); +} + +function InputDeviceComponent() { + const [inputDevice, setInputDevice] = useState(configModule.getInputDeviceId()); + + useEffect(() => { + const listener = () => setInputDevice(configModule.getInputDeviceId()); + FluxDispatcher.subscribe("AUDIO_SET_INPUT_DEVICE", listener); + }); + + return ( +
+ {settings.store.showInputDeviceHeader && Input device} + +
+ ); +} + +function VideoDeviceComponent() { + const [videoDevice, setVideoDevice] = useState(configModule.getVideoDeviceId()); + + useEffect(() => { + const listener = () => setVideoDevice(configModule.getVideoDeviceId()); + FluxDispatcher.subscribe("MEDIA_ENGINE_SET_VIDEO_DEVICE", listener); + }); + + return ( +
+ {settings.store.showVideoDeviceHeader && Camera} + +
+ ); +} + +function VoiceSettings() { + const [showSettings, setShowSettings] = useState(settings.store.uncollapseSettingsByDefault); + return
+
+ { setShowSettings(!showSettings); }}>{!showSettings ? "► Settings" : "▼ Hide"} +
+ + { + showSettings && <> + {settings.store.outputVolume && } + {settings.store.inputVolume && } + {settings.store.outputDevice && } + {settings.store.inputDevice && } + {settings.store.camera && } + + } +
; +} + +export default definePlugin({ + name: "VCPanelSettings", + description: "Control voice settings right from the voice panel", + authors: [Devs.nin0dev], + settings, + renderVoiceSettings() { return ; }, + patches: [ + { + find: "this.renderChannelButtons()", + replacement: { + match: /this.renderChannelButtons\(\)/, + replace: "this.renderChannelButtons(), $self.renderVoiceSettings()" + } + } + ] +}); diff --git a/src/equicordplugins/vcPanelSettings/style.css b/src/equicordplugins/vcPanelSettings/style.css new file mode 100644 index 00000000..f5465741 --- /dev/null +++ b/src/equicordplugins/vcPanelSettings/style.css @@ -0,0 +1,3 @@ +.vc-panelsettings-underline-on-hover:hover { + text-decoration: underline; +}