From 52484e5d72c2bba57cb0805230fff75437199fa0 Mon Sep 17 00:00:00 2001 From: prism Date: Tue, 16 Dec 2025 21:28:46 +0100 Subject: [PATCH] some ui related fixes and modifications (#568) * more modal tweaks * t * (IconViewer): better icon detection (+60 atm) * fix open themes folder * ui tweaks and minor fixes --- src/components/ThemeSettings/ThemesTab.tsx | 5 +- .../newPluginsManager/NewPluginsModal.tsx | 216 +++++++++--------- .../newPluginsManager/styles.css | 56 +++-- 3 files changed, 149 insertions(+), 128 deletions(-) diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index fd83cc412..bde731b08 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -381,7 +381,7 @@ function ThemesTab() { const [themeNames, setThemeNames] = useState>(() => { return settings.themeNames ?? {}; }); - const [themeDir, , themeDirPending] = useAwaiter(VencordNative.themes.getThemesDir); + const [themeDir] = useAwaiter(VencordNative.themes.getThemesDir); const [searchQuery, setSearchQuery] = useState(""); const [filter, setFilter] = useState(ThemeFilter.All); @@ -686,8 +686,7 @@ function ThemesTab() { ) : ( showItemInFolder(themeDir!)} - disabled={themeDirPending} + action={() => VencordNative.themes.openFolder()} Icon={FolderIcon} /> )} diff --git a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx index 42ec9670b..9f1c60510 100644 --- a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx +++ b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx @@ -9,36 +9,41 @@ import "./styles.css"; import { Settings, useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import { BaseText } from "@components/BaseText"; +import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; -import { Paragraph } from "@components/Paragraph"; +import { PluginDependencyList } from "@components/settings/tabs/plugins"; import { PluginCard } from "@components/settings/tabs/plugins/PluginCard"; import { ChangeList } from "@utils/ChangeList"; -import { classes, Margins } from "@utils/index"; -import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useForceUpdater } from "@utils/react"; import { findComponentByCodeLazy } from "@webpack"; -import { Button, React, Tooltip, useMemo } from "@webpack/common"; -import { JSX } from "react"; +import { Tooltip, useMemo } from "@webpack/common"; +import { ReactNode } from "react"; import Plugins from "~plugins"; import { getNewPlugins, getNewSettings, KnownPluginSettingsMap, writeKnownSettings } from "./knownSettings"; -const cl = classNameFactory("vc-plugins-"); +const cl = classNameFactory("vc-new-plugins-"); +const CloseButton = findComponentByCodeLazy("CLOSE_BUTTON_LABEL"); const Checkbox = findComponentByCodeLazy(".checkboxWrapperDisabled:"); let hasSeen = false; -// Most of this was stolen from PluginSettings directly. +interface ModalComponentProps { + modalProps: ModalProps; + newPlugins: Set; + newSettings: KnownPluginSettingsMap; +} -export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modalProps: ModalProps; newPlugins: Set; newSettings: KnownPluginSettingsMap; }) { +function NewPluginsModal({ modalProps, newPlugins, newSettings }: ModalComponentProps) { const settings = useSettings(); - const changes = React.useMemo(() => new ChangeList(), []); - let updateContinueButton = () => { }; + const changes = useMemo(() => new ChangeList(), []); + const forceUpdate = useForceUpdater(); - const depMap = React.useMemo(() => { + const depMap = useMemo(() => { const o = {} as Record; for (const plugin in Plugins) { const deps = Plugins[plugin].dependencies; @@ -52,53 +57,50 @@ export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modal return o; }, []); - const mapPlugins = (array: string[]) => array.map(pn => Plugins[pn]) - .sort((a, b) => a.name.localeCompare(b.name)); + const sortedPlugins = useMemo(() => { + const mapPlugins = (array: string[]) => array.map(pn => Plugins[pn]).sort((a, b) => a.name.localeCompare(b.name)); + return [ + ...mapPlugins([...newPlugins]), + ...mapPlugins([...newSettings.keys()].filter(p => !newPlugins.has(p))) + ]; + }, []); - const sortedPlugins = useMemo(() => [ - ...mapPlugins([...newPlugins]), - ...mapPlugins([...newSettings.keys()].filter(p => !newPlugins.has(p))) - ], []); + const onRestartNeeded = (name: string) => { + changes.handleChange(name); + forceUpdate(); + }; - const plugins = [] as JSX.Element[]; - const requiredPlugins = [] as JSX.Element[]; + const pluginCards: ReactNode[] = []; + const requiredPluginCards: ReactNode[] = []; for (const p of sortedPlugins) { - if (p.hidden) - continue; + if (p.hidden) continue; const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled); if (isRequired) { const tooltipText = p.required ? "This plugin is required for Equicord to function." - : makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled)); + : settings.plugins[d].enabled)} />; - requiredPlugins.push( + requiredPluginCards.push( {({ onMouseLeave, onMouseEnter }) => ( { - changes.handleChange(name); - updateContinueButton(); - }} + onRestartNeeded={onRestartNeeded} disabled={true} plugin={p} - key={p.name} isNew={newPlugins.has(p.name)} /> )} ); } else { - plugins.push( + pluginCards.push( { - changes.handleChange(name); - updateContinueButton(); - }} + onRestartNeeded={onRestartNeeded} disabled={false} plugin={p} key={p.name} @@ -108,36 +110,63 @@ export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modal } } + const totalCount = pluginCards.length + requiredPluginCards.length; + + const handleContinue = async () => { + await writeKnownSettings(); + if (changes.hasChanges) { + location.reload(); + } else { + modalProps.onClose(); + } + }; + + return ( + + +
+ + New Plugins and Settings ({totalCount}) + + + New plugins have been added since your last visit. Enable any you'd like or continue to dismiss. + +
+
+ +
+
+ + +
+ {pluginCards} + {requiredPluginCards} +
+
+ + + + + The following plugins require a restart: +
    + {changes.map(p =>
  • {p}
  • )} +
+ + } + shouldShow={changes.hasChanges} + > + {tooltipProps => ( + + )} +
- return - - New Plugins and Settings ({[...plugins, ...requiredPlugins].length}) - - {tooltipProps => -
- -
- } -
-
- -
- {[...plugins, ...requiredPlugins]} -
-
- - - void) => updateContinueButton = v} - /> - - -
- Don't show this again + Don't show this again -
-
-
-
; -} - -function ContinueButton(props: { callback: (update: () => void) => void; changes: ChangeList; close: () => any; }) { - const update = useForceUpdater(); - props.callback(update); - return - The following plugins require a restart: -
-
    - {props.changes.map(p =>
  • {p}
  • )} -
- } - shouldShow={props.changes.hasChanges} - > - {tooltipProps => - - } - ; -} - - -function makeDependencyList(deps: string[]) { - return ( - - This plugin is required by: - {deps.map((dep: string) => {dep})} - + + + ); } @@ -197,12 +187,14 @@ export async function openNewPluginsModal() { const newSettings = await getNewSettings(); if ((newPlugins.size || newSettings.size) && !hasSeen) { hasSeen = true; - const modalKey = openModal(modalProps => { closeModal(modalKey); }}> - - ); + const modalKey = openModal(modalProps => ( + closeModal(modalKey)}> + + + )); } } diff --git a/src/equicordplugins/newPluginsManager/styles.css b/src/equicordplugins/newPluginsManager/styles.css index 163505ae7..d7e4fd9e4 100644 --- a/src/equicordplugins/newPluginsManager/styles.css +++ b/src/equicordplugins/newPluginsManager/styles.css @@ -1,20 +1,50 @@ -.vc-newPluginsManager-restart-tooltip { - max-width: 280px; +.vc-new-plugins-header { + align-items: flex-start; + position: relative; + padding: 16px 16px 0 24px; } -.vc-newPluginsManager-restart-list { +.vc-new-plugins-header-content { + display: flex; + flex-direction: column; + gap: 8px; + flex: 1; +} + +.vc-new-plugins-header-trailing { + position: absolute; + top: 8px; + right: 8px; +} + +.vc-new-plugins-title { + color: var(--text-strong); + padding-right: 32px; +} + +.vc-new-plugins-description { + color: var(--text-subtle); + padding-right: 60px; +} + +.vc-new-plugins-grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); +} + +.vc-new-plugins-footer { + width: 100%; + align-items: center; + flex-direction: row-reverse; + gap: 16px; +} + +.vc-new-plugins-restart-list { padding-left: 1.25em; + margin-top: 8px; } -.vc-newPluginsManager-restart-list li { +.vc-new-plugins-restart-list li { list-style: disc; } - -.vc-newPluginsManager-close > div { - display: flex; -} - -.vc-newPluginsManager-disable-wrapper { - display: flex; - margin: auto 0; -} \ No newline at end of file