diff --git a/src/plugins/serverProfilesToolbox/README.md b/src/plugins/serverProfilesToolbox/README.md new file mode 100644 index 000000000..493a1087a --- /dev/null +++ b/src/plugins/serverProfilesToolbox/README.md @@ -0,0 +1,5 @@ +# ServerProfilesToolbox + +Adds a copy/paste/reset button to the server profiles editor + +![Screenshot](https://github.com/Vendicated/Vencord/assets/24937357/3216d511-dc97-44fb-b4a7-24929ac14e6e) diff --git a/src/plugins/serverProfilesToolbox/index.tsx b/src/plugins/serverProfilesToolbox/index.tsx new file mode 100644 index 000000000..85b815b9d --- /dev/null +++ b/src/plugins/serverProfilesToolbox/index.tsx @@ -0,0 +1,190 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { + Button, + Clipboard, + GuildMemberStore, + Text, + Toasts, + UserProfileStore, + UserStore +} from "@webpack/common"; +import { GuildMember } from "discord-types/general"; + +const SummaryItem = findComponentByCodeLazy("borderType", "showBorder", "hideDivider"); + +interface SavedProfile { + nick: string | null; + pronouns: string | null; + bio: string | null; + themeColors: number[] | undefined; + banner: string | undefined; + avatar: string | undefined; + profileEffectId: string | undefined; + avatarDecoration: string | undefined; +} + +const savedProfile: SavedProfile = { + nick: null, + pronouns: null, + bio: null, + themeColors: undefined, + banner: undefined, + avatar: undefined, + profileEffectId: undefined, + avatarDecoration: undefined, +}; + +const { + setPendingAvatar, + setPendingBanner, + setPendingBio, + setPendingNickname, + setPendingPronouns, + setPendingThemeColors, + setPendingProfileEffectId, + setPendingAvatarDecoration, +}: { + setPendingAvatar: (a: string | undefined) => void; + setPendingBanner: (a: string | undefined) => void; + setPendingBio: (a: string | null) => void; + setPendingNickname: (a: string | null) => void; + setPendingPronouns: (a: string | null) => void; + setPendingThemeColors: (a: number[] | undefined) => void; + setPendingProfileEffectId: (a: string | undefined) => void; + setPendingAvatarDecoration: (a: string | undefined) => void; +} = findByPropsLazy("setPendingNickname", "setPendingPronouns"); + +export default definePlugin({ + name: "ServerProfilesToolbox", + authors: [Devs.D3SOX], + description: "Adds a copy/paste/reset button to the server profiles editor", + + patchServerProfiles({ guildId }: { guildId: string }) { + const currentUser = UserStore.getCurrentUser(); + const premiumType = currentUser.premiumType ?? 0; + + const copy = () => { + const profile = UserProfileStore.getGuildMemberProfile(currentUser.id, guildId); + const nick = GuildMemberStore.getNick(guildId, currentUser.id); + const selfMember = GuildMemberStore.getMember(guildId, currentUser.id) as GuildMember & { avatarDecoration: string | undefined }; + savedProfile.nick = nick ?? ""; + savedProfile.pronouns = profile.pronouns; + savedProfile.bio = profile.bio; + savedProfile.themeColors = profile.themeColors; + savedProfile.banner = profile.banner; + savedProfile.avatar = selfMember.avatar; + savedProfile.profileEffectId = profile.profileEffectId; + savedProfile.avatarDecoration = selfMember.avatarDecoration; + }; + + const paste = () => { + setPendingNickname(savedProfile.nick); + setPendingPronouns(savedProfile.pronouns); + if (premiumType === 2) { + setPendingBio(savedProfile.bio); + setPendingThemeColors(savedProfile.themeColors); + setPendingBanner(savedProfile.banner); + setPendingAvatar(savedProfile.avatar); + setPendingProfileEffectId(savedProfile.profileEffectId); + setPendingAvatarDecoration(savedProfile.avatarDecoration); + } + }; + + const reset = () => { + setPendingNickname(null); + setPendingPronouns(""); + if (premiumType === 2) { + setPendingBio(null); + setPendingThemeColors([]); + setPendingBanner(undefined); + setPendingAvatar(undefined); + setPendingProfileEffectId(undefined); + setPendingAvatarDecoration(undefined); + } + }; + + const copyToClipboard = () => { + copy(); + Clipboard.copy(JSON.stringify(savedProfile)); + }; + + const pasteFromClipboard = async () => { + try { + const clip = await navigator.clipboard.readText(); + if (!clip) { + Toasts.show({ + message: "Clipboard is empty", + type: Toasts.Type.FAILURE, + id: Toasts.genId(), + }); + return; + } + const clipboardProfile: SavedProfile = JSON.parse(clip); + + if (!("nick" in clipboardProfile)) { + Toasts.show({ + message: "Data is not in correct format", + type: Toasts.Type.FAILURE, + id: Toasts.genId(), + }); + return; + } + + Object.assign(savedProfile, JSON.parse(clip)); + paste(); + } catch (e) { + Toasts.show({ + message: `Failed to read clipboard data: ${e}`, + type: Toasts.Type.FAILURE, + id: Toasts.genId(), + }); + } + }; + + return +
+ + Use the following buttons to mange the currently selected server + +
+ + + +
+
+ + +
+
+
; + }, + + patches: [ + { + find: ".PROFILE_CUSTOMIZATION_GUILD_SELECT_TITLE", + replacement: { + match: /return\(0(.{10,350})\}\)\}\)\}/, + replace: "return [(0$1})}),$self.patchServerProfiles(e)]}" + } + } + ], + +});