diff --git a/packages/app/config/index.js b/packages/app/config/index.js index bc7ac92d..32b42378 100644 --- a/packages/app/config/index.js +++ b/packages/app/config/index.js @@ -14,8 +14,8 @@ export default { }, remotes: { mainApi: process.env.NODE_ENV !== "production" ? `http://${window.location.hostname}:3000` : defaultRemotesOrigins.main_api, - streamingApi: defaultRemotesOrigins.streaming_api, - websocketApi: process.env.NODE_ENV !== "production" ? `ws://${window.location.hostname}:3000` : defaultRemotesOrigins.websocket_api, + streamingApi: process.env.NODE_ENV !== "production" ? `https://${window.location.hostname}:3002` : defaultRemotesOrigins.streaming_api, + websocketApi: process.env.NODE_ENV !== "production" ? `ws://${window.location.hostname}:3001` : defaultRemotesOrigins.websocket_api, }, app: { title: packagejson.name, diff --git a/packages/app/constants/settings/account.jsx b/packages/app/constants/settings/account.jsx index 760eec89..4dfbeb0d 100644 --- a/packages/app/constants/settings/account.jsx +++ b/packages/app/constants/settings/account.jsx @@ -5,7 +5,7 @@ export default [ { "id": "username", "group": "account.basicInfo", - "type": "Button", + "component": "Button", "icon": "AtSign", "title": "Username", "description": "Your username is the name you use to log in to your account.", @@ -17,7 +17,7 @@ export default [ { "id": "fullName", "group": "account.basicInfo", - "type": "Input", + "component": "Input", "icon": "Edit3", "title": "Name", "description": "Change your public name", @@ -57,7 +57,7 @@ export default [ { "id": "email", "group": "account.basicInfo", - "type": "Input", + "component": "Input", "icon": "Mail", "title": "Email", "description": "Change your email address", @@ -87,7 +87,7 @@ export default [ { "id": "avatar", "group": "account.profile", - "type": "ImageUpload", + "component": "ImageUpload", "icon": "Image", "title": "Avatar", "description": "Change your avatar", @@ -95,7 +95,7 @@ export default [ { "id": "cover", "group": "account.profile", - "type": "ImageUpload", + "component": "ImageUpload", "icon": "Image", "title": "Cover", "description": "Change your cover", @@ -103,7 +103,7 @@ export default [ { "id": "primaryBadge", "group": "account.profile", - "type": "Select", + "component": "Select", "icon": "Tag", "title": "Primary badge", "description": "Change your primary badge", @@ -111,7 +111,7 @@ export default [ { "id": "description", "group": "account.profile", - "type": "TextArea", + "component": "TextArea", "icon": "Edit3", "title": "Description", "description": "Change your description for your profile", @@ -141,7 +141,7 @@ export default [ { "id": "logout", "footer": true, - "type": "Button", + "component": "Button", "icon": "LogOut", "title": "Logout", "emitEvent": "session.logout", diff --git a/packages/app/constants/settings/app.jsx b/packages/app/constants/settings/app.jsx index d3af1c8f..32178b39 100644 --- a/packages/app/constants/settings/app.jsx +++ b/packages/app/constants/settings/app.jsx @@ -7,7 +7,7 @@ export default [ "id": "language", "storaged": true, "group": "general", - "type": "Select", + "component": "Select", "icon": "MdTranslate", "title": "Language", "description": "Choose a language for the application", @@ -22,7 +22,7 @@ export default [ "id": "forceMobileMode", "storaged": true, "group": "general", - "type": "Switch", + "component": "Switch", "icon": "MdSmartphone", "title": "Force Mobile Mode", "description": "Force the application to run in mobile mode.", @@ -32,7 +32,7 @@ export default [ "id": "haptic_feedback", "storaged": true, "group": "general", - "type": "Switch", + "component": "Switch", "icon": "MdVibration", "title": "Haptic Feedback", "description": "Enable haptic feedback on touch events.", @@ -41,7 +41,7 @@ export default [ "id": "selection_longPress_timeout", "storaged": true, "group": "general", - "type": "Slider", + "component": "Slider", "icon": "MdTimer", "title": "Selection press delay", "description": "Set the delay before the selection trigger is activated.", @@ -62,7 +62,7 @@ export default [ "id": "notifications_sound", "storaged": true, "group": "notifications", - "type": "Switch", + "component": "Switch", "icon": "MdVolumeUp", "title": "Notifications Sound", "description": "Play a sound when a notification is received.", @@ -71,7 +71,7 @@ export default [ "id": "notifications_vibrate", "storaged": true, "group": "notifications", - "type": "Switch", + "component": "Switch", "icon": "MdVibration", "title": "Vibration", "description": "Vibrate the device when a notification is received.", @@ -81,7 +81,7 @@ export default [ "id": "notifications_sound_volume", "storaged": true, "group": "notifications", - "type": "Slider", + "component": "Slider", "icon": "MdVolumeUp", "title": "Sound Volume", "description": "Set the volume of the sound when a notification is received.", @@ -96,7 +96,7 @@ export default [ "id": "edit_sidebar", "storaged": true, "group": "sidebar", - "type": "Button", + "component": "Button", "icon": "Edit", "title": "Edit Sidebar", "emitEvent": "edit_sidebar", @@ -106,7 +106,7 @@ export default [ "id": "collapseOnLooseFocus", "storaged": true, "group": "sidebar", - "type": "Switch", + "component": "Switch", "icon": "Columns", "title": "Auto Collapse", "description": "Collapse the sidebar when loose focus", @@ -116,7 +116,7 @@ export default [ "id": "autoCollapseDelay", "storaged": true, "group": "sidebar", - "type": "Slider", + "component": "Slider", "icon": "Wh", "dependsOn": { "collapseOnLooseFocus": true diff --git a/packages/app/constants/settings/apparence.jsx b/packages/app/constants/settings/apparence.jsx index 310ba6f3..5dd1864f 100644 --- a/packages/app/constants/settings/apparence.jsx +++ b/packages/app/constants/settings/apparence.jsx @@ -5,17 +5,33 @@ export default [ "id": "reduceAnimations", "storaged": true, "group": "aspect", - "type": "Switch", + "component": "Switch", "icon": "MdOutlineAnimation", "title": "Reduce animation", "experimental": true }, { + "id": "auto_darkMode", + "dependsOn": { + "darkMode": false + }, "experimental": true, + "storaged": true, + "group": "aspect", + "component": "Switch", + "icon": "Moon", + "title": "Auto dark mode", + "emitEvent": "app.autoDarkModeToogle", + }, + { + "experimental": true, + "dependsOn": { + "auto_darkMode": false + }, "id": "darkMode", "storaged": true, "group": "aspect", - "type": "Switch", + "component": "Switch", "icon": "Moon", "title": "Dark mode", "emitEvent": "theme.applyVariant", @@ -27,7 +43,7 @@ export default [ "id": "primaryColor", "storaged": true, "group": "aspect", - "type": "SliderColorPicker", + "component": "SliderColorPicker", "title": "Primary color", "description": "Change primary color of the application.", "emitEvent": "modifyTheme", @@ -42,7 +58,7 @@ export default [ "id": "resetTheme", "storaged": true, "group": "aspect", - "type": "Button", + "component": "Button", "title": "Reset theme", "props": { "children": "Default Theme" diff --git a/packages/app/src/components/Settings/index.jsx b/packages/app/src/components/Settings/index.jsx index 0887d213..9edb6142 100644 --- a/packages/app/src/components/Settings/index.jsx +++ b/packages/app/src/components/Settings/index.jsx @@ -28,16 +28,16 @@ const ItemTypes = { const SettingItem = (props) => { let { item } = props + const [loading, setLoading] = React.useState(true) const [value, setValue] = React.useState(item.defaultValue ?? false) const [delayedValue, setDelayedValue] = React.useState(null) + const [disabled, setDisabled] = React.useState(false) - if (!item.type) { - console.error(`Item [${item.id}] has no an type!`) - return null - } - if (typeof ItemTypes[item.type] === "undefined") { - console.error(`Item [${item.id}] has an invalid type: ${item.type}`) + let SettingComponent = item.component + + if (!SettingComponent) { + console.error(`Item [${item.id}] has no an component!`) return null } @@ -100,6 +100,29 @@ 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) + + console.debug(`Checking validation for [${key}] with now value [${storagedValue}]`) + + if (typeof item.dependsOn[key] === "function") { + return item.dependsOn[key](storagedValue) + } + + return storagedValue === item.dependsOn[key] + })) + } + const settingInitialization = async () => { if (item.storaged) { const storagedValue = window.app.settings.get(item.id) @@ -111,17 +134,15 @@ const SettingItem = (props) => { } if (typeof item.dependsOn === "object") { - const dependsOptionsKeys = Object.keys(item.dependsOn) + // create a event handler to watch changes + Object.keys(item.dependsOn).forEach((key) => { + window.app.eventBus.on(`setting.update.${key}`, () => { + setDisabled(checkDependsValidation()) + }) + }) - item.props.disabled = !Boolean(dependsOptionsKeys.every((key) => { - const storagedValue = window.app.settings.get(key) - - if (typeof item.dependsOn[key] === "function") { - return item.dependsOn[key](storagedValue) - } - - return storagedValue === item.dependsOn[key] - })) + // by default check depends validation + setDisabled(checkDependsValidation()) } if (typeof item.listenUpdateValue === "string") { @@ -140,58 +161,76 @@ const SettingItem = (props) => { React.useEffect(() => { settingInitialization() + + return onUnmount }, []) - switch (item.type.toLowerCase()) { - case "slidercolorpicker": { - item.props.onChange = (color) => { - item.props.color = color.hex - } - item.props.onChangeComplete = (color) => { - onUpdateItem(color.hex) - } + if (typeof SettingComponent === "string") { + if (typeof ItemTypes[SettingComponent] === "undefined") { + console.error(`Item [${item.id}] has an invalid component: ${item.component}`) + return null + } - item.props.color = value + // fix props - break - } - case "textarea": { - item.props.defaultValue = value - item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) - item.props.onChange = (event) => onUpdateItem(event.target.value) - break - } - case "input": { - item.props.defaultValue = value - item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) - item.props.onChange = (event) => onUpdateItem(event.target.value) - break - } - case "switch": { - item.props.checked = value - item.props.onClick = (event) => onUpdateItem(event) - break - } - case "select": { - item.props.onChange = (value) => onUpdateItem(value) - item.props.defaultValue = value - break - } - case "slider": { - item.props.defaultValue = value - item.props.onAfterChange = (value) => onUpdateItem(value) - break - } - default: { - if (!item.props.children) { - item.props.children = item.title ?? item.id + switch (SettingComponent.toLowerCase()) { + case "slidercolorpicker": { + item.props.onChange = (color) => { + item.props.color = color.hex + } + item.props.onChangeComplete = (color) => { + onUpdateItem(color.hex) + } + + item.props.color = value + + break + } + case "textarea": { + item.props.defaultValue = value + item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) + item.props.onChange = (event) => onUpdateItem(event.target.value) + break + } + case "input": { + item.props.defaultValue = value + item.props.onPressEnter = (event) => dispatchUpdate(event.target.value) + item.props.onChange = (event) => onUpdateItem(event.target.value) + break + } + case "switch": { + item.props.checked = value + item.props.onClick = (event) => onUpdateItem(event) + break + } + case "select": { + item.props.onChange = (value) => onUpdateItem(value) + item.props.defaultValue = value + break + } + case "slider": { + item.props.defaultValue = value + item.props.onAfterChange = (value) => onUpdateItem(value) + break + } + default: { + if (!item.props.children) { + item.props.children = item.title ?? item.id + } + + item.props.value = item.defaultValue + item.props.onClick = (event) => onUpdateItem(event) + + break } - item.props.value = item.defaultValue - item.props.onClick = (event) => onUpdateItem(event) - break } + + // override with default item component + SettingComponent = ItemTypes[SettingComponent] } + item.props["disabled"] = disabled + return
@@ -230,7 +269,7 @@ const SettingItem = (props) => {
- {loading ?
Loading...
: React.createElement(ItemTypes[item.type], item.props)} + {loading ?
Loading...
: React.createElement(SettingComponent, item.props)}
{delayedValue &&
diff --git a/packages/app/src/cores/render/index.jsx b/packages/app/src/cores/render/index.jsx index 6f347245..f9995dfe 100644 --- a/packages/app/src/cores/render/index.jsx +++ b/packages/app/src/cores/render/index.jsx @@ -106,11 +106,11 @@ export class RouteRender extends EvitePureComponent { export class RenderCore extends Core { progressBar = progressBar.configure({ parent: "html", showSpinner: false }) - + publicMethods = { bindContexts: RenderCore.bindContexts, } - + initialize = () => { const defaultTransitionDelay = 150 diff --git a/packages/app/src/cores/style/index.jsx b/packages/app/src/cores/style/index.jsx index 42b9daa9..68dd22cd 100644 --- a/packages/app/src/cores/style/index.jsx +++ b/packages/app/src/cores/style/index.jsx @@ -12,6 +12,11 @@ export default class StyleCore extends Core { currentVariant = null events = { + "app.autoDarkMode": (value) => { + if (value === true) { + this.handleAutoColorScheme() + } + }, "theme.applyVariant": (value) => { this.applyVariant(value) this.setVariant(value) @@ -28,11 +33,21 @@ export default class StyleCore extends Core { publicMethods = { style: this } - + static get currentVariant() { return document.documentElement.style.getPropertyValue("--themeVariant") } + handleAutoColorScheme() { + const prefered = window.matchMedia("(prefers-color-scheme: light)") + + if (window.app.settings.get("app.auto_darkMode") && !prefered.matches) { + this.applyVariant("dark") + }else { + this.applyVariant("false") + } + } + initialize = async () => { let theme = this.getStoragedTheme() @@ -61,6 +76,11 @@ export default class StyleCore extends Core { // apply variation this.applyVariant(variantKey) + + // handle auto prefered color scheme + if (window.app.settings.get("app.auto_darkMode")) { + window.matchMedia("(prefers-color-scheme: light)").addListener(this.handleAutoColorScheme) + } } getRootVariables = () => {