From a67db11dc2554c9360d1ef8e97b4b558b71045af Mon Sep 17 00:00:00 2001
From: A user <pedro.santos.cartaxo@gmail.com>
Date: Tue, 13 Dec 2022 20:44:57 -0300
Subject: [PATCH] Improve Settings UI & View Raw Modal (#332)

very cool
---
 src/components/PluginSettings/PluginModal.tsx | 69 ++++++++++---------
 src/plugins/viewRaw.tsx                       | 43 ++++++------
 src/webpack/common.tsx                        |  2 +-
 3 files changed, 61 insertions(+), 53 deletions(-)

diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx
index 7cff58f7..46568505 100644
--- a/src/components/PluginSettings/PluginModal.tsx
+++ b/src/components/PluginSettings/PluginModal.tsx
@@ -21,7 +21,7 @@ import { useSettings } from "@api/settings";
 import ErrorBoundary from "@components/ErrorBoundary";
 import { Flex } from "@components/Flex";
 import { LazyComponent } from "@utils/misc";
-import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
+import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
 import { proxyLazy } from "@utils/proxyLazy";
 import { OptionType, Plugin } from "@utils/types";
 import { findByCode, findByPropsLazy } from "@webpack";
@@ -84,6 +84,8 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
 
     const canSubmit = () => Object.values(errors).every(e => !e);
 
+    const hasSettings = Boolean(pluginSettings && plugin.options);
+
     React.useEffect(() => {
         (async () => {
             for (const user of plugin.authors.slice(0, 6)) {
@@ -121,33 +123,33 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
     }
 
     function renderSettings() {
-        if (!pluginSettings || !plugin.options) {
+        if (!hasSettings || !plugin.options) {
             return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
+        } else {
+            const options = Object.entries(plugin.options).map(([key, setting]) => {
+                function onChange(newValue: any) {
+                    setTempSettings(s => ({ ...s, [key]: newValue }));
+                }
+
+                function onError(hasError: boolean) {
+                    setErrors(e => ({ ...e, [key]: hasError }));
+                }
+
+                const Component = Components[setting.type];
+                return (
+                    <Component
+                        id={key}
+                        key={key}
+                        option={setting}
+                        onChange={onChange}
+                        onError={onError}
+                        pluginSettings={pluginSettings}
+                    />
+                );
+            });
+
+            return <Flex flexDirection="column" style={{ gap: 12 }}>{options}</Flex>;
         }
-
-        const options = Object.entries(plugin.options).map(([key, setting]) => {
-            function onChange(newValue: any) {
-                setTempSettings(s => ({ ...s, [key]: newValue }));
-            }
-
-            function onError(hasError: boolean) {
-                setErrors(e => ({ ...e, [key]: hasError }));
-            }
-
-            const Component = Components[setting.type];
-            return (
-                <Component
-                    id={key}
-                    key={key}
-                    option={setting}
-                    onChange={onChange}
-                    onError={onError}
-                    pluginSettings={pluginSettings}
-                />
-            );
-        });
-
-        return <Flex flexDirection="column" style={{ gap: 12 }}>{options}</Flex>;
     }
 
     function renderMoreUsers(_label: string, count: number) {
@@ -172,14 +174,16 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
 
     return (
         <ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM}>
-            <ModalHeader>
-                <Text variant="heading-md/bold">{plugin.name}</Text>
+            <ModalHeader separator={false}>
+                <Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{plugin.name}</Text>
+                <ModalCloseButton onClick={onClose} />
             </ModalHeader>
             <ModalContent style={{ marginBottom: 8, marginTop: 8 }}>
                 <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" }}>
+                    <Forms.FormTitle tag="h3" style={{ marginTop: 8, marginBottom: 0 }}>Authors</Forms.FormTitle>
+                    <div style={{ width: "fit-content", marginBottom: 8 }}>
                         <UserSummaryItem
                             users={authors}
                             count={plugin.authors.length}
@@ -206,13 +210,14 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
                     {renderSettings()}
                 </Forms.FormSection>
             </ModalContent>
-            <ModalFooter>
+            {hasSettings && <ModalFooter>
                 <Flex flexDirection="column" style={{ width: "100%" }}>
                     <Flex style={{ marginLeft: "auto" }}>
                         <Button
                             onClick={onClose}
                             size={Button.Sizes.SMALL}
-                            color={Button.Colors.RED}
+                            color={Button.Colors.WHITE}
+                            look={Button.Looks.LINK}
                         >
                             Cancel
                         </Button>
@@ -233,7 +238,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
                     </Flex>
                     {saveError && <Text variant="text-md/semibold" style={{ color: "var(--text-danger)" }}>Error while saving: {saveError}</Text>}
                 </Flex>
-            </ModalFooter>
+            </ModalFooter>}
         </ModalRoot>
     );
 }
diff --git a/src/plugins/viewRaw.tsx b/src/plugins/viewRaw.tsx
index c49180b8..fc7a42a0 100644
--- a/src/plugins/viewRaw.tsx
+++ b/src/plugins/viewRaw.tsx
@@ -21,9 +21,9 @@ import ErrorBoundary from "@components/ErrorBoundary";
 import { Flex } from "@components/Flex";
 import { Devs } from "@utils/constants";
 import { copyWithToast } from "@utils/misc";
-import { closeModal, ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
+import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
 import definePlugin from "@utils/types";
-import { Button, ChannelStore, Forms, Margins, Parser } from "@webpack/common";
+import { Button, ChannelStore, Forms, Margins, Parser, Text } from "@webpack/common";
 import { Message } from "discord-types/general";
 
 
@@ -89,30 +89,33 @@ function openViewRawModal(msg: Message) {
         <ErrorBoundary>
             <ModalRoot {...props} size={ModalSize.LARGE}>
                 <ModalHeader>
-                    <Forms.FormTitle tag="h1">View Raw</Forms.FormTitle>
+                    <Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>View Raw</Text>
                     <ModalCloseButton onClick={() => closeModal(key)} />
                 </ModalHeader>
-                <ModalContent style={{ padding: "1em" }}>
-                    <Flex style={{ marginBottom: "1em", marginTop: "1em" }}>
-                        <Button onClick={() => copyWithToast(msg.content, "Content copied to clipboard!")}>
-                            Copy Raw Content
-                        </Button>
+                <ModalContent>
+                    <div style={{ padding: "16px 0" }}>
+                        {!!msg.content && (
+                            <>
+                                <Forms.FormTitle tag="h5">Content</Forms.FormTitle>
+                                <CodeBlock content={msg.content} lang="" />
+                                <Forms.FormDivider classes={Margins.marginBottom20} />
+                            </>
+                        )}
+
+                        <Forms.FormTitle tag="h5">Message Data</Forms.FormTitle>
+                        <CodeBlock content={msgJson} lang="json" />
+                    </div>
+                </ModalContent >
+                <ModalFooter>
+                    <Flex cellSpacing={10}>
                         <Button onClick={() => copyWithToast(msgJson, "Message data copied to clipboard!")}>
                             Copy Message JSON
                         </Button>
+                        <Button onClick={() => copyWithToast(msg.content, "Content copied to clipboard!")}>
+                            Copy Raw Content
+                        </Button>
                     </Flex>
-
-                    {!!msg.content && (
-                        <>
-                            <Forms.FormTitle tag="h5">Content</Forms.FormTitle>
-                            <CodeBlock content={msg.content} lang="" />
-                            <Forms.FormDivider classes={Margins.marginBottom20} />
-                        </>
-                    )}
-
-                    <Forms.FormTitle tag="h5">Message Data</Forms.FormTitle>
-                    <CodeBlock content={msgJson} lang="json" />
-                </ModalContent >
+                </ModalFooter>
             </ModalRoot >
         </ErrorBoundary >
     ));
diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx
index 0a5fd7d5..2ee2d5d2 100644
--- a/src/webpack/common.tsx
+++ b/src/webpack/common.tsx
@@ -207,7 +207,7 @@ export type TextProps = React.PropsWithChildren & {
     className?: string;
 };
 
-export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
+export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
 
 type RC<C> = React.ComponentType<React.PropsWithChildren<C & Record<string, any>>>;
 interface Menu {