mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
added tabs to settings
This commit is contained in:
parent
751cb23683
commit
dd7524385e
103
packages/app/constants/settings/account.jsx
Normal file
103
packages/app/constants/settings/account.jsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { User } from "models"
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
"id": "username",
|
||||||
|
"group": "account.basicInfo",
|
||||||
|
"type": "Button",
|
||||||
|
"icon": "AtSign",
|
||||||
|
"title": "Username",
|
||||||
|
"description": "Your username is the name you use to log in to your account.",
|
||||||
|
"props": {
|
||||||
|
"disabled": true,
|
||||||
|
"children": "Change username",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fullName",
|
||||||
|
"group": "account.basicInfo",
|
||||||
|
"type": "Input",
|
||||||
|
"icon": "Edit3",
|
||||||
|
"title": "Name",
|
||||||
|
"description": "Change your public name",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "Enter your name. e.g. John Doe",
|
||||||
|
},
|
||||||
|
"defaultValue": async () => {
|
||||||
|
const userData = await User.data()
|
||||||
|
return userData.fullName
|
||||||
|
},
|
||||||
|
"onUpdate": async (value) => {
|
||||||
|
const selfId = await User.selfUserId()
|
||||||
|
|
||||||
|
const result = window.app.request.post.updateUser({
|
||||||
|
_id: selfId,
|
||||||
|
update: {
|
||||||
|
fullName: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extraActions": [
|
||||||
|
{
|
||||||
|
"id": "unset",
|
||||||
|
"icon": "Delete",
|
||||||
|
"title": "Unset",
|
||||||
|
"onClick": async () => {
|
||||||
|
window.app.request.post.unsetPublicName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"debounced": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "email",
|
||||||
|
"group": "account.basicInfo",
|
||||||
|
"type": "Input",
|
||||||
|
"icon": "Mail",
|
||||||
|
"title": "Email",
|
||||||
|
"description": "Change your email address",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "Enter your email address",
|
||||||
|
},
|
||||||
|
"defaultValue": async () => {
|
||||||
|
const userData = await User.data()
|
||||||
|
return userData.email
|
||||||
|
},
|
||||||
|
"onUpdate": async (value) => {
|
||||||
|
const selfId = await User.selfUserId()
|
||||||
|
|
||||||
|
const result = window.app.request.post.updateUser({
|
||||||
|
_id: selfId,
|
||||||
|
update: {
|
||||||
|
email: value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debounced": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Avatar",
|
||||||
|
"group": "account.basicInfo",
|
||||||
|
"type": "ImageUpload",
|
||||||
|
"icon": "Image",
|
||||||
|
"title": "Avatar",
|
||||||
|
"description": "Change your avatar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "primaryBadge",
|
||||||
|
"group": "account.basicInfo",
|
||||||
|
"type": "Select",
|
||||||
|
"icon": "Tag",
|
||||||
|
"title": "Primary badge",
|
||||||
|
"description": "Change your primary badge",
|
||||||
|
},
|
||||||
|
]
|
@ -5,6 +5,7 @@ import { Select } from "antd"
|
|||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
"id": "language",
|
"id": "language",
|
||||||
|
"storaged": true,
|
||||||
"group": "general",
|
"group": "general",
|
||||||
"type": "Select",
|
"type": "Select",
|
||||||
"icon": "MdTranslate",
|
"icon": "MdTranslate",
|
||||||
@ -19,6 +20,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "forceMobileMode",
|
"id": "forceMobileMode",
|
||||||
|
"storaged": true,
|
||||||
"group": "general",
|
"group": "general",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "MdSmartphone",
|
"icon": "MdSmartphone",
|
||||||
@ -28,6 +30,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "haptic_feedback",
|
"id": "haptic_feedback",
|
||||||
|
"storaged": true,
|
||||||
"group": "general",
|
"group": "general",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "MdVibration",
|
"icon": "MdVibration",
|
||||||
@ -36,6 +39,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "selection_longPress_timeout",
|
"id": "selection_longPress_timeout",
|
||||||
|
"storaged": true,
|
||||||
"group": "general",
|
"group": "general",
|
||||||
"type": "Slider",
|
"type": "Slider",
|
||||||
"icon": "MdTimer",
|
"icon": "MdTimer",
|
||||||
@ -56,6 +60,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "notifications_sound",
|
"id": "notifications_sound",
|
||||||
|
"storaged": true,
|
||||||
"group": "notifications",
|
"group": "notifications",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "MdVolumeUp",
|
"icon": "MdVolumeUp",
|
||||||
@ -64,6 +69,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "notifications_vibrate",
|
"id": "notifications_vibrate",
|
||||||
|
"storaged": true,
|
||||||
"group": "notifications",
|
"group": "notifications",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "MdVibration",
|
"icon": "MdVibration",
|
||||||
@ -73,6 +79,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "notifications_sound_volume",
|
"id": "notifications_sound_volume",
|
||||||
|
"storaged": true,
|
||||||
"group": "notifications",
|
"group": "notifications",
|
||||||
"type": "Slider",
|
"type": "Slider",
|
||||||
"icon": "MdVolumeUp",
|
"icon": "MdVolumeUp",
|
||||||
@ -87,6 +94,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "edit_sidebar",
|
"id": "edit_sidebar",
|
||||||
|
"storaged": true,
|
||||||
"group": "sidebar",
|
"group": "sidebar",
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"icon": "Edit",
|
"icon": "Edit",
|
||||||
@ -96,6 +104,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "collapseOnLooseFocus",
|
"id": "collapseOnLooseFocus",
|
||||||
|
"storaged": true,
|
||||||
"group": "sidebar",
|
"group": "sidebar",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "Columns",
|
"icon": "Columns",
|
||||||
@ -105,6 +114,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "autoCollapseDelay",
|
"id": "autoCollapseDelay",
|
||||||
|
"storaged": true,
|
||||||
"group": "sidebar",
|
"group": "sidebar",
|
||||||
"type": "Slider",
|
"type": "Slider",
|
||||||
"icon": "Wh",
|
"icon": "Wh",
|
||||||
@ -128,6 +138,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "reduceAnimations",
|
"id": "reduceAnimations",
|
||||||
|
"storaged": true,
|
||||||
"group": "aspect",
|
"group": "aspect",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "MdOutlineAnimation",
|
"icon": "MdOutlineAnimation",
|
||||||
@ -136,6 +147,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "darkMode",
|
"id": "darkMode",
|
||||||
|
"storaged": true,
|
||||||
"group": "aspect",
|
"group": "aspect",
|
||||||
"type": "Switch",
|
"type": "Switch",
|
||||||
"icon": "Moon",
|
"icon": "Moon",
|
||||||
@ -145,11 +157,13 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "primaryColor",
|
"id": "primaryColor",
|
||||||
|
"storaged": true,
|
||||||
"group": "aspect",
|
"group": "aspect",
|
||||||
"type": "SliderColorPicker",
|
"type": "SliderColorPicker",
|
||||||
"title": "Primary color",
|
"title": "Primary color",
|
||||||
"description": "Change primary color of the application.",
|
"description": "Change primary color of the application.",
|
||||||
"emitEvent": "modifyTheme",
|
"emitEvent": "modifyTheme",
|
||||||
|
"reloadValueOnUpdateEvent": "resetTheme",
|
||||||
"emissionValueUpdate": (value) => {
|
"emissionValueUpdate": (value) => {
|
||||||
return {
|
return {
|
||||||
primaryColor: value
|
primaryColor: value
|
||||||
@ -158,6 +172,7 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "resetTheme",
|
"id": "resetTheme",
|
||||||
|
"storaged": true,
|
||||||
"group": "aspect",
|
"group": "aspect",
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"title": "Reset theme",
|
"title": "Reset theme",
|
||||||
@ -165,6 +180,6 @@ export default [
|
|||||||
"children": "Default Theme"
|
"children": "Default Theme"
|
||||||
},
|
},
|
||||||
"emitEvent": "resetTheme",
|
"emitEvent": "resetTheme",
|
||||||
"noStorage": true
|
"noUpdate": true,
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -14,5 +14,9 @@
|
|||||||
"aspect": {
|
"aspect": {
|
||||||
"title": "Aspect",
|
"title": "Aspect",
|
||||||
"icon": "Eye"
|
"icon": "Eye"
|
||||||
|
},
|
||||||
|
"account.basicInfo": {
|
||||||
|
"title": "Basic Information",
|
||||||
|
"icon": "Info"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,10 +2,14 @@ import React from "react"
|
|||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { SliderPicker } from "react-color"
|
import { SliderPicker } from "react-color"
|
||||||
import { Translation } from "react-i18next"
|
import { Translation } from "react-i18next"
|
||||||
|
import classnames from "classnames"
|
||||||
|
|
||||||
import config from "config"
|
import config from "config"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
import settingList from "schemas/settings"
|
|
||||||
|
import AppSettings from "schemas/settings/app"
|
||||||
|
import AccountSettings from "schemas/settings/account"
|
||||||
|
|
||||||
import groupsDecorator from "schemas/settingsGroupsDecorator.json"
|
import groupsDecorator from "schemas/settingsGroupsDecorator.json"
|
||||||
|
|
||||||
import { AboutApp } from ".."
|
import { AboutApp } from ".."
|
||||||
@ -23,25 +27,47 @@ const ItemTypes = {
|
|||||||
SliderColorPicker: SliderPicker,
|
SliderColorPicker: SliderPicker,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SettingsMenu extends React.Component {
|
const SettingItem = (props) => {
|
||||||
state = {
|
let { item } = props
|
||||||
settings: window.app.settings.get() ?? {},
|
const [loading, setLoading] = React.useState(true)
|
||||||
|
const [value, setValue] = React.useState(item.defaultValue ?? false)
|
||||||
|
const [delayedValue, setDelayedValue] = React.useState(null)
|
||||||
|
|
||||||
|
if (!item.type) {
|
||||||
|
console.error(`Item [${item.id}] has no an type!`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (typeof ItemTypes[item.type] === "undefined") {
|
||||||
|
console.error(`Item [${item.id}] has an invalid type: ${item.type}`)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdate = (item, update) => {
|
if (typeof item.props === "undefined") {
|
||||||
if (typeof item.id === "undefined") {
|
item.props = {}
|
||||||
console.error("[Settings] Cannot handle update, item has no id")
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentValue = window.app.settings.get(item.id)
|
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 (typeof update === "undefined") {
|
if (!result) {
|
||||||
update = !currentValue
|
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") {
|
if (typeof item.emitEvent === "string") {
|
||||||
let emissionPayload = update
|
let emissionPayload = updateValue
|
||||||
|
|
||||||
if (typeof item.emissionValueUpdate === "function") {
|
if (typeof item.emissionValueUpdate === "function") {
|
||||||
emissionPayload = item.emissionValueUpdate(emissionPayload)
|
emissionPayload = item.emissionValueUpdate(emissionPayload)
|
||||||
@ -50,110 +76,200 @@ export default class SettingsMenu extends React.Component {
|
|||||||
window.app.eventBus.emit(item.emitEvent, emissionPayload)
|
window.app.eventBus.emit(item.emitEvent, emissionPayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item.noStorage) {
|
if (item.noUpdate) {
|
||||||
window.app.settings.set(item.id, update)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ settings: { ...this.state.settings, [item.id]: update } })
|
if (item.storaged) {
|
||||||
|
await window.app.settings.set(item.id, updateValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.debounced) {
|
||||||
|
setDelayedValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(updateValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem = (item) => {
|
const onUpdateItem = async (updateValue) => {
|
||||||
if (!item.type) {
|
setValue(updateValue)
|
||||||
console.error(`Item [${item.id}] has no an type!`)
|
|
||||||
return null
|
if (!item.debounced) {
|
||||||
|
await dispatchUpdate(updateValue)
|
||||||
|
} else {
|
||||||
|
setDelayedValue(updateValue)
|
||||||
}
|
}
|
||||||
if (typeof ItemTypes[item.type] === "undefined") {
|
}
|
||||||
console.error(`Item [${item.id}] has an invalid type: ${item.type}`)
|
|
||||||
return null
|
const settingInitialization = async () => {
|
||||||
|
if (item.storaged) {
|
||||||
|
const storagedValue = window.app.settings.get(item.id)
|
||||||
|
setValue(storagedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof item.props === "undefined") {
|
if (typeof item.defaultValue === "function") {
|
||||||
item.props = {}
|
setValue(await item.defaultValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix handlers
|
|
||||||
switch (item.type.toLowerCase()) {
|
|
||||||
case "slidercolorpicker": {
|
|
||||||
item.props.onChange = (color) => {
|
|
||||||
item.props.color = color.hex
|
|
||||||
}
|
|
||||||
item.props.onChangeComplete = (color) => {
|
|
||||||
this.handleUpdate(item, color.hex)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "switch": {
|
|
||||||
item.props.checked = this.state.settings[item.id]
|
|
||||||
item.props.onClick = (event) => this.handleUpdate(item, event)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "select": {
|
|
||||||
item.props.onChange = (value) => this.handleUpdate(item, value)
|
|
||||||
item.props.defaultValue = this.state.settings[item.id]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "slider":{
|
|
||||||
item.props.defaultValue = this.state.settings[item.id]
|
|
||||||
item.props.onAfterChange = (value) => this.handleUpdate(item, value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (!item.props.children) {
|
|
||||||
item.props.children = item.title ?? item.id
|
|
||||||
}
|
|
||||||
item.props.value = this.state.settings[item.id]
|
|
||||||
item.props.onClick = (event) => this.handleUpdate(item, event)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support async children
|
|
||||||
// if (typeof item.children === "function") {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (typeof item.dependsOn === "object") {
|
if (typeof item.dependsOn === "object") {
|
||||||
const dependsOptionsKeys = Object.keys(item.dependsOn)
|
const dependsOptionsKeys = Object.keys(item.dependsOn)
|
||||||
|
|
||||||
item.props.disabled = !Boolean(dependsOptionsKeys.every((key) => {
|
item.props.disabled = !Boolean(dependsOptionsKeys.every((key) => {
|
||||||
|
const storagedValue = window.app.settings.get(key)
|
||||||
|
|
||||||
if (typeof item.dependsOn[key] === "function") {
|
if (typeof item.dependsOn[key] === "function") {
|
||||||
return item.dependsOn[key](this.state.settings[key])
|
return item.dependsOn[key](storagedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.state.settings[key] === item.dependsOn[key]
|
return storagedValue === item.dependsOn[key]
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (typeof item.listenUpdateValue === "string") {
|
||||||
<div key={item.id} className="settingItem">
|
window.app.eventBus.on(`setting.update.${item.listenUpdateValue}`, (value) => setValue(value))
|
||||||
<div className="header">
|
}
|
||||||
<div>
|
|
||||||
<h4>
|
if (item.reloadValueOnUpdateEvent) {
|
||||||
{Icons[item.icon] ? React.createElement(Icons[item.icon]) : null}
|
window.app.eventBus.on(item.reloadValueOnUpdateEvent, () => {
|
||||||
<Translation>{
|
console.log(`Reloading value for item [${item.id}]`)
|
||||||
t => t(item.title ?? item.id)
|
settingInitialization()
|
||||||
}</Translation>
|
})
|
||||||
</h4>
|
}
|
||||||
<p> <Translation>{
|
|
||||||
t => t(item.description)
|
setLoading(false)
|
||||||
}</Translation></p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{item.experimental && <antd.Tag> Experimental </antd.Tag>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="component">
|
|
||||||
{React.createElement(ItemTypes[item.type], item.props)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGroup = (key, group) => {
|
React.useEffect(() => {
|
||||||
|
settingInitialization()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
switch (item.type.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 "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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div key={item.id} className="settingItem">
|
||||||
|
<div className="header">
|
||||||
|
<div className="title">
|
||||||
|
<div>
|
||||||
|
<h4>
|
||||||
|
{Icons[item.icon] ? React.createElement(Icons[item.icon]) : null}
|
||||||
|
<Translation>{
|
||||||
|
t => t(item.title ?? item.id)
|
||||||
|
}</Translation>
|
||||||
|
</h4>
|
||||||
|
<p> <Translation>{
|
||||||
|
t => t(item.description)
|
||||||
|
}</Translation></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{item.experimental && <antd.Tag> Experimental </antd.Tag>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{item.extraActions &&
|
||||||
|
<div className="extraActions">
|
||||||
|
{item.extraActions.map((action, index) => {
|
||||||
|
return <div>
|
||||||
|
<antd.Button
|
||||||
|
key={action.id}
|
||||||
|
id={action.id}
|
||||||
|
onClick={action.onClick}
|
||||||
|
icon={action.icon && createIconRender(action.icon)}
|
||||||
|
type={action.type ?? "round"}
|
||||||
|
>
|
||||||
|
{action.title}
|
||||||
|
</antd.Button>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="component">
|
||||||
|
<div>
|
||||||
|
{loading ? <div> Loading... </div> : React.createElement(ItemTypes[item.type], item.props)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{delayedValue && <div>
|
||||||
|
<antd.Button
|
||||||
|
type="round"
|
||||||
|
icon={<Icons.Save />}
|
||||||
|
onClick={async () => await dispatchUpdate(value)}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</antd.Button>
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SettingsMenu extends React.PureComponent {
|
||||||
|
state = {
|
||||||
|
transitionActive: false,
|
||||||
|
activeKey: "app"
|
||||||
|
}
|
||||||
|
|
||||||
|
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 fromDecoratorIcon = groupsDecorator[key]?.icon
|
||||||
const fromDecoratorTitle = groupsDecorator[key]?.title
|
const fromDecoratorTitle = groupsDecorator[key]?.title
|
||||||
|
|
||||||
return (
|
return <div className={classnames("fade-opacity-active", { "fade-opacity-leave": this.state.transitionActive })}>
|
||||||
<div key={key} className="group">
|
<div key={key} className="group">
|
||||||
<h1>
|
<h1>
|
||||||
{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null}
|
{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null}
|
||||||
@ -162,13 +278,13 @@ export default class SettingsMenu extends React.Component {
|
|||||||
}</Translation>
|
}</Translation>
|
||||||
</h1>
|
</h1>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
{group.map((item) => this.renderItem(item))}
|
{group.map((item) => <SettingItem item={item} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
generateSettings = (data) => {
|
generateSettingsGroups = (data) => {
|
||||||
let groups = {}
|
let groups = {}
|
||||||
|
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
@ -180,7 +296,7 @@ export default class SettingsMenu extends React.Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return Object.keys(groups).map((groupKey) => {
|
return Object.keys(groups).map((groupKey) => {
|
||||||
return this.renderGroup(groupKey, groups[groupKey])
|
return this.renderSettings(groupKey, groups[groupKey])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,30 +305,78 @@ export default class SettingsMenu extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings">
|
<div className="settings">
|
||||||
{this.generateSettings(settingList)}
|
<antd.Tabs
|
||||||
<div className="footer">
|
activeKey={this.state.activeKey}
|
||||||
<div>
|
centered
|
||||||
<div>{config.app?.siteName}</div>
|
destroyInactiveTabPane
|
||||||
<div>
|
onTabClick={this.handlePageTransition}
|
||||||
<antd.Tag>
|
>
|
||||||
<Icons.Tag />v{window.app.version}
|
<antd.Tabs.TabPane
|
||||||
</antd.Tag>
|
key="app"
|
||||||
|
tab={
|
||||||
|
<span>
|
||||||
|
<Icons.Command />
|
||||||
|
App
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{this.generateSettingsGroups(AppSettings)}
|
||||||
|
<div className="footer">
|
||||||
|
<div>
|
||||||
|
<div>{config.app?.siteName}</div>
|
||||||
|
<div>
|
||||||
|
<antd.Tag>
|
||||||
|
<Icons.Tag />v{window.app.version}
|
||||||
|
</antd.Tag>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<antd.Tag color={isDevMode ? "magenta" : "green"}>
|
||||||
|
{isDevMode ? <Icons.Triangle /> : <Icons.Box />}
|
||||||
|
{isDevMode ? "development" : "stable"}
|
||||||
|
</antd.Tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<antd.Button type="link" onClick={() => AboutApp.openModal()}>
|
||||||
|
<Translation>
|
||||||
|
{t => t("about")}
|
||||||
|
</Translation>
|
||||||
|
</antd.Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</antd.Tabs.TabPane>
|
||||||
<antd.Tag color={isDevMode ? "magenta" : "green"}>
|
<antd.Tabs.TabPane
|
||||||
{isDevMode ? <Icons.Triangle /> : <Icons.Box />}
|
key="account"
|
||||||
{isDevMode ? "development" : "stable"}
|
tab={
|
||||||
</antd.Tag>
|
<span>
|
||||||
</div>
|
<Icons.User />
|
||||||
</div>
|
Account
|
||||||
<div>
|
</span>
|
||||||
<antd.Button type="link" onClick={() => AboutApp.openModal()}>
|
}
|
||||||
<Translation>
|
>
|
||||||
{t => t("about")}
|
{this.generateSettingsGroups(AccountSettings)}
|
||||||
</Translation>
|
</antd.Tabs.TabPane>
|
||||||
</antd.Button>
|
<antd.Tabs.TabPane
|
||||||
</div>
|
key="security"
|
||||||
</div>
|
tab={
|
||||||
|
<span>
|
||||||
|
<Icons.Shield />
|
||||||
|
Security
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
</antd.Tabs.TabPane>
|
||||||
|
<antd.Tabs.TabPane
|
||||||
|
key="privacy"
|
||||||
|
tab={
|
||||||
|
<span>
|
||||||
|
<Icons.Eye />
|
||||||
|
Privacy
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
</antd.Tabs.TabPane>
|
||||||
|
</antd.Tabs>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -35,38 +35,62 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display : flex;
|
display : inline-flex;
|
||||||
align-items: center;
|
flex-direction: row;
|
||||||
color : var(--background-color-contrast);
|
align-items : center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
h1,
|
.title {
|
||||||
h2,
|
display : flex;
|
||||||
h3,
|
align-items: center;
|
||||||
h4,
|
color : var(--background-color-contrast);
|
||||||
h5,
|
|
||||||
h6 {
|
h1,
|
||||||
margin: 0;
|
h2,
|
||||||
color : var(--background-color-contrast);
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
margin: 0;
|
||||||
|
color : var(--background-color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 11px;
|
||||||
|
color : var(--background-color-contrast);
|
||||||
|
margin : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.extraActions {
|
||||||
font-size: 11px;
|
display: inline-flex;
|
||||||
color : var(--background-color-contrast);
|
align-items: center;
|
||||||
margin : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
>div {
|
> div {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.component {
|
.component {
|
||||||
|
display : flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
--ignore-dragger: true;
|
--ignore-dragger: true;
|
||||||
padding : 0 20px;
|
padding : 0 20px;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: var(--background-color-contrast);
|
color: var(--background-color-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,4 +130,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-tabs-nav-list {
|
||||||
|
width : 100%;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user