aboutsummaryrefslogtreecommitdiff
path: root/src/components/PluginSettings
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/PluginSettings')
-rw-r--r--src/components/PluginSettings/ContributorModal.tsx113
-rw-r--r--src/components/PluginSettings/PluginModal.tsx29
-rw-r--r--src/components/PluginSettings/contributorModal.css57
-rw-r--r--src/components/PluginSettings/index.tsx2
-rw-r--r--src/components/PluginSettings/userPopoutHideBotTag.css3
5 files changed, 186 insertions, 18 deletions
diff --git a/src/components/PluginSettings/ContributorModal.tsx b/src/components/PluginSettings/ContributorModal.tsx
new file mode 100644
index 0000000..82c2302
--- /dev/null
+++ b/src/components/PluginSettings/ContributorModal.tsx
@@ -0,0 +1,113 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2023 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import "./contributorModal.css";
+
+import { useSettings } from "@api/Settings";
+import { classNameFactory } from "@api/Styles";
+import ErrorBoundary from "@components/ErrorBoundary";
+import { DevsById } from "@utils/constants";
+import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
+import { ModalContent, ModalRoot, openModal } from "@utils/modal";
+import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
+import { User } from "discord-types/general";
+
+import Plugins from "~plugins";
+
+import { PluginCard } from ".";
+
+const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
+const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
+const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
+const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
+
+const cl = classNameFactory("vc-author-modal-");
+
+export function openContributorModal(user: User) {
+ openModal(modalProps =>
+ <ModalRoot {...modalProps}>
+ <ErrorBoundary>
+ <ModalContent className={cl("root")}>
+ <ContributorModal user={user} />
+ </ModalContent>
+ </ErrorBoundary>
+ </ModalRoot>
+ );
+}
+
+function GithubIcon() {
+ const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
+ return <img src={src} alt="GitHub" />;
+}
+
+function WebsiteIcon() {
+ const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
+ return <img src={src} alt="Website" />;
+}
+
+function ContributorModal({ user }: { user: User; }) {
+ useSettings();
+
+ const profile = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(user.id));
+
+ useEffect(() => {
+ if (!profile && !user.bot && user.id)
+ fetchUserProfile(user.id);
+ }, [user.id]);
+
+ const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
+ const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
+
+ const plugins = useMemo(() => {
+ const allPlugins = Object.values(Plugins);
+ const pluginsByAuthor = DevsById[user.id]
+ ? allPlugins.filter(p => p.authors.includes(DevsById[user.id]))
+ : allPlugins.filter(p => p.authors.some(a => a.name === user.username));
+
+ return pluginsByAuthor
+ .filter(p => !p.name.endsWith("API"))
+ .sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
+ }, [user.id, user.username]);
+
+ return (
+ <>
+ <div className={cl("header")}>
+ <img
+ className={cl("avatar")}
+ src={user.getAvatarURL(void 0, 512, true)}
+ alt=""
+ />
+ <Forms.FormTitle tag="h2" className={cl("name")}>{user.username}</Forms.FormTitle>
+
+ <div className={cl("links")}>
+ {website && (
+ <MaskedLink
+ href={"https://" + website}
+ >
+ <WebsiteIcon />
+ </MaskedLink>
+ )}
+ {githubName && (
+ <MaskedLink href={`https://github.com/${githubName}`}>
+ <GithubIcon />
+ </MaskedLink>
+ )}
+ </div>
+ </div>
+
+ <div className={cl("plugins")}>
+ {plugins.map(p =>
+ <PluginCard
+ key={p.name}
+ plugin={p}
+ disabled={p.required ?? false}
+ onRestartNeeded={() => showToast("Restart to apply changes!")}
+ />
+ )}
+ </div>
+ </>
+ );
+}
diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx
index f30cede..78f3c9d 100644
--- a/src/components/PluginSettings/PluginModal.tsx
+++ b/src/components/PluginSettings/PluginModal.tsx
@@ -18,7 +18,6 @@
import { generateId } from "@api/Commands";
import { useSettings } from "@api/Settings";
-import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy";
@@ -28,7 +27,7 @@ import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, M
import { LazyComponent } from "@utils/react";
import { OptionType, Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack";
-import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
+import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
import { User } from "discord-types/general";
import { Constructor } from "type-fest";
@@ -41,7 +40,7 @@ import {
SettingSliderComponent,
SettingTextComponent
} from "./components";
-import hideBotTagStyle from "./userPopoutHideBotTag.css?managed";
+import { openContributorModal } from "./ContributorModal";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
@@ -92,27 +91,16 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
React.useEffect(() => {
- enableStyle(hideBotTagStyle);
-
- let originalUser: User;
(async () => {
for (const user of plugin.authors.slice(0, 6)) {
const author = user.id
? await UserUtils.fetchUser(`${user.id}`)
- // only show name & pfp and no actions so users cannot harass plugin devs for support (send dms, add as friend, etc)
- .then(u => (originalUser = u, makeDummyUser(u)))
.catch(() => makeDummyUser({ username: user.name }))
: makeDummyUser({ username: user.name });
setAuthors(a => [...a, author]);
}
})();
-
- return () => {
- disableStyle(hideBotTagStyle);
- if (originalUser)
- FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
- };
}, []);
async function saveAndClose() {
@@ -214,6 +202,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
showDefaultAvatarsForNullUsers
showUserPopout
renderMoreUsers={renderMoreUsers}
+ renderUser={(user: User) => (
+ <Clickable
+ className={AvatarStyles.clickableAvatar}
+ onClick={() => openContributorModal(user)}
+ >
+ <img
+ className={AvatarStyles.avatar}
+ src={user.getAvatarURL(void 0, 80, true)}
+ alt={user.username}
+ title={user.username}
+ />
+ </Clickable>
+ )}
/>
</div>
</Forms.FormSection>
diff --git a/src/components/PluginSettings/contributorModal.css b/src/components/PluginSettings/contributorModal.css
new file mode 100644
index 0000000..a8af8c8
--- /dev/null
+++ b/src/components/PluginSettings/contributorModal.css
@@ -0,0 +1,57 @@
+.vc-author-modal-root {
+ padding: 1em;
+}
+
+.vc-author-modal-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 1em;
+}
+
+.vc-author-modal-name {
+ text-transform: none;
+ flex-grow: 0;
+ background: var(--background-tertiary);
+ border-radius: 0 9999px 9999px 0;
+ padding: 6px 0.8em 6px 0.5em;
+ font-size: 20px;
+ height: 20px;
+ position: relative;
+}
+
+.vc-author-modal-name::before {
+ content: "";
+ display: block;
+ position: absolute;
+ height: 100%;
+ width: 16px;
+ background: var(--background-tertiary);
+ z-index: -1;
+ left: -16px;
+ top: 0;
+}
+
+.vc-author-modal-avatar {
+ height: 32px;
+ width: 32px;
+ border-radius: 50%;
+}
+
+.vc-author-modal-links {
+ margin-left: auto;
+ display: flex;
+ gap: 0.2em;
+}
+
+.vc-author-modal-links img {
+ height: 32px;
+ width: 32px;
+ border-radius: 50%;
+ border: 4px solid var(--background-tertiary);
+ box-sizing: border-box
+}
+
+.vc-author-modal-plugins {
+ display: grid;
+ gap: 0.5em;
+}
diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx
index f19d326..3712f3d 100644
--- a/src/components/PluginSettings/index.tsx
+++ b/src/components/PluginSettings/index.tsx
@@ -91,7 +91,7 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
isNew?: boolean;
}
-function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
+export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
const settings = Settings.plugins[plugin.name];
const isEnabled = () => settings.enabled ?? false;
diff --git a/src/components/PluginSettings/userPopoutHideBotTag.css b/src/components/PluginSettings/userPopoutHideBotTag.css
deleted file mode 100644
index 5e33e4b..0000000
--- a/src/components/PluginSettings/userPopoutHideBotTag.css
+++ /dev/null
@@ -1,3 +0,0 @@
-[class|="userPopoutOuter"] [class*="botTag"] {
- display: none;
-}