import React from "react" import * as antd from "antd" import { SliderPicker } from "react-color" import { Translation } from "react-i18next" import classnames from "classnames" import config from "config" import { Icons, createIconRender } from "components/Icons" import SettingsList from "schemas/settings" import groupsDecorator from "schemas/settingsGroupsDecorator.json" import { AboutApp } from ".." import "./index.less" const ItemTypes = { Button: antd.Button, Switch: antd.Switch, Slider: antd.Slider, Checkbox: antd.Checkbox, Input: antd.Input, TextArea: antd.Input.TextArea, InputNumber: antd.InputNumber, Select: antd.Select, SliderColorPicker: SliderPicker, } const SettingItem = (props) => { let { item } = props const [loading, setLoading] = React.useState(true) const [value, setValue] = React.useState(item.defaultValue ?? false) const [delayedValue, setDelayedValue] = React.useState(null) let SettingComponent = item.component if (!SettingComponent) { console.error(`Item [${item.id}] has no an component!`) return null } if (typeof item.props === "undefined") { item.props = {} } const dispatchUpdate = async (updateValue) => { if (typeof item.onUpdate === "function") { const result = await item.onUpdate(updateValue).catch((error) => { console.error(error) antd.message.error(error.message) return false }) if (!result) { return false } updateValue = result } else { const storagedValue = await window.app.settings.get(item.id) if (typeof updateValue === "undefined") { updateValue = !storagedValue } } if (typeof item.emitEvent === "string") { let emissionPayload = updateValue if (typeof item.emissionValueUpdate === "function") { emissionPayload = item.emissionValueUpdate(emissionPayload) } window.app.eventBus.emit(item.emitEvent, emissionPayload) } if (item.noUpdate) { return false } if (item.storaged) { await window.app.settings.set(item.id, updateValue) } if (item.debounced) { setDelayedValue(null) } setValue(updateValue) } const onUpdateItem = async (updateValue) => { setValue(updateValue) if (!item.debounced) { await dispatchUpdate(updateValue) } else { setDelayedValue(updateValue) } } const settingInitialization = async () => { if (item.storaged) { const storagedValue = window.app.settings.get(item.id) setValue(storagedValue) } if (typeof item.defaultValue === "function") { setValue(await item.defaultValue()) } if (typeof item.dependsOn === "object") { const dependsOptionsKeys = Object.keys(item.dependsOn) item.props.disabled = !Boolean(dependsOptionsKeys.every((key) => { const storagedValue = window.app.settings.get(key) if (typeof item.dependsOn[key] === "function") { return item.dependsOn[key](storagedValue) } return storagedValue === item.dependsOn[key] })) } if (typeof item.listenUpdateValue === "string") { window.app.eventBus.on(`setting.update.${item.listenUpdateValue}`, (value) => setValue(value)) } if (item.reloadValueOnUpdateEvent) { window.app.eventBus.on(item.reloadValueOnUpdateEvent, () => { console.log(`Reloading value for item [${item.id}]`) settingInitialization() }) } setLoading(false) } React.useEffect(() => { settingInitialization() }, []) if (typeof SettingComponent === "string") { if (typeof ItemTypes[SettingComponent] === "undefined") { console.error(`Item [${item.id}] has an invalid component: ${item.component}`) return null } // fix props switch (SettingComponent.toLowerCase()) { case "slidercolorpicker": { item.props.onChange = (color) => { item.props.color = color.hex } item.props.onChangeComplete = (color) => { onUpdateItem(color.hex) } item.props.color = value break } case "textarea": { item.props.defaultValue = value item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) item.props.onChange = (event) => onUpdateItem(event.target.value) break } case "input": { item.props.defaultValue = value item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) item.props.onChange = (event) => onUpdateItem(event.target.value) break } case "switch": { item.props.checked = value item.props.onClick = (event) => onUpdateItem(event) break } case "select": { item.props.onChange = (value) => onUpdateItem(value) item.props.defaultValue = value break } case "slider": { item.props.defaultValue = value item.props.onAfterChange = (value) => onUpdateItem(value) break } default: { if (!item.props.children) { item.props.children = item.title ?? item.id } item.props.value = item.defaultValue item.props.onClick = (event) => onUpdateItem(event) break } } // override with default item component SettingComponent = ItemTypes[SettingComponent] } return

{Icons[item.icon] ? React.createElement(Icons[item.icon]) : null} { t => t(item.title ?? item.id) }

{ t => t(item.description) }

{item.experimental && Experimental }
{item.extraActions &&
{item.extraActions.map((action, index) => { return
{action.title}
})}
}
{loading ?
Loading...
: React.createElement(SettingComponent, item.props)}
{delayedValue &&
} onClick={async () => await dispatchUpdate(value)} > Save
}
} export default class SettingsMenu extends React.PureComponent { state = { transitionActive: false, activeKey: "app" } componentDidMount() { if (typeof this.props.close === "function") { // register escape key to close settings menu window.addEventListener("keydown", this.handleKeyDown) } } componentWillUnmount() { if (typeof this.props.close === "function") { window.removeEventListener("keydown", this.handleKeyDown) } } handleKeyDown = (event) => { if (event.key === "Escape") { this.props.close() } } handlePageTransition = (key) => { this.setState({ transitionActive: true, }) setTimeout(() => { this.setState({ activeKey: key }) setTimeout(() => { this.setState({ transitionActive: false, }) }, 100) }, 100) } renderSettings = (key, group) => { const fromDecoratorIcon = groupsDecorator[key]?.icon const fromDecoratorTitle = groupsDecorator[key]?.title return

{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null} { t => t(fromDecoratorTitle ?? key) }

{group.map((item) => )}
} generateSettingsGroups = (data) => { let groups = {} data.forEach((item) => { if (!groups[item.group]) { groups[item.group] = [] } groups[item.group].push(item) }) return Object.keys(groups).map((groupKey) => { return this.renderSettings(groupKey, groups[groupKey]) }) } generateSettingsTabs = () => { return Object.keys(SettingsList).map((key) => { return {createIconRender(SettingsList[key].icon)} {SettingsList[key].label} } > {this.generateSettingsGroups(SettingsList[key].settings)} }) } render() { const isDevMode = window.__evite?.env?.NODE_ENV !== "production" return (
{this.generateSettingsTabs()}
{config.app?.siteName}
v{window.app.version}
{isDevMode ? : } {isDevMode ? "development" : "stable"}
AboutApp.openModal()}> {t => t("about")}
) } }