Settings UI refactor (#2086)

* Full width settings page
* Group settings
* Make config fields optional
* auto save on change
* Add settings context
* Refactor stash library section
* Restructure settings
* Refactor tasks page
* Add collapse buttons for setting groups
* Add collapse buttons in library
* Add loading indicator
* Simplify task options. Add details to manual
* Add manual links to tasks page
* Add help tooltips
* Refactor about page
* Refactor log page
* Refactor tools panel
* Refactor plugin page
* Refactor task queue
* Improve disabled styling
This commit is contained in:
WithoutPants
2021-12-14 15:06:05 +11:00
committed by GitHub
parent b4b955efc8
commit d176e9f192
44 changed files with 3540 additions and 3022 deletions

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useMemo } from "react";
import { Button } from "react-bootstrap";
import { FormattedMessage, useIntl } from "react-intl";
import * as GQL from "src/core/generated-graphql";
@@ -6,6 +6,8 @@ import { mutateReloadPlugins, usePlugins } from "src/core/StashService";
import { useToast } from "src/hooks";
import { TextUtils } from "src/utils";
import { CollapseButton, Icon, LoadingIndicator } from "src/components/Shared";
import { SettingSection } from "./SettingSection";
import { Setting, SettingGroup } from "./Inputs";
export const SettingsPluginsPanel: React.FC = () => {
const Toast = useToast();
@@ -17,91 +19,101 @@ export const SettingsPluginsPanel: React.FC = () => {
await mutateReloadPlugins().catch((e) => Toast.error(e));
}
function renderLink(url?: string) {
if (url) {
const pluginElements = useMemo(() => {
function renderLink(url?: string) {
if (url) {
return (
<Button className="minimal">
<a
href={TextUtils.sanitiseURL(url)}
className="link"
target="_blank"
rel="noopener noreferrer"
>
<Icon icon="link" />
</a>
</Button>
);
}
}
function renderPlugins() {
const elements = (data?.plugins ?? []).map((plugin) => (
<SettingGroup
key={plugin.id}
settingProps={{
heading: `${plugin.name} ${
plugin.version ? `(${plugin.version})` : undefined
}`,
subHeading: plugin.description,
}}
topLevel={renderLink(plugin.url ?? undefined)}
>
{renderPluginHooks(plugin.hooks ?? undefined)}
</SettingGroup>
));
return <div>{elements}</div>;
}
function renderPluginHooks(
hooks?: Pick<GQL.PluginHook, "name" | "description" | "hooks">[]
) {
if (!hooks || hooks.length === 0) {
return;
}
return (
<Button className="minimal">
<a
href={TextUtils.sanitiseURL(url)}
className="link"
target="_blank"
rel="noopener noreferrer"
>
<Icon icon="link" />
</a>
</Button>
<div className="setting">
<div>
<h5>
<FormattedMessage id="config.plugins.hooks" />
</h5>
{hooks.map((h) => (
<div key={`${h.name}`}>
<h6>{h.name}</h6>
<CollapseButton
text={intl.formatMessage({
id: "config.plugins.triggers_on",
})}
>
<ul>
{h.hooks?.map((hh) => (
<li key={hh}>
<code>{hh}</code>
</li>
))}
</ul>
</CollapseButton>
<small className="text-muted">{h.description}</small>
</div>
))}
</div>
<div />
</div>
);
}
}
function renderPlugins() {
const elements = (data?.plugins ?? []).map((plugin) => (
<div key={plugin.id}>
<h4>
{plugin.name} {plugin.version ? `(${plugin.version})` : undefined}{" "}
{renderLink(plugin.url ?? undefined)}
</h4>
{plugin.description ? (
<small className="text-muted">{plugin.description}</small>
) : undefined}
{renderPluginHooks(plugin.hooks ?? undefined)}
<hr />
</div>
));
return <div>{elements}</div>;
}
function renderPluginHooks(
hooks?: Pick<GQL.PluginHook, "name" | "description" | "hooks">[]
) {
if (!hooks || hooks.length === 0) {
return;
}
return (
<div className="mt-2">
<h5>
<FormattedMessage id="config.plugins.hooks" />
</h5>
{hooks.map((h) => (
<div key={`${h.name}`} className="mb-3">
<h6>{h.name}</h6>
<CollapseButton
text={intl.formatMessage({ id: "config.plugins.triggers_on" })}
>
<ul>
{h.hooks?.map((hh) => (
<li key={hh}>
<code>{hh}</code>
</li>
))}
</ul>
</CollapseButton>
<small className="text-muted">{h.description}</small>
</div>
))}
</div>
);
}
return renderPlugins();
}, [data?.plugins, intl]);
if (loading) return <LoadingIndicator />;
return (
<>
<h3>
<FormattedMessage id="config.categories.plugins" />
</h3>
<hr />
{renderPlugins()}
<Button onClick={() => onReloadPlugins()}>
<span className="fa-icon">
<Icon icon="sync-alt" />
</span>
<span>
<FormattedMessage id="actions.reload_plugins" />
</span>
</Button>
<SettingSection headingID="config.categories.plugins">
<Setting headingID="actions.reload_plugins">
<Button onClick={() => onReloadPlugins()}>
<span className="fa-icon">
<Icon icon="sync-alt" />
</span>
<span>
<FormattedMessage id="actions.reload_plugins" />
</span>
</Button>
</Setting>
{pluginElements}
</SettingSection>
</>
);
};