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
This commit is contained in:
parent
faff196008
commit
52484e5d72
3 changed files with 149 additions and 128 deletions
|
|
@ -381,7 +381,7 @@ function ThemesTab() {
|
|||
const [themeNames, setThemeNames] = useState<Record<string, string>>(() => {
|
||||
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() {
|
|||
) : (
|
||||
<QuickAction
|
||||
text="Open Themes Folder"
|
||||
action={() => showItemInFolder(themeDir!)}
|
||||
disabled={themeDirPending}
|
||||
action={() => VencordNative.themes.openFolder()}
|
||||
Icon={FolderIcon}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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<string>;
|
||||
newSettings: KnownPluginSettingsMap;
|
||||
}
|
||||
|
||||
export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modalProps: ModalProps; newPlugins: Set<string>; newSettings: KnownPluginSettingsMap; }) {
|
||||
function NewPluginsModal({ modalProps, newPlugins, newSettings }: ModalComponentProps) {
|
||||
const settings = useSettings();
|
||||
const changes = React.useMemo(() => new ChangeList<string>(), []);
|
||||
let updateContinueButton = () => { };
|
||||
const changes = useMemo(() => new ChangeList<string>(), []);
|
||||
const forceUpdate = useForceUpdater();
|
||||
|
||||
const depMap = React.useMemo(() => {
|
||||
const depMap = useMemo(() => {
|
||||
const o = {} as Record<string, string[]>;
|
||||
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));
|
||||
: <PluginDependencyList deps={depMap[p.name]?.filter(d => settings.plugins[d].enabled)} />;
|
||||
|
||||
requiredPlugins.push(
|
||||
requiredPluginCards.push(
|
||||
<Tooltip text={tooltipText} key={p.name}>
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<PluginCard
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onRestartNeeded={name => {
|
||||
changes.handleChange(name);
|
||||
updateContinueButton();
|
||||
}}
|
||||
onRestartNeeded={onRestartNeeded}
|
||||
disabled={true}
|
||||
plugin={p}
|
||||
key={p.name}
|
||||
isNew={newPlugins.has(p.name)}
|
||||
/>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
plugins.push(
|
||||
pluginCards.push(
|
||||
<PluginCard
|
||||
onRestartNeeded={name => {
|
||||
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 (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false} className={cl("header")}>
|
||||
<div className={cl("header-content")}>
|
||||
<BaseText size="lg" weight="semibold" className={cl("title")}>
|
||||
New Plugins and Settings ({totalCount})
|
||||
</BaseText>
|
||||
<BaseText size="sm" className={cl("description")}>
|
||||
New plugins have been added since your last visit. Enable any you'd like or continue to dismiss.
|
||||
</BaseText>
|
||||
</div>
|
||||
<div className={cl("header-trailing")}>
|
||||
<CloseButton onClick={modalProps.onClose} />
|
||||
</div>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<div className={cl("grid")}>
|
||||
{pluginCards}
|
||||
{requiredPluginCards}
|
||||
</div>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Flex className={cl("footer")}>
|
||||
<Tooltip
|
||||
text={
|
||||
<>
|
||||
The following plugins require a restart:
|
||||
<ul className={cl("restart-list")}>
|
||||
{changes.map(p => <li key={p}>{p}</li>)}
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
shouldShow={changes.hasChanges}
|
||||
>
|
||||
{tooltipProps => (
|
||||
<Button
|
||||
{...tooltipProps}
|
||||
onClick={handleContinue}
|
||||
>
|
||||
{changes.hasChanges ? "Restart" : "Continue"}
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
return <ModalRoot {...modalProps} size={ModalSize.MEDIUM} >
|
||||
<ModalHeader>
|
||||
<BaseText size="lg" weight="semibold" style={{ flexGrow: 1 }}>New Plugins and Settings ({[...plugins, ...requiredPlugins].length})</BaseText>
|
||||
<Tooltip text="Dismiss for this session">
|
||||
{tooltipProps =>
|
||||
<div {...tooltipProps}>
|
||||
<ModalCloseButton
|
||||
onClick={modalProps.onClose}
|
||||
className={classes(cl("close"), "vc-newPluginsManager-close")}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</Tooltip>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<div className={cl("grid")}>
|
||||
{[...plugins, ...requiredPlugins]}
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex flexDirection="row-reverse">
|
||||
<ContinueButton
|
||||
close={modalProps.onClose}
|
||||
changes={changes}
|
||||
callback={(v: () => void) => updateContinueButton = v}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex flexDirection="row">
|
||||
<div className="vc-newPluginsManager-disable-wrapper">
|
||||
<Checkbox
|
||||
type="inverted"
|
||||
value={!settings?.plugins?.NewPluginsManager?.enabled}
|
||||
|
|
@ -145,50 +174,11 @@ export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modal
|
|||
Settings.plugins.NewPluginsManager.enabled = !settings?.plugins?.NewPluginsManager?.enabled;
|
||||
}}
|
||||
>
|
||||
<BaseText>Don't show this again</BaseText>
|
||||
Don't show this again
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>;
|
||||
}
|
||||
|
||||
function ContinueButton(props: { callback: (update: () => void) => void; changes: ChangeList<string>; close: () => any; }) {
|
||||
const update = useForceUpdater();
|
||||
props.callback(update);
|
||||
return <Tooltip
|
||||
tooltipClassName="vc-newPluginsManager-restart-tooltip"
|
||||
text={<>
|
||||
The following plugins require a restart:
|
||||
<div className={Margins.bottom8} />
|
||||
<ul className="vc-newPluginsManager-restart-list">
|
||||
{props.changes.map(p => <li key={p}>{p}</li>)}
|
||||
</ul>
|
||||
</>}
|
||||
shouldShow={props.changes.hasChanges}
|
||||
>
|
||||
{tooltipProps =>
|
||||
<Button
|
||||
{...tooltipProps}
|
||||
color={Button.Colors.GREEN}
|
||||
onClick={async () => {
|
||||
await writeKnownSettings();
|
||||
props.changes.hasChanges ? location.reload() : props.close();
|
||||
}}
|
||||
>
|
||||
{props.changes.hasChanges ? "Restart" : "Continue"}
|
||||
</Button>
|
||||
}
|
||||
</Tooltip>;
|
||||
}
|
||||
|
||||
|
||||
function makeDependencyList(deps: string[]) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Paragraph>This plugin is required by:</Paragraph>
|
||||
{deps.map((dep: string) => <Paragraph key={dep} className={cl("dep-text")}>{dep}</Paragraph>)}
|
||||
</React.Fragment>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -197,12 +187,14 @@ export async function openNewPluginsModal() {
|
|||
const newSettings = await getNewSettings();
|
||||
if ((newPlugins.size || newSettings.size) && !hasSeen) {
|
||||
hasSeen = true;
|
||||
const modalKey = openModal(modalProps => <ErrorBoundary noop onError={() => { closeModal(modalKey); }}>
|
||||
<NewPluginsModal
|
||||
modalProps={modalProps}
|
||||
newPlugins={newPlugins}
|
||||
newSettings={newSettings}
|
||||
/>
|
||||
</ErrorBoundary>);
|
||||
const modalKey = openModal(modalProps => (
|
||||
<ErrorBoundary noop onError={() => closeModal(modalKey)}>
|
||||
<NewPluginsModal
|
||||
modalProps={modalProps}
|
||||
newPlugins={newPlugins}
|
||||
newSettings={newSettings}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue