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}
+ />