mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
improve settings initialization
This commit is contained in:
parent
6760c207d6
commit
d74116d253
@ -2,162 +2,166 @@ import React from "react"
|
||||
import config from "config"
|
||||
import { Select } from "antd"
|
||||
|
||||
export default [
|
||||
{
|
||||
"id": "language",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Select",
|
||||
"icon": "MdTranslate",
|
||||
"title": "Language",
|
||||
"description": "Choose a language for the application",
|
||||
"props": {
|
||||
children: config.i18n.languages.map((language) => {
|
||||
return <Select.Option value={language.locale}>{language.name}</Select.Option>
|
||||
})
|
||||
export default {
|
||||
icon: "Command",
|
||||
label: "App",
|
||||
settings: [
|
||||
{
|
||||
"id": "language",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Select",
|
||||
"icon": "MdTranslate",
|
||||
"title": "Language",
|
||||
"description": "Choose a language for the application",
|
||||
"props": {
|
||||
children: config.i18n.languages.map((language) => {
|
||||
return <Select.Option value={language.locale}>{language.name}</Select.Option>
|
||||
})
|
||||
},
|
||||
"emitEvent": "changeLanguage"
|
||||
},
|
||||
"emitEvent": "changeLanguage"
|
||||
},
|
||||
{
|
||||
"id": "haptic_feedback",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Switch",
|
||||
"icon": "MdVibration",
|
||||
"title": "Haptic Feedback",
|
||||
"description": "Enable haptic feedback on touch events.",
|
||||
},
|
||||
{
|
||||
"id": "selection_longPress_timeout",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Slider",
|
||||
"icon": "MdTimer",
|
||||
"title": "Selection press delay",
|
||||
"description": "Set the delay before the selection trigger is activated.",
|
||||
"props": {
|
||||
min: 300,
|
||||
max: 2000,
|
||||
step: 100,
|
||||
marks: {
|
||||
300: "0.3s",
|
||||
600: "0.6s",
|
||||
1000: "1s",
|
||||
1500: "1.5s",
|
||||
2000: "2s",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "clear_internal_storage",
|
||||
"storaged": false,
|
||||
"group": "general",
|
||||
"component": "Button",
|
||||
"icon": "MdDelete",
|
||||
"title": "Clear internal storage",
|
||||
"description": "Clear all the data stored in the internal storage, including your current session. It will not affect the data stored in the cloud.",
|
||||
"emitEvent": "app.clearInternalStorage"
|
||||
},
|
||||
{
|
||||
"id": "notifications_sound",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Switch",
|
||||
"icon": "MdVolumeUp",
|
||||
"title": "Notifications Sound",
|
||||
"description": "Play a sound when a notification is received.",
|
||||
},
|
||||
{
|
||||
"id": "notifications_vibrate",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Switch",
|
||||
"icon": "MdVibration",
|
||||
"title": "Vibration",
|
||||
"description": "Vibrate the device when a notification is received.",
|
||||
"emitEvent": "changeNotificationsVibrate"
|
||||
},
|
||||
{
|
||||
"id": "notifications_sound_volume",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Slider",
|
||||
"icon": "MdVolumeUp",
|
||||
"title": "Sound Volume",
|
||||
"description": "Set the volume of the sound when a notification is received.",
|
||||
"props": {
|
||||
tipFormatter: (value) => {
|
||||
return `${value}%`
|
||||
{
|
||||
"id": "haptic_feedback",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Switch",
|
||||
"icon": "MdVibration",
|
||||
"title": "Haptic Feedback",
|
||||
"description": "Enable haptic feedback on touch events.",
|
||||
},
|
||||
{
|
||||
"id": "selection_longPress_timeout",
|
||||
"storaged": true,
|
||||
"group": "general",
|
||||
"component": "Slider",
|
||||
"icon": "MdTimer",
|
||||
"title": "Selection press delay",
|
||||
"description": "Set the delay before the selection trigger is activated.",
|
||||
"props": {
|
||||
min: 300,
|
||||
max: 2000,
|
||||
step: 100,
|
||||
marks: {
|
||||
300: "0.3s",
|
||||
600: "0.6s",
|
||||
1000: "1s",
|
||||
1500: "1.5s",
|
||||
2000: "2s",
|
||||
}
|
||||
}
|
||||
},
|
||||
"emitEvent": "changeNotificationsSoundVolume"
|
||||
},
|
||||
{
|
||||
"id": "collapseOnLooseFocus",
|
||||
"storaged": true,
|
||||
"group": "sidebar",
|
||||
"component": "Switch",
|
||||
"icon": "Columns",
|
||||
"title": "Auto Collapse",
|
||||
"description": "Collapse the sidebar when loose focus",
|
||||
"emitEvent": "settingChanged.sidebar_collapse",
|
||||
},
|
||||
{
|
||||
"id": "autoCollapseDelay",
|
||||
"storaged": true,
|
||||
"group": "sidebar",
|
||||
"component": "Slider",
|
||||
"icon": "MdTimer",
|
||||
"dependsOn": {
|
||||
"collapseOnLooseFocus": true
|
||||
{
|
||||
"id": "clear_internal_storage",
|
||||
"storaged": false,
|
||||
"group": "general",
|
||||
"component": "Button",
|
||||
"icon": "MdDelete",
|
||||
"title": "Clear internal storage",
|
||||
"description": "Clear all the data stored in the internal storage, including your current session. It will not affect the data stored in the cloud.",
|
||||
"emitEvent": "app.clearInternalStorage"
|
||||
},
|
||||
"title": "Auto Collapse timeout",
|
||||
"description": "Set the delay before the sidebar is collapsed",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 2000,
|
||||
step: 100,
|
||||
marks: {
|
||||
0: "No delay",
|
||||
600: "0.6s",
|
||||
1000: "1s",
|
||||
1500: "1.5s",
|
||||
2000: "2s",
|
||||
{
|
||||
"id": "notifications_sound",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Switch",
|
||||
"icon": "MdVolumeUp",
|
||||
"title": "Notifications Sound",
|
||||
"description": "Play a sound when a notification is received.",
|
||||
},
|
||||
{
|
||||
"id": "notifications_vibrate",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Switch",
|
||||
"icon": "MdVibration",
|
||||
"title": "Vibration",
|
||||
"description": "Vibrate the device when a notification is received.",
|
||||
"emitEvent": "changeNotificationsVibrate"
|
||||
},
|
||||
{
|
||||
"id": "notifications_sound_volume",
|
||||
"storaged": true,
|
||||
"group": "notifications",
|
||||
"component": "Slider",
|
||||
"icon": "MdVolumeUp",
|
||||
"title": "Sound Volume",
|
||||
"description": "Set the volume of the sound when a notification is received.",
|
||||
"props": {
|
||||
tipFormatter: (value) => {
|
||||
return `${value}%`
|
||||
}
|
||||
},
|
||||
"emitEvent": "changeNotificationsSoundVolume"
|
||||
},
|
||||
{
|
||||
"id": "collapseOnLooseFocus",
|
||||
"storaged": true,
|
||||
"group": "sidebar",
|
||||
"component": "Switch",
|
||||
"icon": "Columns",
|
||||
"title": "Auto Collapse",
|
||||
"description": "Collapse the sidebar when loose focus",
|
||||
"emitEvent": "settingChanged.sidebar_collapse",
|
||||
},
|
||||
{
|
||||
"id": "autoCollapseDelay",
|
||||
"storaged": true,
|
||||
"group": "sidebar",
|
||||
"component": "Slider",
|
||||
"icon": "MdTimer",
|
||||
"dependsOn": {
|
||||
"collapseOnLooseFocus": true
|
||||
},
|
||||
"title": "Auto Collapse timeout",
|
||||
"description": "Set the delay before the sidebar is collapsed",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 2000,
|
||||
step: 100,
|
||||
marks: {
|
||||
0: "No delay",
|
||||
600: "0.6s",
|
||||
1000: "1s",
|
||||
1500: "1.5s",
|
||||
2000: "2s",
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "feed_max_fetch",
|
||||
"title": "Fetch max items",
|
||||
"description": "Set the maximum number of items to load per fetch in the feed list",
|
||||
"component": "Slider",
|
||||
"icon": "MdFormatListNumbered",
|
||||
"group": "posts",
|
||||
"props": {
|
||||
min: 5,
|
||||
max: 50,
|
||||
},
|
||||
"storaged": true,
|
||||
},
|
||||
{
|
||||
"id": "postCard_carrusel_auto",
|
||||
"title": "Post autoplay",
|
||||
"description": "Automatically play the post medias when the post has multiple medias",
|
||||
"component": "Switch",
|
||||
"icon": "MdPhotoCameraBack",
|
||||
"group": "posts",
|
||||
"storaged": true,
|
||||
"emitEvent": "router.forceUpdate",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"id": "postCard_expansible_actions",
|
||||
"title": "Expansible actions",
|
||||
"description": "Automatically show or hide the actions bar",
|
||||
"component": "Switch",
|
||||
"icon": "MdCallToAction",
|
||||
"group": "posts",
|
||||
"storaged": true,
|
||||
"emitEvent": "router.forceUpdate"
|
||||
},
|
||||
]
|
||||
{
|
||||
"id": "feed_max_fetch",
|
||||
"title": "Fetch max items",
|
||||
"description": "Set the maximum number of items to load per fetch in the feed list",
|
||||
"component": "Slider",
|
||||
"icon": "MdFormatListNumbered",
|
||||
"group": "posts",
|
||||
"props": {
|
||||
min: 5,
|
||||
max: 50,
|
||||
},
|
||||
"storaged": true,
|
||||
},
|
||||
{
|
||||
"id": "postCard_carrusel_auto",
|
||||
"title": "Post autoplay",
|
||||
"description": "Automatically play the post medias when the post has multiple medias",
|
||||
"component": "Switch",
|
||||
"icon": "MdPhotoCameraBack",
|
||||
"group": "posts",
|
||||
"storaged": true,
|
||||
"emitEvent": "router.forceUpdate",
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"id": "postCard_expansible_actions",
|
||||
"title": "Expansible actions",
|
||||
"description": "Automatically show or hide the actions bar",
|
||||
"component": "Switch",
|
||||
"icon": "MdCallToAction",
|
||||
"group": "posts",
|
||||
"storaged": true,
|
||||
"emitEvent": "router.forceUpdate"
|
||||
},
|
||||
]
|
||||
}
|
@ -3,167 +3,171 @@ import loadable from "@loadable/component"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
export default [
|
||||
{
|
||||
"id": "compactWidth",
|
||||
"title": "Compact Width",
|
||||
"description": "Sets the width of the app to a compact width to facilitate the vision of components.",
|
||||
"component": "Switch",
|
||||
"icon": "MdCompress",
|
||||
"group": "layout",
|
||||
"experimental": true,
|
||||
"storaged": true
|
||||
},
|
||||
{
|
||||
"id": "sidebar.floating",
|
||||
"title": "Floating Sidebar",
|
||||
"description": "Make the sidebar float over layout content.",
|
||||
"component": "Switch",
|
||||
"icon": "MdOutlineLastPage",
|
||||
"group": "layout",
|
||||
"emitEvent": "app.softReload",
|
||||
"storaged": true
|
||||
},
|
||||
{
|
||||
"id": "reduceAnimations",
|
||||
"storaged": true,
|
||||
"group": "animations",
|
||||
"component": "Switch",
|
||||
"icon": "MdOutlineSlowMotionVideo",
|
||||
"title": "Reduce animation",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"id": "pageTransitionDuration",
|
||||
"storaged": true,
|
||||
"group": "animations",
|
||||
"component": "Slider",
|
||||
"icon": "MdOutlineSpeed",
|
||||
"title": "Page transition duration",
|
||||
"description": "Change the duration of the page transition animation.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 50,
|
||||
tooltip: {
|
||||
formatter: (value) => `${value / 1000}s`
|
||||
export default {
|
||||
icon: "Eye",
|
||||
label: "Apparence",
|
||||
settings: [
|
||||
{
|
||||
"id": "compactWidth",
|
||||
"title": "Compact Width",
|
||||
"description": "Sets the width of the app to a compact width to facilitate the vision of components.",
|
||||
"component": "Switch",
|
||||
"icon": "MdCompress",
|
||||
"group": "layout",
|
||||
"experimental": true,
|
||||
"storaged": true
|
||||
},
|
||||
{
|
||||
"id": "sidebar.floating",
|
||||
"title": "Floating Sidebar",
|
||||
"description": "Make the sidebar float over layout content.",
|
||||
"component": "Switch",
|
||||
"icon": "MdOutlineLastPage",
|
||||
"group": "layout",
|
||||
"emitEvent": "app.softReload",
|
||||
"storaged": true
|
||||
},
|
||||
{
|
||||
"id": "reduceAnimations",
|
||||
"storaged": true,
|
||||
"group": "animations",
|
||||
"component": "Switch",
|
||||
"icon": "MdOutlineSlowMotionVideo",
|
||||
"title": "Reduce animation",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"id": "pageTransitionDuration",
|
||||
"storaged": true,
|
||||
"group": "animations",
|
||||
"component": "Slider",
|
||||
"icon": "MdOutlineSpeed",
|
||||
"title": "Page transition duration",
|
||||
"description": "Change the duration of the page transition animation.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 50,
|
||||
tooltip: {
|
||||
formatter: (value) => `${value / 1000}s`
|
||||
}
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
"page-transition-duration": `${value}ms`
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "auto_darkMode",
|
||||
"experimental": true,
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Switch",
|
||||
"icon": "Moon",
|
||||
"title": "Auto dark mode",
|
||||
"emitEvent": "style.autoDarkModeToogle",
|
||||
},
|
||||
{
|
||||
"experimental": true,
|
||||
"dependsOn": {
|
||||
"auto_darkMode": false
|
||||
},
|
||||
"id": "darkMode",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Switch",
|
||||
"icon": "Moon",
|
||||
"title": "Dark mode",
|
||||
"emitEvent": "theme.applyVariant",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return value ? "dark" : "light"
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "primaryColor",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "SliderColorPicker",
|
||||
"title": "Primary color",
|
||||
"description": "Change primary color of the application.",
|
||||
"emitEvent": "modifyTheme",
|
||||
"reloadValueOnUpdateEvent": "resetTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
primaryColor: value
|
||||
}
|
||||
}
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
"page-transition-duration": `${value}ms`
|
||||
}
|
||||
{
|
||||
"id": "backgroundImage",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"title": "Background image",
|
||||
"description": "Change background image of the application. You can use a local image or a remote image (URL).",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"props": {
|
||||
"noPreview": true,
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundImage: `url(${value})`
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "auto_darkMode",
|
||||
"experimental": true,
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Switch",
|
||||
"icon": "Moon",
|
||||
"title": "Auto dark mode",
|
||||
"emitEvent": "style.autoDarkModeToogle",
|
||||
},
|
||||
{
|
||||
"experimental": true,
|
||||
"dependsOn": {
|
||||
"auto_darkMode": false
|
||||
{
|
||||
"id": "backgroundBlur",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Slider",
|
||||
"icon": "Eye",
|
||||
"title": "Background blur",
|
||||
"description": "Create a blur effect on the background.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 5
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundBlur: `${value}px`,
|
||||
}
|
||||
},
|
||||
},
|
||||
"id": "darkMode",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Switch",
|
||||
"icon": "Moon",
|
||||
"title": "Dark mode",
|
||||
"emitEvent": "theme.applyVariant",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return value ? "dark" : "light"
|
||||
{
|
||||
"id": "backgroundColorTransparency",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Slider",
|
||||
"icon": "Eye",
|
||||
"title": "Background color transparency",
|
||||
"description": "Adjust the transparency of the background color.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.1
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundColorTransparency: value,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "primaryColor",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "SliderColorPicker",
|
||||
"title": "Primary color",
|
||||
"description": "Change primary color of the application.",
|
||||
"emitEvent": "modifyTheme",
|
||||
"reloadValueOnUpdateEvent": "resetTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
primaryColor: value
|
||||
}
|
||||
{
|
||||
"id": "resetTheme",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Button",
|
||||
"title": "Reset theme",
|
||||
"props": {
|
||||
"children": "Default Theme"
|
||||
},
|
||||
"emitEvent": "resetTheme",
|
||||
"noUpdate": true,
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "backgroundImage",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"title": "Background image",
|
||||
"description": "Change background image of the application. You can use a local image or a remote image (URL).",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"props": {
|
||||
"noPreview": true,
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundImage: `url(${value})`
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "backgroundBlur",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Slider",
|
||||
"icon": "Eye",
|
||||
"title": "Background blur",
|
||||
"description": "Create a blur effect on the background.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 5
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundBlur: `${value}px`,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "backgroundColorTransparency",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Slider",
|
||||
"icon": "Eye",
|
||||
"title": "Background color transparency",
|
||||
"description": "Adjust the transparency of the background color.",
|
||||
"props": {
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.1
|
||||
},
|
||||
"emitEvent": "modifyTheme",
|
||||
"emissionValueUpdate": (value) => {
|
||||
return {
|
||||
backgroundColorTransparency: value,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "resetTheme",
|
||||
"storaged": true,
|
||||
"group": "aspect",
|
||||
"component": "Button",
|
||||
"title": "Reset theme",
|
||||
"props": {
|
||||
"children": "Default Theme"
|
||||
},
|
||||
"emitEvent": "resetTheme",
|
||||
"noUpdate": true,
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -6,29 +6,9 @@ import ApparenceSettings from "./apparence"
|
||||
//import ExtensionsSettings from "./extensions"
|
||||
|
||||
export default {
|
||||
app: {
|
||||
icon: "Command",
|
||||
label: "App",
|
||||
settings: AppSettings
|
||||
},
|
||||
profile: {
|
||||
icon: "User",
|
||||
label: "Profile",
|
||||
settings: ProfileSettings
|
||||
},
|
||||
apparence: {
|
||||
icon: "Eye",
|
||||
label: "Apparence",
|
||||
settings: ApparenceSettings
|
||||
},
|
||||
security: {
|
||||
icon: "Shield",
|
||||
label: "Security",
|
||||
settings: SecuritySettings
|
||||
},
|
||||
notifications: {
|
||||
icon: "Bell",
|
||||
label: "Notifications",
|
||||
settings: NotificationsSettings
|
||||
},
|
||||
app: AppSettings,
|
||||
profile: ProfileSettings,
|
||||
apparence: ApparenceSettings,
|
||||
security: SecuritySettings,
|
||||
notifications: NotificationsSettings,
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
import React from "react"
|
||||
|
||||
export default [
|
||||
export default {
|
||||
icon: "Bell",
|
||||
label: "Notifications",
|
||||
settings: [
|
||||
|
||||
]
|
||||
]
|
||||
}
|
@ -2,183 +2,191 @@ import React from "react"
|
||||
import { User } from "models"
|
||||
import loadable from "@loadable/component"
|
||||
|
||||
export default [
|
||||
{
|
||||
"id": "username",
|
||||
"group": "account.basicInfo",
|
||||
"component": "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",
|
||||
},
|
||||
export default {
|
||||
icon: "User",
|
||||
label: "Profile",
|
||||
ctxData: async () => {
|
||||
const userData = await User.data()
|
||||
|
||||
return {
|
||||
userData
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fullName",
|
||||
"group": "account.basicInfo",
|
||||
"component": "Input",
|
||||
"icon": "Edit3",
|
||||
"title": "Name",
|
||||
"description": "Change your public name",
|
||||
"props": {
|
||||
// set max length
|
||||
"maxLength": 120,
|
||||
"showCount": true,
|
||||
"allowClear": true,
|
||||
"placeholder": "Enter your name. e.g. John Doe",
|
||||
settings: [
|
||||
{
|
||||
"id": "username",
|
||||
"group": "account.basicInfo",
|
||||
"component": "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",
|
||||
},
|
||||
},
|
||||
"defaultValue": async () => {
|
||||
const userData = await User.data()
|
||||
return userData.fullName
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
{
|
||||
"id": "fullName",
|
||||
"group": "account.basicInfo",
|
||||
"component": "Input",
|
||||
"icon": "Edit3",
|
||||
"title": "Name",
|
||||
"description": "Change your public name",
|
||||
"props": {
|
||||
// set max length
|
||||
"maxLength": 120,
|
||||
"showCount": true,
|
||||
"allowClear": true,
|
||||
"placeholder": "Enter your name. e.g. John Doe",
|
||||
},
|
||||
"defaultValue": (ctx) => {
|
||||
console.log(ctx)
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
fullName: value
|
||||
return ctx.userData.fullName
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
fullName: value
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
},
|
||||
"extraActions": [
|
||||
{
|
||||
"id": "unset",
|
||||
"icon": "Delete",
|
||||
"title": "Unset",
|
||||
"onClick": async () => {
|
||||
window.app.api.withEndpoints("main").post.unsetPublicName()
|
||||
},
|
||||
"extraActions": [
|
||||
{
|
||||
"id": "unset",
|
||||
"icon": "Delete",
|
||||
"title": "Unset",
|
||||
"onClick": async () => {
|
||||
window.app.api.withEndpoints("main").post.unsetPublicName()
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"debounced": true,
|
||||
},
|
||||
{
|
||||
"id": "email",
|
||||
"group": "account.basicInfo",
|
||||
"component": "Input",
|
||||
"icon": "Mail",
|
||||
"title": "Email",
|
||||
"description": "Change your email address",
|
||||
"props": {
|
||||
"placeholder": "Enter your email address",
|
||||
"allowClear": true,
|
||||
"showCount": true,
|
||||
"maxLength": 320,
|
||||
],
|
||||
"debounced": true,
|
||||
},
|
||||
"defaultValue": async () => {
|
||||
const userData = await User.data()
|
||||
return userData.email
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
{
|
||||
"id": "email",
|
||||
"group": "account.basicInfo",
|
||||
"component": "Input",
|
||||
"icon": "Mail",
|
||||
"title": "Email",
|
||||
"description": "Change your email address",
|
||||
"props": {
|
||||
"placeholder": "Enter your email address",
|
||||
"allowClear": true,
|
||||
"showCount": true,
|
||||
"maxLength": 320,
|
||||
},
|
||||
"defaultValue": (ctx) => {
|
||||
return ctx.userData.email
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
email: value
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
email: value
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
{
|
||||
"id": "avatar",
|
||||
"group": "account.profile",
|
||||
"icon": "Image",
|
||||
"title": "Avatar",
|
||||
"description": "Change your avatar (Upload an image or use an URL)",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"defaultValue": async () => {
|
||||
const userData = await User.data()
|
||||
return userData.avatar
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
{
|
||||
"id": "avatar",
|
||||
"group": "account.profile",
|
||||
"icon": "Image",
|
||||
"title": "Avatar",
|
||||
"description": "Change your avatar (Upload an image or use an URL)",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"defaultValue": (ctx) => {
|
||||
return ctx.userData.avatar
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
avatar: value
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
avatar: value
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
app.message.success("Avatar updated")
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
app.message.success("Avatar updated")
|
||||
return result
|
||||
}
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
{
|
||||
"id": "cover",
|
||||
"group": "account.profile",
|
||||
"icon": "Image",
|
||||
"title": "Cover",
|
||||
"description": "Change your profile cover (Upload an image or use an URL)",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"defaultValue": async () => {
|
||||
const userData = await User.data()
|
||||
return userData.cover
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
{
|
||||
"id": "cover",
|
||||
"group": "account.profile",
|
||||
"icon": "Image",
|
||||
"title": "Cover",
|
||||
"description": "Change your profile cover (Upload an image or use an URL)",
|
||||
"component": loadable(() => import("../components/ImageUploader")),
|
||||
"defaultValue": (ctx) => {
|
||||
return ctx.userData.cover
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
cover: value
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
cover: value
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
app.message.success("Cover updated")
|
||||
return result
|
||||
}
|
||||
})
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
{
|
||||
"id": "description",
|
||||
"group": "account.profile",
|
||||
"component": "TextArea",
|
||||
"icon": "Edit3",
|
||||
"title": "Description",
|
||||
"description": "Change your description for your profile",
|
||||
"props": {
|
||||
"placeholder": "Enter here a description for your profile",
|
||||
"maxLength": 1000,
|
||||
"showCount": true,
|
||||
"allowClear": true
|
||||
},
|
||||
"defaultValue": (ctx) => {
|
||||
return ctx.userData.description
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
|
||||
if (result) {
|
||||
app.message.success("Cover updated")
|
||||
return result
|
||||
}
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
{
|
||||
"id": "description",
|
||||
"group": "account.profile",
|
||||
"component": "TextArea",
|
||||
"icon": "Edit3",
|
||||
"title": "Description",
|
||||
"description": "Change your description for your profile",
|
||||
"props": {
|
||||
"placeholder": "Enter here a description for your profile",
|
||||
"maxLength": 1000,
|
||||
"showCount": true,
|
||||
"allowClear": true
|
||||
},
|
||||
"defaultValue": async () => {
|
||||
const userData = await User.data()
|
||||
return userData.description
|
||||
},
|
||||
"onUpdate": async (value) => {
|
||||
const selfId = await User.selfUserId()
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
description: value
|
||||
}
|
||||
})
|
||||
|
||||
const result = window.app.api.withEndpoints("main").post.updateUser({
|
||||
_id: selfId,
|
||||
update: {
|
||||
description: value
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
})
|
||||
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
"debounced": true,
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
@ -3,39 +3,43 @@ import loadable from "@loadable/component"
|
||||
|
||||
// TODO: Make logout button require a valid session to be not disabled
|
||||
|
||||
export default [
|
||||
{
|
||||
"id": "change-password",
|
||||
"group": "security.account",
|
||||
"title": "Change Password",
|
||||
"description": "Change your password",
|
||||
"icon": "Lock",
|
||||
"component": loadable(() => import("../components/changePassword")),
|
||||
},
|
||||
{
|
||||
"id": "two-factor-authentication",
|
||||
"group": "security.account",
|
||||
"title": "Two-Factor Authentication",
|
||||
"description": "Add an extra layer of security to your account",
|
||||
"icon": "MdOutlineSecurity",
|
||||
"component": "Switch",
|
||||
},
|
||||
{
|
||||
"id": "sessions",
|
||||
"group": "security.account",
|
||||
"title": "Sessions",
|
||||
"description": "Manage your active sessions",
|
||||
"icon": "Monitor",
|
||||
"component": loadable(() => import("../components/sessions")),
|
||||
"storaged": false
|
||||
},
|
||||
{
|
||||
"id": "logout",
|
||||
"group": "security.other",
|
||||
"component": "Button",
|
||||
"icon": "LogOut",
|
||||
"title": "Logout",
|
||||
"description": "Logout from your account",
|
||||
"emitEvent": "session.logout",
|
||||
}
|
||||
]
|
||||
export default {
|
||||
icon: "Shield",
|
||||
label: "Security",
|
||||
settings: [
|
||||
{
|
||||
"id": "change-password",
|
||||
"group": "security.account",
|
||||
"title": "Change Password",
|
||||
"description": "Change your password",
|
||||
"icon": "Lock",
|
||||
"component": loadable(() => import("../components/changePassword")),
|
||||
},
|
||||
{
|
||||
"id": "two-factor-authentication",
|
||||
"group": "security.account",
|
||||
"title": "Two-Factor Authentication",
|
||||
"description": "Add an extra layer of security to your account",
|
||||
"icon": "MdOutlineSecurity",
|
||||
"component": "Switch",
|
||||
},
|
||||
{
|
||||
"id": "sessions",
|
||||
"group": "security.account",
|
||||
"title": "Sessions",
|
||||
"description": "Manage your active sessions",
|
||||
"icon": "Monitor",
|
||||
"component": loadable(() => import("../components/sessions")),
|
||||
"storaged": false
|
||||
},
|
||||
{
|
||||
"id": "logout",
|
||||
"group": "security.other",
|
||||
"component": "Button",
|
||||
"icon": "LogOut",
|
||||
"title": "Logout",
|
||||
"description": "Logout from your account",
|
||||
"emitEvent": "session.logout",
|
||||
}
|
||||
]
|
||||
}
|
@ -56,7 +56,7 @@ const SettingItem = (props) => {
|
||||
let { item } = props
|
||||
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const [value, setValue] = React.useState(item.defaultValue ?? false)
|
||||
const [value, setValue] = React.useState(null)
|
||||
const [delayedValue, setDelayedValue] = React.useState(null)
|
||||
const [disabled, setDisabled] = React.useState(false)
|
||||
|
||||
@ -130,15 +130,6 @@ const SettingItem = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const onUnmount = () => {
|
||||
// unsubscribe eventBus events
|
||||
if (typeof item.dependsOn === "object") {
|
||||
for (let key in item.dependsOn) {
|
||||
window.app.eventBus.off(`setting.update.${key}`, onUpdateItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const checkDependsValidation = () => {
|
||||
return !Boolean(Object.keys(item.dependsOn).every((key) => {
|
||||
const storagedValue = window.app.settings.get(key)
|
||||
@ -160,7 +151,11 @@ const SettingItem = (props) => {
|
||||
}
|
||||
|
||||
if (typeof item.defaultValue === "function") {
|
||||
setValue(await item.defaultValue())
|
||||
setLoading(true)
|
||||
|
||||
setValue(await item.defaultValue(props.ctx))
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
if (item.disabled === true) {
|
||||
@ -196,7 +191,13 @@ const SettingItem = (props) => {
|
||||
React.useEffect(() => {
|
||||
settingInitialization()
|
||||
|
||||
return onUnmount
|
||||
return () => {
|
||||
if (typeof item.dependsOn === "object") {
|
||||
for (let key in item.dependsOn) {
|
||||
window.app.eventBus.off(`setting.update.${key}`, onUpdateItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (typeof SettingComponent === "string") {
|
||||
@ -301,37 +302,140 @@ const SettingItem = (props) => {
|
||||
</div>
|
||||
<div className="component">
|
||||
<div>
|
||||
{loading ? <div> Loading... </div> : React.createElement(SettingComponent, {
|
||||
...item.props,
|
||||
ctx: {
|
||||
currentValue: value,
|
||||
dispatchUpdate,
|
||||
onUpdateItem,
|
||||
...props.ctx,
|
||||
}
|
||||
})}
|
||||
{
|
||||
loading
|
||||
? <div> Loading... </div>
|
||||
: React.createElement(SettingComponent, {
|
||||
...item.props,
|
||||
ctx: {
|
||||
currentValue: value,
|
||||
dispatchUpdate,
|
||||
onUpdateItem,
|
||||
...props.ctx,
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
|
||||
{delayedValue && <div>
|
||||
<antd.Button
|
||||
type="round"
|
||||
icon={<Icons.Save />}
|
||||
onClick={async () => await dispatchUpdate(value)}
|
||||
>
|
||||
Save
|
||||
</antd.Button>
|
||||
</div>}
|
||||
{
|
||||
delayedValue && <div>
|
||||
<antd.Button
|
||||
type="round"
|
||||
icon={<Icons.Save />}
|
||||
onClick={async () => await dispatchUpdate(value)}
|
||||
>
|
||||
Save
|
||||
</antd.Button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
const SettingGroup = React.memo((props) => {
|
||||
const {
|
||||
ctx,
|
||||
groupKey,
|
||||
settings,
|
||||
loading,
|
||||
} = props
|
||||
|
||||
const fromDecoratorIcon = groupsDecorator[groupKey]?.icon
|
||||
const fromDecoratorTitle = groupsDecorator[groupKey]?.title
|
||||
|
||||
if (loading) {
|
||||
return <antd.Skeleton active />
|
||||
}
|
||||
|
||||
return <div index={groupKey} key={groupKey} className="group">
|
||||
<h1>
|
||||
{
|
||||
fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null
|
||||
}
|
||||
<Translation>
|
||||
{
|
||||
t => t(fromDecoratorTitle ?? groupKey)
|
||||
}
|
||||
</Translation>
|
||||
</h1>
|
||||
<div className="content">
|
||||
{
|
||||
settings.map((item) => <SettingItem
|
||||
item={item}
|
||||
ctx={ctx}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
|
||||
const SettingTab = React.memo((props) => {
|
||||
const { tab } = props
|
||||
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const [ctxData, setCtxData] = React.useState(null)
|
||||
|
||||
let groupsSettings = {}
|
||||
|
||||
if (!Array.isArray(tab.settings)) {
|
||||
console.error("Cannot generate settings from non-array")
|
||||
return groupsSettings
|
||||
}
|
||||
|
||||
tab.settings.forEach((item) => {
|
||||
if (!groupsSettings[item.group]) {
|
||||
groupsSettings[item.group] = []
|
||||
}
|
||||
|
||||
groupsSettings[item.group].push(item)
|
||||
})
|
||||
|
||||
const processCtx = async () => {
|
||||
setLoading(true)
|
||||
|
||||
if (typeof tab.ctxData === "function") {
|
||||
const resultCtx = await tab.ctxData()
|
||||
|
||||
setCtxData(resultCtx)
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
processCtx()
|
||||
}, [])
|
||||
|
||||
return Object.keys(groupsSettings).map((groupKey) => {
|
||||
return <SettingGroup
|
||||
groupKey={groupKey}
|
||||
settings={groupsSettings[groupKey]}
|
||||
loading={loading}
|
||||
ctx={ctxData}
|
||||
/>
|
||||
})
|
||||
})
|
||||
|
||||
const SettingsTabs = Object.keys(SettingsList).map((settingsKey) => {
|
||||
const tab = SettingsList[settingsKey]
|
||||
|
||||
return {
|
||||
key: settingsKey,
|
||||
label: <>
|
||||
{createIconRender(tab.icon ?? "Settings")}
|
||||
{tab.label}
|
||||
</>,
|
||||
children: <SettingTab
|
||||
tab={tab}
|
||||
/>
|
||||
}
|
||||
})
|
||||
|
||||
export default class SettingsMenu extends React.PureComponent {
|
||||
state = {
|
||||
transitionActive: false,
|
||||
activeKey: "app"
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount = async () => {
|
||||
if (typeof this.props.close === "function") {
|
||||
// register escape key to close settings menu
|
||||
window.addEventListener("keydown", this.handleKeyDown)
|
||||
@ -350,85 +454,6 @@ export default class SettingsMenu extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
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 <div className={classnames("fade-opacity-active", { "fade-opacity-leave": this.state.transitionActive })}>
|
||||
<div key={key} className="group">
|
||||
<h1>
|
||||
{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null}
|
||||
<Translation>{
|
||||
t => t(fromDecoratorTitle ?? key)
|
||||
}</Translation>
|
||||
</h1>
|
||||
<div className="content">
|
||||
{group.map((item) => <SettingItem
|
||||
item={item}
|
||||
ctx={{
|
||||
close: this.props.close
|
||||
}}
|
||||
/>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
generateSettingsGroups = (data) => {
|
||||
let groups = {}
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
console.error("Cannot generate settings groups from non-array data")
|
||||
return 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 <antd.Tabs.TabPane
|
||||
key={key}
|
||||
tab={
|
||||
<span>
|
||||
{createIconRender(SettingsList[key].icon)}
|
||||
{SettingsList[key].label}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{this.generateSettingsGroups(SettingsList[key].settings)}
|
||||
</antd.Tabs.TabPane>
|
||||
})
|
||||
}
|
||||
|
||||
onClickAppAbout = () => {
|
||||
window.app.setLocation("/about")
|
||||
|
||||
@ -437,22 +462,28 @@ export default class SettingsMenu extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
changeTab = (activeKey) => {
|
||||
this.setState({ activeKey })
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={
|
||||
classnames("settings_wrapper", {
|
||||
["mobile"]: window.isMobile,
|
||||
})
|
||||
}>
|
||||
return <div
|
||||
className={classnames(
|
||||
"settings_wrapper",
|
||||
{
|
||||
["mobile"]: window.isMobile,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="settings">
|
||||
<antd.Tabs
|
||||
activeKey={this.state.activeKey}
|
||||
onTabClick={this.handlePageTransition}
|
||||
onTabClick={this.changeTab}
|
||||
tabPosition={window.isMobile ? "top" : "left"}
|
||||
centered={window.isMobile}
|
||||
destroyInactiveTabPane
|
||||
>
|
||||
{this.generateSettingsTabs()}
|
||||
</antd.Tabs>
|
||||
items={SettingsTabs}
|
||||
/>
|
||||
|
||||
<SettingsFooter onClickAppAbout={this.onClickAppAbout} />
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user