support config wizard

This commit is contained in:
SrGooglo 2024-02-03 00:29:32 +01:00
parent bdc0f6fbbd
commit 7c410f0a45
8 changed files with 318 additions and 109 deletions

View File

@ -22,12 +22,6 @@ export default async function apply(pkg_id, changes) {
console.log(`[${pkg_id}] apply() | Applying changes... >`, changes)
sendToRender(`pkg:update:status`, {
id: pkg_id,
status: "loading",
statusText: "Applying changes...",
})
if (Array.isArray(changes.patches)) {
if (!Array.isArray(pkg.applied_patches)) {
pkg.applied_patches = []
@ -70,6 +64,12 @@ export default async function apply(pkg_id, changes) {
pkg.applied_patches = pkg.applied_patches.filter((p) => {
return p !== patch.id
})
sendToRender(`pkg:update:status`, {
id: pkg_id,
status: "done",
statusText: `Patch [${patch.id}] removed!`,
})
}
for await (let patch of installPatches) {
@ -100,6 +100,12 @@ export default async function apply(pkg_id, changes) {
// add to applied patches
pkg.applied_patches.push(patch.id)
sendToRender(`pkg:update:status`, {
id: pkg_id,
status: "done",
statusText: `Patch [${patch.id}] applied!`,
})
}
}
@ -115,8 +121,6 @@ export default async function apply(pkg_id, changes) {
}
}
pkg.status = "installed"
await updateInstalledPackage(pkg)
sendToRender(`new:notification`, {
@ -126,7 +130,6 @@ export default async function apply(pkg_id, changes) {
sendToRender(`pkg:update:status`, {
...pkg,
status: "installed",
statusText: "Changes applied!",
})

View File

@ -98,6 +98,10 @@ export default async function install(manifest) {
})
}
if (Array.isArray(pkg.install_ask_configs)) {
sendToRender("pkg:install:ask", pkg)
}
sendToRender(`pkg:update:status`, {
id: pkg_id,
status: "installed",

View File

@ -7,6 +7,7 @@ import getRootCssVar from "utils/getRootCssVar"
import ManifestInfo from "components/ManifestInfo"
import PackageUpdateAvailable from "components/PackageUpdateAvailable"
import InstallConfigAsk from "components/InstallConfigAsk"
import AppLayout from "layout"
import AppModalDialog from "layout/components/ModalDialog"
@ -42,6 +43,14 @@ window.app = {
}
})
},
pkgInstallWizard: (manifest) => {
app.modal.open(InstallConfigAsk, {
manifest: manifest,
close: () => {
app.modal.close()
}
})
},
checkUpdates: () => {
ipc.exec("updater:check")
},
@ -84,6 +93,9 @@ class App extends React.Component {
"app:update_available": (event, data) => {
this.onUpdateAvailable(data)
},
"pkg:install:ask": (event, data) => {
app.pkgInstallWizard(data)
},
"pkg:update_available": (event, data) => {
app.pkgUpdateAvailable(data)
},

View File

@ -0,0 +1,121 @@
import React from "react"
import { Icons, Icon } from "components/Icons"
import * as antd from "antd"
import PKGConfigItem from "../PackageConfigItem"
import "./index.less"
const InstallConfigAsk = (props) => {
const { manifest } = props
const [values, setValues] = React.useState({
...manifest.storaged_configs,
...Object.keys(manifest.configs).reduce((prev, key) => {
prev[key] = manifest.configs[key].default
return prev
}, {})
})
const [currentStep, setCurrentStep] = React.useState(0)
function handleChanges(key, value) {
setValues((prev) => {
return {
...prev,
[key]: value
}
})
}
function handleNextStep() {
setCurrentStep(currentStep + 1)
}
function handleFinish() {
console.log(`Finish config wizard, with values >`, values)
if (typeof props.onFinish === "function") {
props.onFinish(values)
}
if (typeof props.close === "function") {
props.close()
}
if (typeof props.onClose === "function") {
props.onClose()
}
ipc.exec("pkg:apply", manifest.id, {
configs: values
})
app.location.push("/")
}
const stepsItems = React.useMemo(() => {
const steps = manifest.install_ask_configs.map((key, index) => {
return {
key: index,
title: manifest.configs[key].label
}
})
return [...steps, {
key: manifest.install_ask_configs.length,
title: "Finish"
}]
}, [])
const stepsContent = React.useMemo(() => {
const steps = manifest.install_ask_configs.map((key, index) => {
const config = manifest.configs[key]
return <PKGConfigItem
key={index}
config={config}
storagedValue={manifest.storaged_configs[key]}
onChange={handleChanges}
/>
})
return steps
}, [])
return <div className="install_config_ask">
<antd.Steps
size="small"
direction="horizontal"
items={stepsItems}
current={currentStep}
responsive={false}
onChange={(e) => {
setCurrentStep(currentStep)
}}
/>
<div className="install_config_ask-content">
{stepsContent[currentStep]}
</div>
<div className="install_config_ask-actions">
<antd.Button
type="primary"
onClick={() => {
if (currentStep === stepsItems.length - 2) {
handleFinish()
} else {
handleNextStep()
}
}}
>
{currentStep === stepsItems.length - 2 ? "Finish" : "Next"}
</antd.Button>
</div>
</div>
}
export default InstallConfigAsk

View File

@ -0,0 +1,32 @@
.install_config_ask {
display: flex;
flex-direction: column;
gap: 20px;
h1 {
display: inline-flex;
flex-direction: row;
gap: 6px;
font-size: 1rem;
svg {
font-size: 1.5rem;
}
}
.ant-steps {
padding-right: 30px;
}
.install_config_ask-content {
display: flex;
flex-direction: column;
gap: 10px;
padding: 0 15px;
}
}

View File

@ -0,0 +1,110 @@
import React from "react"
import * as antd from "antd"
import { Icon } from "components/Icons"
const PKGConfigsComponents = {
switch: antd.Switch,
button: antd.Button,
input: antd.Input,
slider: antd.Slider,
}
const PKGConfigsComponentByTypes = {
string: "input",
action: "button",
bool: "switch",
number: "slider",
}
const PKGConfigItem = (props) => {
const { config, storagedValue } = props
const key = config.id
const [localValue, setLocalValue] = React.useState(storagedValue ?? config.default)
const ComponentType = config.ui_component ?? PKGConfigsComponentByTypes[config.type] ?? "input"
const ConfigComponent = PKGConfigsComponents[ComponentType]
function handleOnChange(value) {
if (typeof value === "string" && config.string_trim === true) {
value = value.trim()
}
setLocalValue(value)
return props.onChange(key, value)
}
if (ConfigComponent == null) {
return null
}
const ComponentsProps = {
...config.ui_component_props,
defaultValue: storagedValue ?? config.default,
value: localValue
}
switch (ComponentType) {
case "input": {
ComponentsProps.onChange = (e) => {
handleOnChange(e.target.value)
}
break
}
case "slider": {
ComponentsProps.onChange = (value) => {
handleOnChange(value)
}
break
}
case "switch": {
ComponentsProps.onChange = (checked) => {
handleOnChange(checked)
}
break
}
default: {
ComponentsProps.onChange = (value) => {
handleOnChange(value)
}
break;
}
}
return <div
id={key}
className="package_configs-option"
>
<div className="package_configs-option-header">
<span className="package_configs-option-label">
{
config.icon && <Icon
icon={config.icon}
/>
}
{
config.label ?? key
}
</span>
{
config.description && <p className="package_configs-option-description">
{key}
</p>
}
</div>
<div className="package_configs-option-content">
{
React.createElement(ConfigComponent, ComponentsProps)
}
</div>
</div>
}
export default PKGConfigItem

View File

@ -170,10 +170,12 @@ const PackageItem = (props) => {
{
isInstalled && !manifest.executable && <antd.Dropdown
menu={MenuProps}
disabled={isLoading}
>
<antd.Button
icon={<MdOutlineMoreVert />}
type="primary"
disabled={isLoading}
/>
</antd.Dropdown>
}

View File

@ -3,22 +3,10 @@ import * as antd from "antd"
import { Icons, Icon } from "components/Icons"
import { useParams } from "react-router-dom"
import PKGConfigItem from "components/PackageConfigItem"
import "./index.less"
const PKGConfigsComponents = {
switch: antd.Switch,
button: antd.Button,
input: antd.Input,
slider: antd.Slider,
}
const PKGConfigsComponentByTypes = {
string: "input",
action: "button",
bool: "switch",
number: "slider",
}
const PKGConfigs = (props) => {
const { defaultConfigs = {}, configs = {} } = props
@ -30,93 +18,15 @@ const PKGConfigs = (props) => {
return Object.keys(defaultConfigs).map((key, index) => {
const config = defaultConfigs[key]
const storagedValue = configs[key]
const [localValue, setLocalValue] = React.useState(storagedValue ?? config.default)
config.id = key
const ComponentType = config.ui_component ?? PKGConfigsComponentByTypes[config.type] ?? "input"
const ConfigComponent = PKGConfigsComponents[ComponentType]
function handleOnChange(value) {
if (typeof value === "string" && config.string_trim === true) {
value = value.trim()
}
setLocalValue(value)
return props.onChange(key, value)
}
if (ConfigComponent == null) {
return null
}
const ComponentsProps = {
...config.ui_component_props,
defaultValue: storagedValue ?? config.default,
value: localValue
}
switch (ComponentType) {
case "input": {
ComponentsProps.onChange = (e) => {
handleOnChange(e.target.value)
}
break
}
case "slider": {
ComponentsProps.onChange = (value) => {
handleOnChange(value)
}
break
}
case "switch": {
ComponentsProps.onChange = (checked) => {
handleOnChange(checked)
}
break
}
default: {
ComponentsProps.onChange = (value) => {
handleOnChange(value)
}
break;
}
}
return <div
return <PKGConfigItem
key={index}
id={key}
className="package_configs-option"
>
<div className="package_configs-option-header">
<span className="package_configs-option-label">
{
config.icon && <Icon
icon={config.icon}
/>
}
{
config.label ?? key
}
</span>
{
config.description && <p className="package_configs-option-description">
{key}
</p>
}
</div>
<div className="package_configs-option-content">
{
React.createElement(ConfigComponent, ComponentsProps)
}
</div>
</div>
storagedValue={configs[key]}
config={config}
onChange={props.onChange}
/>
})
}
@ -315,17 +225,32 @@ const PackageOptions = (props) => {
onClick={handleReinstall}
icon={<Icons.MdReplay />}
type="default"
size="small"
>
Reinstall
</antd.Button>
<antd.Button
{/* <antd.Button
disabled
icon={<Icons.MdCheck />}
type="default"
size="small"
>
Verify
</antd.Button>
</antd.Button> */}
{
manifest.install_ask_configs && <antd.Button
onClick={() => {
app.pkgInstallWizard(manifest)
}}
icon={<Icons.MdSettings />}
type="default"
size="small"
>
Wizard
</antd.Button>
}
<antd.Button
type="primary"