From f2f3988da637618d95f72b006024d0244cd6db9c Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Fri, 13 Jan 2023 19:07:58 +0000 Subject: [PATCH] improve settings initialization --- packages/app/constants/settings/app/index.jsx | 310 ++++++++-------- .../constants/settings/apparence/index.jsx | 320 ++++++++--------- packages/app/constants/settings/index.js | 30 +- .../settings/notifications/index.jsx | 8 +- .../app/constants/settings/profile/index.jsx | 332 +++++++++--------- .../app/constants/settings/security/index.jsx | 76 ++-- .../app/src/components/Settings/index.jsx | 271 +++++++------- 7 files changed, 691 insertions(+), 656 deletions(-) diff --git a/packages/app/constants/settings/app/index.jsx b/packages/app/constants/settings/app/index.jsx index 66194bb1..8c436617 100755 --- a/packages/app/constants/settings/app/index.jsx +++ b/packages/app/constants/settings/app/index.jsx @@ -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 {language.name} - }) +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 {language.name} + }) + }, + "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" - }, -] \ No newline at end of file + { + "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" + }, + ] +} \ No newline at end of file diff --git a/packages/app/constants/settings/apparence/index.jsx b/packages/app/constants/settings/apparence/index.jsx index 7eee041c..3f957a74 100755 --- a/packages/app/constants/settings/apparence/index.jsx +++ b/packages/app/constants/settings/apparence/index.jsx @@ -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, - } -] \ No newline at end of file + ] +} \ No newline at end of file diff --git a/packages/app/constants/settings/index.js b/packages/app/constants/settings/index.js index cdfabf49..69861654 100755 --- a/packages/app/constants/settings/index.js +++ b/packages/app/constants/settings/index.js @@ -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, } \ No newline at end of file diff --git a/packages/app/constants/settings/notifications/index.jsx b/packages/app/constants/settings/notifications/index.jsx index a6249b33..76ac3ea9 100755 --- a/packages/app/constants/settings/notifications/index.jsx +++ b/packages/app/constants/settings/notifications/index.jsx @@ -1,5 +1,9 @@ import React from "react" -export default [ +export default { + icon: "Bell", + label: "Notifications", + settings: [ -] \ No newline at end of file + ] +} \ No newline at end of file diff --git a/packages/app/constants/settings/profile/index.jsx b/packages/app/constants/settings/profile/index.jsx index 6774bdcf..96329ba8 100755 --- a/packages/app/constants/settings/profile/index.jsx +++ b/packages/app/constants/settings/profile/index.jsx @@ -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, - }, -] \ No newline at end of file + ] +} \ No newline at end of file diff --git a/packages/app/constants/settings/security/index.jsx b/packages/app/constants/settings/security/index.jsx index 10ea6ab7..8bdf2d06 100755 --- a/packages/app/constants/settings/security/index.jsx +++ b/packages/app/constants/settings/security/index.jsx @@ -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", - } -] \ No newline at end of file +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", + } + ] +} \ No newline at end of file diff --git a/packages/app/src/components/Settings/index.jsx b/packages/app/src/components/Settings/index.jsx index 6a4477da..ab87bd33 100755 --- a/packages/app/src/components/Settings/index.jsx +++ b/packages/app/src/components/Settings/index.jsx @@ -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) => {
- {loading ?
Loading...
: React.createElement(SettingComponent, { - ...item.props, - ctx: { - currentValue: value, - dispatchUpdate, - onUpdateItem, - ...props.ctx, - } - })} + { + loading + ?
Loading...
+ : React.createElement(SettingComponent, { + ...item.props, + ctx: { + currentValue: value, + dispatchUpdate, + onUpdateItem, + ...props.ctx, + } + })}
- {delayedValue &&
- } - onClick={async () => await dispatchUpdate(value)} - > - Save - -
} + { + delayedValue &&
+ } + onClick={async () => await dispatchUpdate(value)} + > + Save + +
+ }
} +const SettingGroup = React.memo((props) => { + const { + ctx, + groupKey, + settings, + loading, + } = props + + const fromDecoratorIcon = groupsDecorator[groupKey]?.icon + const fromDecoratorTitle = groupsDecorator[groupKey]?.title + + if (loading) { + return + } + + return
+

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

+
+ { + settings.map((item) => ) + } +
+
+}) + +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 + }) +}) + +const SettingsTabs = Object.keys(SettingsList).map((settingsKey) => { + const tab = SettingsList[settingsKey] + + return { + key: settingsKey, + label: <> + {createIconRender(tab.icon ?? "Settings")} + {tab.label} + , + children: + } +}) + 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
-
-

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

-
- {group.map((item) => )} -
-
-
- } - - 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 - {createIconRender(SettingsList[key].icon)} - {SettingsList[key].label} - - } - > - {this.generateSettingsGroups(SettingsList[key].settings)} - - }) - } - onClickAppAbout = () => { window.app.setLocation("/about") @@ -437,22 +462,28 @@ export default class SettingsMenu extends React.PureComponent { } } + changeTab = (activeKey) => { + this.setState({ activeKey }) + } + render() { - return
+ return
- {this.generateSettingsTabs()} - + items={SettingsTabs} + />