2024-07-11 08:24:32 -04:00
|
|
|
/*
|
|
|
|
* Vencord, a Discord client mod
|
|
|
|
* Copyright (c) 2024 Vendicated and contributors
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { useSettings } from "@api/Settings";
|
|
|
|
import { classNameFactory } from "@api/Styles";
|
|
|
|
import { PluginCard } from "@components/PluginSettings";
|
|
|
|
import { ChangeList } from "@utils/ChangeList";
|
2024-08-22 23:53:51 -04:00
|
|
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
|
|
|
import { useForceUpdater } from "@utils/react";
|
|
|
|
import { Button, Flex, Forms, Parser, React, Text, Tooltip, useMemo } from "@webpack/common";
|
|
|
|
import { JSX } from "react";
|
2024-07-11 08:24:32 -04:00
|
|
|
|
|
|
|
import Plugins from "~plugins";
|
|
|
|
|
2024-08-22 23:53:51 -04:00
|
|
|
import { getNewPlugins, getNewSettings, KnownPluginSettingsMap, writeKnownSettings } from "./knownSettings";
|
2024-07-11 08:24:32 -04:00
|
|
|
|
|
|
|
const cl = classNameFactory("vc-plugins-");
|
|
|
|
|
|
|
|
let hasSeen = false;
|
|
|
|
|
|
|
|
// Most of this was stolen from PluginSettings directly.
|
|
|
|
|
2024-08-22 23:53:51 -04:00
|
|
|
export function NewPluginsModal({ modalProps, newPlugins, newSettings }: { modalProps: ModalProps; newPlugins: Set<string>; newSettings: KnownPluginSettingsMap; }) {
|
2024-07-11 08:24:32 -04:00
|
|
|
const settings = useSettings();
|
|
|
|
const changes = React.useMemo(() => new ChangeList<string>(), []);
|
2024-08-22 23:53:51 -04:00
|
|
|
let updateContinueButton = () => { };
|
2024-07-11 08:24:32 -04:00
|
|
|
|
|
|
|
const depMap = React.useMemo(() => {
|
|
|
|
const o = {} as Record<string, string[]>;
|
|
|
|
for (const plugin in Plugins) {
|
|
|
|
const deps = Plugins[plugin].dependencies;
|
|
|
|
if (deps) {
|
|
|
|
for (const dep of deps) {
|
|
|
|
o[dep] ??= [];
|
|
|
|
o[dep].push(plugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return o;
|
|
|
|
}, []);
|
|
|
|
|
2024-08-22 23:53:51 -04:00
|
|
|
const mapPlugins = (array: string[]) => array.map(pn => Plugins[pn])
|
|
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
|
|
|
|
const sortedPlugins = useMemo(() => [
|
|
|
|
...mapPlugins([...newPlugins]),
|
|
|
|
...mapPlugins([...newSettings.keys()].filter(p => !newPlugins.has(p)))
|
|
|
|
], []);
|
2024-07-11 08:24:32 -04:00
|
|
|
|
|
|
|
const plugins = [] as JSX.Element[];
|
|
|
|
const requiredPlugins = [] as JSX.Element[];
|
|
|
|
|
|
|
|
for (const p of sortedPlugins) {
|
|
|
|
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 Vencord to function."
|
|
|
|
: makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled));
|
|
|
|
|
|
|
|
requiredPlugins.push(
|
|
|
|
<Tooltip text={tooltipText} key={p.name}>
|
|
|
|
{({ onMouseLeave, onMouseEnter }) => (
|
|
|
|
<PluginCard
|
|
|
|
onMouseLeave={onMouseLeave}
|
|
|
|
onMouseEnter={onMouseEnter}
|
2024-08-22 23:53:51 -04:00
|
|
|
onRestartNeeded={name => {
|
|
|
|
changes.handleChange(name);
|
|
|
|
updateContinueButton();
|
|
|
|
}}
|
2024-07-11 08:24:32 -04:00
|
|
|
disabled={true}
|
|
|
|
plugin={p}
|
|
|
|
key={p.name}
|
2024-08-22 23:53:51 -04:00
|
|
|
isNew={newPlugins.has(p.name)}
|
2024-07-11 08:24:32 -04:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</Tooltip>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
plugins.push(
|
|
|
|
<PluginCard
|
2024-08-22 23:53:51 -04:00
|
|
|
onRestartNeeded={name => {
|
|
|
|
changes.handleChange(name);
|
|
|
|
updateContinueButton();
|
|
|
|
}}
|
2024-07-11 08:24:32 -04:00
|
|
|
disabled={false}
|
|
|
|
plugin={p}
|
|
|
|
key={p.name}
|
2024-08-22 23:53:51 -04:00
|
|
|
isNew={newPlugins.has(p.name)}
|
2024-07-11 08:24:32 -04:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return <ModalRoot {...modalProps} size={ModalSize.MEDIUM} >
|
|
|
|
<ModalHeader>
|
2024-08-22 23:53:51 -04:00
|
|
|
<Text variant="heading-lg/semibold">New Plugins and Settings ({[...plugins, ...requiredPlugins].length})</Text>
|
|
|
|
<Tooltip text="Dismiss for this session">
|
|
|
|
{tooltipProps =>
|
|
|
|
<ModalCloseButton
|
|
|
|
{...tooltipProps}
|
|
|
|
onClick={modalProps.onClose}
|
|
|
|
className={cl("close")}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
</Tooltip>
|
2024-07-11 08:24:32 -04:00
|
|
|
</ModalHeader>
|
|
|
|
<ModalContent>
|
|
|
|
<div className={cl("grid")}>
|
|
|
|
{[...plugins, ...requiredPlugins]}
|
|
|
|
</div>
|
|
|
|
</ModalContent>
|
|
|
|
<ModalFooter>
|
|
|
|
<Flex direction={Flex.Direction.HORIZONTAL_REVERSE}>
|
2024-08-22 23:53:51 -04:00
|
|
|
<ContinueButton
|
|
|
|
close={modalProps.onClose}
|
|
|
|
changes={changes}
|
|
|
|
callback={(v: () => void) => updateContinueButton = v}
|
|
|
|
/>
|
2024-07-11 08:24:32 -04:00
|
|
|
</Flex>
|
|
|
|
</ModalFooter>
|
|
|
|
</ModalRoot>;
|
|
|
|
}
|
|
|
|
|
2024-08-22 23:53:51 -04:00
|
|
|
function ContinueButton(props: { callback: (update: () => void) => void; changes: ChangeList<string>; close: () => any; }) {
|
|
|
|
const update = useForceUpdater();
|
|
|
|
props.callback(update);
|
|
|
|
return <Tooltip
|
|
|
|
text={<>
|
|
|
|
The following plugins require a restart:
|
|
|
|
<div>{props.changes.map((s, i) => (
|
|
|
|
<>
|
|
|
|
{i > 0 && ", "}
|
|
|
|
{Parser.parse("`" + s + "`")}
|
|
|
|
</>
|
|
|
|
))}</div>
|
|
|
|
</>}
|
|
|
|
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>;
|
|
|
|
}
|
|
|
|
|
2024-07-11 08:24:32 -04:00
|
|
|
|
|
|
|
function makeDependencyList(deps: string[]) {
|
|
|
|
return (
|
|
|
|
<React.Fragment>
|
|
|
|
<Forms.FormText>This plugin is required by:</Forms.FormText>
|
|
|
|
{deps.map((dep: string) => <Forms.FormText className={cl("dep-text")}>{dep}</Forms.FormText>)}
|
|
|
|
</React.Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function openNewPluginsModal() {
|
|
|
|
const newPlugins = await getNewPlugins();
|
2024-08-22 23:53:51 -04:00
|
|
|
const newSettings = await getNewSettings();
|
|
|
|
if (newSettings.size && !hasSeen) {
|
2024-07-11 08:24:32 -04:00
|
|
|
hasSeen = true;
|
|
|
|
openModal(modalProps => (
|
|
|
|
<NewPluginsModal
|
|
|
|
modalProps={modalProps}
|
|
|
|
newPlugins={newPlugins}
|
2024-08-22 23:53:51 -04:00
|
|
|
newSettings={newSettings}
|
2024-07-11 08:24:32 -04:00
|
|
|
/>
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|