diff --git a/.gitignore b/.gitignore index bee1cf61..b0c6d4e2 100755 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ # production /dist - +/.debug # misc .DS_Store @@ -18,3 +18,4 @@ /src/.umi-production /src/.umi-test /.env.local +.debug/4.11/.editorconfig diff --git a/config/index.js b/config/index.js index 2c531244..c5905bcf 100644 --- a/config/index.js +++ b/config/index.js @@ -10,7 +10,13 @@ module.exports = { DarkLogoPath: '/dark_logo.svg', api_prefix: 'ycorejs_apiv3', - app_settings_storage: 'app_settings' + app_settings_storage: 'app_settings', + + appTheme_container: 'app_theme', + appTheme_desiredContrast: 4.5, + // Contrast level AA = 4.5, Level AAA = 7 + // Reference: https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0&showtechniques=143#qr-visual-audio-contrast-contrast + }, i18n: { diff --git a/globals/settings.js b/globals/settings.js index 1aabcb77..e96c7d6f 100755 --- a/globals/settings.js +++ b/globals/settings.js @@ -30,4 +30,5 @@ module.exports = [ title: 'Overlay loose focus', description: 'Close the overlay when loose focus', }, + ] diff --git a/globals/sidebar_menu.js b/globals/sidebar_menu.js index 335315b9..428b3661 100644 --- a/globals/sidebar_menu.js +++ b/globals/sidebar_menu.js @@ -7,7 +7,8 @@ let MenuList = [ path: '/main', require: 'login', icon: , - mobile: 'true' + mobile: true, + desktop: false }, { id: 'explore', @@ -15,7 +16,8 @@ let MenuList = [ path: '/explore', require: 'login', icon: , - mobile: 'true' + mobile: true, + desktop: true }, { id: 'saves', @@ -23,7 +25,8 @@ let MenuList = [ path: '/saves', require: 'login', icon: , - mobile: 'false' + mobile: false, + desktop: true }, { id: 'messages', @@ -31,7 +34,8 @@ let MenuList = [ path: '/messages', require: 'login', icon: , - mobile: 'false' + mobile: true, + desktop: true }, { id: 'notifications', @@ -39,7 +43,8 @@ let MenuList = [ path: '/notifications', require: 'login', icon: , - mobile: 'false' + mobile: true, + desktop: true }, ] diff --git a/globals/theme_settings.js b/globals/theme_settings.js new file mode 100644 index 00000000..cc5fbb92 --- /dev/null +++ b/globals/theme_settings.js @@ -0,0 +1,35 @@ +import * as Icons from 'components/Icons' + +export default [ + { + id: 'backgroundImage', + icon: , + title: 'Background Image', + description: 'Change the background of the app', + }, + { + id: 'backgroundColor', + icon: , + title: 'Overlay Color', + description: 'Description blah blah', + }, + { + id: 'color', + icon: , + title: 'Colors', + description: 'Texts, Buttons, Sliders ...etc', + }, + { + id: 'fontSize', + icon: , + title: 'Sizes', + description: 'Zoom?', + }, + { + id: 'darkmode', + icon: , + title: 'Dark Mode', + description: 'Yeaah, no more daying', + } +] + \ No newline at end of file diff --git a/package.json b/package.json index e6760fff..c220dc47 100755 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4", "title": "Comty™", "DevBuild": true, - "version": "0.7.24", + "version": "0.7.31", "stage": "dev-pre", "description": "", "author": "RageStudio", @@ -24,20 +24,22 @@ "*.{js,jsx,less,md,json}": [ "prettier --write" ], - "*.ts?(x)": [ + "*.ts?(x)": [ "prettier --parser=typescript --write" ] }, "dependencies": { + "@icons-pack/react-simple-icons": "^3.8.0", "@lingui/cli": "^2.9.1", "@lingui/loader": "^2.9.1", "@lingui/react": "^2.9.1", "@material-ui/core": "^4.9.9", "@material-ui/icons": "^4.9.1", "@ragestudio/ycorejs-lib": "^0.1.21", - "antd": "^4.3.5", + "antd": "^4.5.1", "axios": "^0.19.2", "babel-core": "^6.26.3", + "big-json": "^3.1.0", "classnames": "^2.2.6", "colorthief": "^2.3.0", "dva-model-extend": "^0.1.2", @@ -70,6 +72,7 @@ "react-scripts": "^3.4.1", "react-transition-group": "^4.4.1", "react-virtualized": "^9.21.2", + "simple-icons": "^3.3.0", "stack-trace": "0.0.10", "store": "^2.0.12", "styled-components": "^5.1.1", diff --git a/public/logo.svg b/public/logo.svg index 72f77e0c..024d0503 100755 --- a/public/logo.svg +++ b/public/logo.svg @@ -1,10 +1,10 @@ diff --git a/src/components/Icons/custom.js b/src/components/Icons/custom.js index 3953bb42..b233ac18 100644 --- a/src/components/Icons/custom.js +++ b/src/components/Icons/custom.js @@ -5,4 +5,4 @@ export const Patreon = () => ( -) \ No newline at end of file +) diff --git a/src/components/Icons/index.js b/src/components/Icons/index.js index 049e1e42..72af797d 100644 --- a/src/components/Icons/index.js +++ b/src/components/Icons/index.js @@ -1,4 +1,4 @@ export * from 'feather-reactjs' export * from '@ant-design/icons' export * from './custom' - +export * from '@icons-pack/react-simple-icons' diff --git a/src/components/Layout/ControlBar/index.js b/src/components/Layout/ControlBar/index.js index efe3de80..b3e83e82 100644 --- a/src/components/Layout/ControlBar/index.js +++ b/src/components/Layout/ControlBar/index.js @@ -16,51 +16,45 @@ const animationStyles = { }, } -export function SetControls(e) { - window.ControlComponent.DummySetControls(e) - return -} -export function CloseControls() { - window.ControlComponent.DummyCloseControls() - return +export const control = { + set: (e) => { + if (!window.ControlComponent.state.active) { + window.ControlComponent.setState({ fadein: true }) + } + window.ControlComponent.setState({ active: true, render: e }) + }, + close: () => { + window.ControlComponent.setState({ fadein: false }) + setTimeout(() => window.ControlComponent.setState({ active: false, render: null }), 1000) + } } -class Control extends React.PureComponent { +export default class Control extends React.PureComponent { constructor(props) { - super(props) - window.ControlComponent = this + super(props); this.state = { - Show: false, - FadeIN: true, - } - } - set = e => { - if (this.state.Show == false) { - this.setState({ FadeIN: true }) - } - this.setState({ Show: true, RenderFragment: e }) - } - close() { - this.setState({ FadeIN: false }) - setTimeout(() => this.setState({ Show: false, RenderFragment: null }), 1000) + active: false, + fadein: true, + }; + window.ControlComponent = this; } + render() { - const { RenderFragment, Show, FadeIN } = this.state + const { render, active, fadein } = this.state const isMobile = this.props.mobile? this.props.mobile : false - return Show ? ( + return active ? (
- {RenderFragment} + {render}
) : null } } -export default Control diff --git a/src/components/Layout/Overlay/components/cards/__searchBar.less b/src/components/Layout/Overlay/components/cards/__searchBar.less index 4d77eaf1..8f3ab5da 100644 --- a/src/components/Layout/Overlay/components/cards/__searchBar.less +++ b/src/components/Layout/Overlay/components/cards/__searchBar.less @@ -10,7 +10,7 @@ background-color: transparent; border: 0!important; outline: 0!important; - color: #273346; + color: @AppTheme_global_color_accent; padding: 0 0 0 48px; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 56.966 56.966' fill='%23c1c7cd'%3e%3cpath d='M55.146 51.887L41.588 37.786A22.926 22.926 0 0046.984 23c0-12.682-10.318-23-23-23s-23 10.318-23 23 10.318 23 23 23c4.761 0 9.298-1.436 13.177-4.162l13.661 14.208c.571.593 1.339.92 2.162.92.779 0 1.518-.297 2.079-.837a3.004 3.004 0 00.083-4.242zM23.984 6c9.374 0 17 7.626 17 17s-7.626 17-17 17-17-7.626-17-17 7.626-17 17-17z'/%3e%3c/svg%3e"); background-repeat: no-repeat; @@ -20,7 +20,7 @@ font-weight: 600; font-size: 15px; &::placeholder { - color: #a2a2a2; + color: @AppTheme_global_color_accent; } transition: all 150ms ease-in-out; diff --git a/src/components/Layout/Overlay/index.tsx b/src/components/Layout/Overlay/index.tsx index ffa16241..245913a9 100644 --- a/src/components/Layout/Overlay/index.tsx +++ b/src/components/Layout/Overlay/index.tsx @@ -117,7 +117,7 @@ export default class Overlay extends React.PureComponent { } return( -
<__searchBar />
yeah
} /> +
<__searchBar />
} /> ) } diff --git a/src/components/Layout/Sider/default/index.js b/src/components/Layout/Sider/default/index.js index 4ecb0a02..2e998812 100644 --- a/src/components/Layout/Sider/default/index.js +++ b/src/components/Layout/Sider/default/index.js @@ -15,12 +15,12 @@ export default class Sider_Default extends React.PureComponent { renderMenus(data){ return data.map(e => { - return( + return e.desktop? ( {e.icon} {e.title} - ) + ) : null }) } @@ -35,7 +35,7 @@ export default class Sider_Default extends React.PureComponent { >
handleClickMenu({key: '/'})} + onClick={() => handleClickMenu({key: ''})} src={logo} />
diff --git a/src/components/Layout/Sider/default/index.less b/src/components/Layout/Sider/default/index.less index 758f9e44..caf9bdf9 100644 --- a/src/components/Layout/Sider/default/index.less +++ b/src/components/Layout/Sider/default/index.less @@ -3,9 +3,10 @@ .left_sider_wrapper { border-color: transparent; - font-size: 13px; - font-family: @__Global_general_font_family; - + font-size: 15px; + font-family: @__Global_texted_font; + font-weight: 600; + width: 100%; height: 100vh; @@ -15,19 +16,13 @@ background-color: transparent; backdrop-filter: blur(2px); - &.matchColor{ - h1,h2,h3,h4,h5,span,p,svg { - color: unset; - filter: invert(1); - } - } - - :global { .ant-layout-sider { background-color: transparent; float: right; .ant-menu { + font-weight: 700; + color: @AppTheme_global_color; vertical-align: middle; // margin: 0 0 0 5px; } @@ -45,9 +40,10 @@ border-radius: 8px 8px 8px 8px; transform: translate(10px,0); - // background: linear-gradient(49deg, rgba(255,255,255,1) 32%, rgba(255, 255, 255, 0) 100%); + // background: linear-gradient(49deg, rgba(255,255,255,1) 32%, rgba(255, 255, 255, 0) 100%); backdrop-filter: blur(2px); - border-left: 10px #FFCC00 solid; + // border-left: 10px @app_accent_gradient solid; + box-shadow: -2px 2px 1px 0 rgba(51, 51, 51, 0.13); color: rgb(102, 102, 102); } @@ -76,7 +72,7 @@ cursor: pointer; img{ - margin: 15px 0 0 0; + margin: 15px 0 0 22px; max-height: 70px; height: 5vh; width: 5vh; @@ -137,7 +133,7 @@ } .ant-menu-dark .ant-menu-item a { - color: rgb(197, 197, 197); + color: @AppTheme_global_color; } } } diff --git a/src/components/Layout/Sider/index.js b/src/components/Layout/Sider/index.js index 1db8c9b8..b1150f6d 100755 --- a/src/components/Layout/Sider/index.js +++ b/src/components/Layout/Sider/index.js @@ -9,7 +9,7 @@ import Sider_Default from './default' class Sider extends React.PureComponent { handleClickMenu = e => { - router.go(e.key) + router.go(`/${e.key}`) } render() { diff --git a/src/components/Layout/Sider/mobile/index.js b/src/components/Layout/Sider/mobile/index.js index 42445532..733d4bdb 100644 --- a/src/components/Layout/Sider/mobile/index.js +++ b/src/components/Layout/Sider/mobile/index.js @@ -6,11 +6,10 @@ import styles from './index.less' export default class Sider_Mobile extends React.PureComponent { renderMenus(data){ - // filter by item with mobile support return data.map(e => { - return( + return e.mobile? ( {e.icon} - ) + ) : null }) } diff --git a/src/core/index.js b/src/core/index.js index 120a21a7..3146011d 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -272,11 +272,11 @@ export function setLocale(language) { export function get_value(source,key){ if( !key || !source ) return false try { - const map = source.map(item => { - return item.id === key? item.value : null + const find = source.find(item => { + return item.id === key }) - const fr = map.filter(Boolean) - return fr.toString() + return find.value + } catch (error) { return false diff --git a/src/core/libs/errorhandler/index.js b/src/core/libs/errorhandler/index.js index 61072032..b43d239c 100644 --- a/src/core/libs/errorhandler/index.js +++ b/src/core/libs/errorhandler/index.js @@ -1,2 +1,14 @@ +import { notify } from 'core/libs/interface/notify' +import verbosity from 'core/libs/verbosity' + // STRINGS -export const OVERLAY_BADPOSITION = `Invalid overlay position! Was expected "primary" or "secondary"` \ No newline at end of file +export const OVERLAY_BADPOSITION = `Invalid overlay position! Was expected "primary" or "secondary"` +export const INTERNAL_PROCESS_FAILED = `An internal error has occurred! ` +// HANDLERS +export const onError = { + internal_proccess: (...rest) => { + verbosity.error(...rest) + notify.warn(INTERNAL_PROCESS_FAILED, ...rest) + return false + } +} \ No newline at end of file diff --git a/src/core/libs/json_prune/index.js b/src/core/libs/json_prune/index.js new file mode 100644 index 00000000..27a01fc9 --- /dev/null +++ b/src/core/libs/json_prune/index.js @@ -0,0 +1,161 @@ +// JSON.prune : a function to stringify any object without overflow +// two additional optional parameters : +// - the maximal depth (default : 6) +// - the maximal length of arrays (default : 50) +// You can also pass an "options" object. +// examples : +// var json = JSON.prune(window) +// var arr = Array.apply(0,Array(1000)); var json = JSON.prune(arr, 4, 20) +// var json = JSON.prune(window.location, {inheritedProperties:true}) +// Web site : http://dystroy.org/JSON.prune/ +// JSON.prune on github : https://github.com/Canop/JSON.prune +// This was discussed here : http://stackoverflow.com/q/13861254/263525 +// The code is based on Douglas Crockford's code : https://github.com/douglascrockford/JSON-js/blob/master/json2.js +// No effort was done to support old browsers. JSON.prune will fail on IE8. + 'use strict'; + + var DEFAULT_MAX_DEPTH = 6; + var DEFAULT_ARRAY_MAX_LENGTH = 50; + var DEFAULT_PRUNED_VALUE = '"-pruned-"'; + var seen; // Same variable used for all stringifications + var iterator; // either forEachEnumerableOwnProperty, forEachEnumerableProperty or forEachProperty + + // iterates on enumerable own properties (default behavior) + var forEachEnumerableOwnProperty = function(obj, callback) { + for (var k in obj) { + if (Object.prototype.hasOwnProperty.call(obj, k)) callback(k); + } + }; + // iterates on enumerable properties + var forEachEnumerableProperty = function(obj, callback) { + for (var k in obj) callback(k); + }; + // iterates on properties, even non enumerable and inherited ones + // This is dangerous + var forEachProperty = function(obj, callback, excluded) { + if (obj==null) return; + excluded = excluded || {}; + Object.getOwnPropertyNames(obj).forEach(function(k){ + if (!excluded[k]) { + callback(k); + excluded[k] = true; + } + }); + forEachProperty(Object.getPrototypeOf(obj), callback, excluded); + }; + + Object.defineProperty(Date.prototype, "toPrunedJSON", {value:Date.prototype.toJSON}); + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }; + + function quote(string) { + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' + ? c + : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + var prune = function (value, depthDecr, arrayMaxLength) { + var prunedString = DEFAULT_PRUNED_VALUE; + var replacer; + if (typeof depthDecr == "object") { + var options = depthDecr; + depthDecr = options.depthDecr; + arrayMaxLength = options.arrayMaxLength; + iterator = options.iterator || forEachEnumerableOwnProperty; + if (options.allProperties) iterator = forEachProperty; + else if (options.inheritedProperties) iterator = forEachEnumerableProperty + if ("prunedString" in options) { + prunedString = options.prunedString; + } + if (options.replacer) { + replacer = options.replacer; + } + } else { + iterator = forEachEnumerableOwnProperty; + } + seen = []; + depthDecr = depthDecr || DEFAULT_MAX_DEPTH; + arrayMaxLength = arrayMaxLength || DEFAULT_ARRAY_MAX_LENGTH; + function str(key, holder, depthDecr) { + var i, k, v, length, partial, value = holder[key]; + + if (value && typeof value === 'object' && typeof value.toPrunedJSON === 'function') { + value = value.toPrunedJSON(key); + } + if (value && typeof value.toJSON === 'function') { + value = value.toJSON(); + } + + switch (typeof value) { + case 'string': + return quote(value); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'boolean': + case 'null': + return String(value); + case 'object': + if (!value) { + return 'null'; + } + if (depthDecr<=0 || seen.indexOf(value)!==-1) { + if (replacer) { + var replacement = replacer(value, prunedString, true); + return replacement===undefined ? undefined : ''+replacement; + } + return prunedString; + } + seen.push(value); + partial = []; + if (Object.prototype.toString.apply(value) === '[object Array]') { + length = Math.min(value.length, arrayMaxLength); + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value, depthDecr-1) || 'null'; + } + v = '[' + partial.join(',') + ']'; + if (replacer && value.length>arrayMaxLength) return replacer(value, v, false); + return v; + } + if (value instanceof RegExp) { + return quote(value.toString()); + } + iterator(value, function(k) { + try { + v = str(k, value, depthDecr-1); + if (v) partial.push(quote(k) + ':' + v); + } catch (e) { + // this try/catch due to forbidden accessors on some objects + } + }); + return '{' + partial.join(',') + '}'; + case 'function': + case 'undefined': + return replacer ? replacer(value, undefined, false) : undefined; + } + } + return str('', {'': value}, depthDecr); + }; + + prune.log = function() { + console.log.apply(console, Array.prototype.map.call(arguments, function(v) { + return JSON.parse(JSON.prune(v)); + })); + }; + prune.forEachProperty = forEachProperty; // you might want to also assign it to Object.forEachProperty + + export default prune \ No newline at end of file diff --git a/src/core/libs/settings/index.js b/src/core/libs/settings/index.js index 97cee9fd..22be9e58 100644 --- a/src/core/libs/settings/index.js +++ b/src/core/libs/settings/index.js @@ -1,7 +1,6 @@ import { defaults, app_config } from 'config' import { get_value } from 'core' - export function parseLocalStorage(){ const a = localStorage.getItem(app_config.app_settings_storage) try { @@ -10,41 +9,71 @@ export function parseLocalStorage(){ console.log(error) } } - -export function settingValue(key){ - let tmp = []; - - const keys = Object.keys(defaults) - const values = Object.values(defaults) - const length = keys.length - - for (let i = 0; i < length; i++) { - const storagedValue = get_value(parseLocalStorage(), keys[i]) - tmp[keys[i]] = (storagedValue? storagedValue : values[i]) - } - - if (key) { - return tmp[key] - } - - return tmp +export function newSetting(key, value) { + let setting = {} + setting.id = key + setting.value = value + return [setting] } -export const storage = { +export const settings = { get: (key) => { - return settingValue(key) + let tmp = []; + + const keys = Object.keys(defaults) + const values = Object.values(defaults) + const length = keys.length + + for (let i = 0; i < length; i++) { + if(parseLocalStorage()){ + const storagedValue = parseLocalStorage().find(item => { + return item.id === keys[i] + }) + if (typeof(storagedValue) == 'undefined') { + tmp[keys[i]] = values[i] + }else{ + tmp[keys[i]] = storagedValue.value + } + } + else{ + tmp[keys[i]] = values[i] + } + } + + if (key) { + return tmp[key] + } + + return tmp }, set: (key, value) => { - const storaged = parseLocalStorage() - const updated = storaged.map(element => { - return element.id === key? Object.assign(element, { value: value }) : element - }); + let tmp + let data = parseLocalStorage() + + if (data) { + const finded = data.find(element => { + return element.id === key + }) + if (!finded) { + const parsed = data.concat(newSetting(key, value)) + tmp = parsed + } else { + const updated = data.map(element => { + return element.id === key? Object.assign(element, { value: value }) : element + }) + tmp = updated + } + }else{ + tmp = newSetting(key, value) + } + data = tmp try { - localStorage.setItem( app_config.app_settings_storage, JSON.stringify(updated) ) + localStorage.setItem( app_config.app_settings_storage, JSON.stringify(data) ) } catch (error) { console.log(error) + return false } } } -export default settingValue \ No newline at end of file +export default (e) => settings.get(e) \ No newline at end of file diff --git a/src/core/libs/style/index.js b/src/core/libs/style/index.js new file mode 100644 index 00000000..24deb909 --- /dev/null +++ b/src/core/libs/style/index.js @@ -0,0 +1,237 @@ +import store from 'store'; +import { app_config } from 'config'; +import verbosity from 'core/libs/verbosity' +import * as errorHandlers from 'core/libs/errorhandler' +import { negate } from 'lodash'; + +const { appTheme_desiredContrast, appTheme_container } = app_config + +export const theme = { + get: (key) => { + if(!localStorage.getItem(appTheme_container)) return false + const raw = store.get(appTheme_container) + let container = [] + if (raw) { + raw.forEach((e)=>{ + container[e.key] = e.value + }) + return container + } + return null + }, + set: (data) => { + if (!data || data.length > 2) return false + try { + let mix = [] + const obj = Object.entries(data) + obj.forEach((e) => { + mix.push({key: e[0], value: e[1]}) + }) + return store.set(appTheme_container, mix) + } catch (error) { + console.log(error) + return false + } + }, + raw: () => { + return store.get(appTheme_container) + } +} + +export function get_style_rule_value(selector, style) +{ + const selector_lowercase = selector.toLowerCase(); + const selector_parsed = selector_lowercase.substr(0,1)==='.' ? selector_lowercase.substr(1) : '.'+selector_lowercase; + + for (let i = 0; i < document.styleSheets.length; i++) + { + let styleSheet = document.styleSheets[i]; + let rules = styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules; + + for (var j = 0; j < rules.length; j++) + { + if (rules[j].selectorText) + { + var check = rules[j].selectorText.toLowerCase(); + switch (check) + { + case selector_lowercase : + case selector_parsed : return rules[j].style[style]; + } + } + } + } + } + +export function getOptimalOpacityFromIMG(payload, callback) { + const { textColor, overlayColor, img } = payload; + + verbosity.debug(payload) + let canvas = document.createElement('canvas'); + let image = new Image(); + + image.src = img + image.setAttribute('crossOrigin', ''); + image.onload = () =>{ + const imagePixelColors = getImagePixelColorsUsingCanvas(canvas, image); + if(imagePixelColors){ + const worstContrastColorInImage = getWorstContrastColorInImage(textColor, imagePixelColors); + const optimalOpacity = findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast); + return callback(optimalOpacity) + }else{ + return false + } + + } + +} + +export function getImagePixelColorsUsingCanvas(canvas, image) { + let imagePixelColors = null; + const ctx = canvas.getContext('2d'); + + canvas.height = getCanvasHeightToMatchImageProportions(canvas, image); + + const sourceImageCoordinates = [0, 0, image.width, image.height]; + const destinationCanvasCoordinates = [0, 0, canvas.width, canvas.height]; + + ctx.drawImage( + image, + ...sourceImageCoordinates, + ...destinationCanvasCoordinates + ); + + // Remember getImageData only works for same-origin or cross-origin-enabled images. + // See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more info. + try { + imagePixelColors = ctx.getImageData(...destinationCanvasCoordinates); + } catch (error) { + return errorHandlers.onError.internal_proccess(error) + } + + if (imagePixelColors) { + return imagePixelColors + } +} + +export function getCanvasHeightToMatchImageProportions(canvas, image) { + return (image.height / image.width) * canvas.width; +} + +export function getWorstContrastColorInImage(textColor, imagePixelColors) { + let worstContrastColorInImage; + let worstContrast = Infinity; + + for (let i = 0; i < imagePixelColors.data.length; i += 4) { + let pixelColor = { + r: imagePixelColors.data[i], + g: imagePixelColors.data[i + 1], + b: imagePixelColors.data[i + 2], + }; + + let contrast = getContrast(textColor, pixelColor); + if(contrast < worstContrast) { + worstContrast = contrast; + worstContrastColorInImage = pixelColor; + } + } + return worstContrastColorInImage; +} + +export function getContrast(color1, color2) { + const color1_luminance = getLuminance(color1); + const color2_luminance = getLuminance(color2); + + const lighterColorLuminance = Math.max(color1_luminance, color2_luminance); + const darkerColorLuminance = Math.min(color1_luminance, color2_luminance); + + const contrast = (lighterColorLuminance + 0.05) / (darkerColorLuminance + 0.05); + return contrast; +} + +export function getLuminance({r,g,b}) { + return (0.2126 * getLinearRGB(r) + 0.7152 * getLinearRGB(g) + 0.0722 * getLinearRGB(b)); +} + +export function getLinearRGB(primaryColor_8bit) { + // First convert from 8-bit rbg (0-255) to standard RGB (0-1) + const primaryColor_sRGB = convert_8bit_RGB_to_standard_RGB(primaryColor_8bit); + + // Then convert from sRGB to linear RGB so we can use it to calculate luminance + const primaryColor_RGB_linear = convert_standard_RGB_to_linear_RGB(primaryColor_sRGB); + + return primaryColor_RGB_linear; +} + +export function convert_8bit_RGB_to_standard_RGB(primaryColor_8bit) { + return primaryColor_8bit / 255; +} + +export function convert_standard_RGB_to_linear_RGB(primaryColor_sRGB) { + const primaryColor_linear = primaryColor_sRGB < 0.03928 ? + primaryColor_sRGB/12.92 : + Math.pow((primaryColor_sRGB + 0.055) / 1.055, 2.4); + return primaryColor_linear; +} + +export function getTextContrastWithImagePlusOverlay({textColor, overlayColor, imagePixelColor, overlayOpacity}) { + const colorOfImagePixelPlusOverlay = mixColors(imagePixelColor, overlayColor, overlayOpacity); + const contrast = getContrast(textColor, colorOfImagePixelPlusOverlay); + return contrast; +} + +export function mixColors(baseColor, overlayColor, overlayOpacity) { + const mixedColor = { + r: baseColor.r + (overlayColor.r - baseColor.r) * overlayOpacity, + g: baseColor.g + (overlayColor.g - baseColor.g) * overlayOpacity, + b: baseColor.b + (overlayColor.b - baseColor.b) * overlayOpacity, + } + return mixedColor; +} + +export function findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast) { + const opacityGuessRange = { + lowerBound: 0, + midpoint: 0.5, + upperBound: 1, + }; + + let numberOfGuesses = 0; + const maxGuesses = 8; + const opacityLimit = 0.99; + + while (numberOfGuesses < maxGuesses) { + numberOfGuesses++; + const currentGuess = opacityGuessRange.midpoint; + + const contrastOfGuess = getTextContrastWithImagePlusOverlay({ + textColor, + overlayColor, + imagePixelColor: worstContrastColorInImage, + overlayOpacity: currentGuess, + }); + + const isGuessTooLow = contrastOfGuess < appTheme_desiredContrast; + const isGuessTooHigh = contrastOfGuess > appTheme_desiredContrast; + + if (isGuessTooLow) { + opacityGuessRange.lowerBound = currentGuess; + } + else if (isGuessTooHigh) { + opacityGuessRange.upperBound = currentGuess; + } + + const newMidpoint = ((opacityGuessRange.upperBound - opacityGuessRange.lowerBound) / 2) + opacityGuessRange.lowerBound; + opacityGuessRange.midpoint = newMidpoint; + } + + const optimalOpacity = opacityGuessRange.midpoint; + + if (optimalOpacity > opacityLimit) { + return opacityLimit; + } + + return optimalOpacity; +} + + \ No newline at end of file diff --git a/src/core/libs/verbosity/index.js b/src/core/libs/verbosity/index.js index e44963cb..bd7ef045 100644 --- a/src/core/libs/verbosity/index.js +++ b/src/core/libs/verbosity/index.js @@ -1,7 +1,7 @@ -import { storage } from 'core/libs/settings' +import settings from 'core/libs/settings' import stackTrace from 'stack-trace' // import path from 'path' -const verbosity_enabled = storage.get('verbosity') +const verbosity_enabled = settings('verbosity') const verbosity = { log: (...cont) => { diff --git a/src/layouts/PrimaryLayout.js b/src/layouts/PrimaryLayout.js index 9bf584f0..363717aa 100755 --- a/src/layouts/PrimaryLayout.js +++ b/src/layouts/PrimaryLayout.js @@ -12,6 +12,7 @@ import store from 'store' import classnames from 'classnames' import { app_config } from 'config' +import { theme } from 'core/libs/style' import * as antd from 'antd' import styles from './PrimaryLayout.less' @@ -23,12 +24,13 @@ const { Sider, Control, Overlay } = MyLayout @connect(({ app, loading }) => ({ app, loading })) class PrimaryLayout extends React.PureComponent { constructor(props) { - super(props) - window.PrimaryComponent = this + super(props); this.state = { collapsed: app_config.default_collapse_sider ? true : false, isMobile: false, - } + }, + window.PrimaryComponent = this; + } componentDidMount() { @@ -52,10 +54,25 @@ class PrimaryLayout extends React.PureComponent { store.set('collapsed', !fromStore) } - Swapper = { - - } + renderThemeComponents() { + const currentTheme = theme.get() + if (!currentTheme) return false + if (currentTheme.backgroundImage) { + return currentTheme.backgroundImage.active?
: null + } + } render() { const { location, dispatch, children } = this.props const { collapsed, isMobile } = this.state @@ -77,6 +94,8 @@ class PrimaryLayout extends React.PureComponent { return ( + + {this.renderThemeComponents()}
diff --git a/src/models/app.js b/src/models/app.js index e451925b..8b336508 100755 --- a/src/models/app.js +++ b/src/models/app.js @@ -26,7 +26,8 @@ export default { controlActive: false, feedOutdated: false, - app_settings: store.get('app_settings'), + app_settings: store.get(app_config.app_settings_storage), + app_theme: store.get(app_config.appTheme_container), notifications: [], locationQuery: {}, @@ -52,7 +53,7 @@ export default { cancelRequest.forEach((value, key) => { if (value.pathname !== window.location.pathname) { - value.cancel(CANCEL_REQUEST_MESSAGE); + value.cancel('cancel request'); cancelRequest.delete(key); } }); @@ -113,6 +114,25 @@ export default { } }); }, + *updateTheme({payload}, {call, put, select}){ + if (!payload) return false; + let tmp = [] + + const keys = Object.keys(payload) + const values = Object.values(payload) + const lenght = keys.length + + for (let i = 0; i < lenght; i++) { + let obj = {} + obj.key = keys[i] + obj.value = values[i] + + tmp[i] = obj + } + + return yield put({ type: 'handleUpdateTheme', payload: tmp }); + + }, }, reducers: { updateState(state, { payload }) { @@ -131,6 +151,12 @@ export default { state.notifications = []; }, + handleUpdateTheme(state, { payload }) { + verbosity.debug(payload) + store.set(app_config.appTheme_container, payload); + state.app_theme = payload + }, + disconnectServices(state) { state.service_valid = false; state.session_valid = false; diff --git a/src/pages/debug/index.js b/src/pages/debug/index.js index 141ad75b..35e8734c 100644 --- a/src/pages/debug/index.js +++ b/src/pages/debug/index.js @@ -14,7 +14,6 @@ import { import classnames from 'classnames'; import { CloseOutlined } from '@ant-design/icons'; import endpoints_list from 'config/endpoints'; -import { Page } from 'components'; import styles from './index.less'; @@ -171,7 +170,7 @@ class RequestPage extends React.Component { const { result, url, method, ParamsKeys, BodyKeys, visible } = this.state; return ( - +
{result}
-
+
); } } diff --git a/src/pages/debug/theme.js b/src/pages/debug/theme.js new file mode 100644 index 00000000..ed51cf58 --- /dev/null +++ b/src/pages/debug/theme.js @@ -0,0 +1,79 @@ +import React from 'react' +import * as antd from 'antd' +import * as themeLIB from 'core/libs/style' + +function getBase64(img, callback) { + const reader = new FileReader() + reader.addEventListener('load', () => callback(reader.result)) + reader.readAsDataURL(img) + } + +export default class themedebug extends React.PureComponent { + state = { + textColor: {r: '255', g: '255', b: '255'}, + overlayColor: {r: '0', g: '0', b: '0'}, + + optimal: null, + file: null, + fileURL: null, + } + ToogleUpload() { + this.setState({ uploader: !this.state.uploader }) + } + handleDeleteFile = () => { + this.setState({ fileURL: null }) + } + handleFileUpload = info => { + if (info.file.status === 'uploading') { + this.setState({ loading: true }) + return + } + if (info.file.status === 'done') { + this.setState({ file: info.file.originFileObj, uploader: false }) + getBase64(info.file.originFileObj, fileURL => { + this.setState({ fileURL, loading: false }) + }) + } + } + + handleGetOptimal() { + const optimal = themeLIB.getOptimalOpacityFromIMG({ textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: this.state.fileURL }) + this.setState({ optimal: optimal }) + } + + schemeToRGB(values) { + return `rgb(${values.r}, ${values.g}, ${values.b})` + } + + render(){ + + return( +
+ this.handleGetOptimal()}> Get OPACITY + + + Click to Upload + + +
+ {JSON.stringify(this.state.file)}

+ textColor:{JSON.stringify(this.state.textColor)}

+ overlayColor:{JSON.stringify(this.state.overlayColor)}

+ +
+ + +
+

Sample text

+ +
+ + + +
+ ) + } +} \ No newline at end of file diff --git a/src/pages/settings/components/base.js b/src/pages/settings/components/base.js index 0caff7d9..ed1a2cac 100755 --- a/src/pages/settings/components/base.js +++ b/src/pages/settings/components/base.js @@ -1,30 +1,29 @@ import React, { Component, Fragment } from 'react' import { List, Switch, Button, notification, InputNumber } from 'antd' import ListSettings from 'globals/settings' -import ControlBar from 'components/layout/ControlBar' +import { control } from 'components/layout/ControlBar' import verbosity from 'core/libs/verbosity' import * as Icons from 'components/Icons' +import { settings, newSetting } from 'core/libs/settings' class Base extends Component { constructor(props) { - super(props), - (this.state = { - SettingRepo: ListSettings, - forSave: false, - }) + super(props); + this.state = { + list: ListSettings, + }; } - rendersets = item => { - let e = item.type - switch (e) { + renderSetting = item => { + switch (item.type) { case 'switch': return ( this.onChangeSwitch(item)} + checked={settings.get(item.id)} + onChange={() => this.onChange(item)} /> ) case 'numeric': @@ -41,29 +40,6 @@ class Base extends Component { } } - SettingRender = data => { - try { - return ( -
- ( - - - {this.rendersets(item)} - - )} - /> -
- ) - } catch (err) { - return verbosity.log(err) - } - } handleControlBar() { const ListControls = [
@@ -76,7 +52,7 @@ class Base extends Component {
, ] - ControlBar.set(ListControls) + control.set(ListControls) } saveChanges() { @@ -87,39 +63,44 @@ class Base extends Component { description: 'The configuration has been saved, it may for some configuration to make changes you need to reload the application', }) - setTimeout(app._app.refresh(), 1000) - ControlBar.close() + control.close() } - onChangeSwitch(item) { + onChange(item) { try { - this.handleControlBar() - const to = !item.value - const updatedValue = [...this.state.SettingRepo].map(ita => - ita === item ? Object.assign(ita, { value: to }) : ita - ) - this.setState({ SettingRepo: updatedValue, forSave: true }) - verbosity.log(`Changing ${item.SettingID} to value ${to}`) + switch (item.type) { + case 'switch': { + item.to = !settings.get(item.id) + verbosity.debug(`Changing setting (${item.id}: ${settings.get(item.id)}) => ${item.to}`) + settings.set(item.id, item.to) + this.handleChange(item) + + } + case 'numeric': { + + } + default: { + return null + } + } } catch (err) { console.log(err) } } - onChangeNumeric(value, item) { - this.HandleChangeNumeric(value) - } - HandleChangeNumeric(item, value) { + + + handleChange(item) { try { - this.handleControlBar() - console.log(item.SettingID, value) - const updatedValue = [...this.state.SettingRepo].map(ita => - ita === item ? Object.assign(ita, { value: value }) : ita + const updatedValue = this.state.list.map(element => + element.id === item.id ? Object.assign(element, { value: item.to }) : element ) - this.setState({ SettingRepo: updatedValue, forSave: true }) - verbosity.log(`Changing ${item.SettingID} to value ${to}`) + this.setState({ list: updatedValue}) } catch (err) { console.log(err) } } + + render() { return ( @@ -127,7 +108,19 @@ class Base extends Component {

Behaviors

- {this.SettingRender(this.state.SettingRepo)} + ( + + + {this.renderSetting(item)} + + )} + />
) diff --git a/src/pages/settings/components/theme/index.js b/src/pages/settings/components/theme/index.js index b6c46c88..4d366101 100644 --- a/src/pages/settings/components/theme/index.js +++ b/src/pages/settings/components/theme/index.js @@ -1,12 +1,35 @@ import React from 'react' -import * as Feather from 'feather-reactjs' +import * as Icons from 'components/Icons' import * as antd from 'antd' +import themeSettings from 'globals/theme_settings' +import {connect} from 'umi' +import styles from './index.less' +import json_prune from 'core/libs/json_prune' + import { SketchPicker } from 'react-color'; +import { theme, getOptimalOpacityFromIMG, get_style_rule_value } from 'core/libs/style' -import ColorThief from 'colorthief/dist/color-thief' -var colorThief = new ColorThief(); +function getBase64(img, callback) { + const reader = new FileReader() + reader.addEventListener('load', () => callback(reader.result)) + reader.readAsDataURL(img) +} + +function toDataUrl(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.onload = function() { + var reader = new FileReader(); + reader.onloadend = function() { + callback(reader.result); + } + reader.readAsDataURL(xhr.response); + }; + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.send(); +} class BackgroundColor extends React.Component{ state = { @@ -29,30 +52,219 @@ class BackgroundColor extends React.Component{ } } +@connect(({ app }) => ({ app })) class BackgroundImage extends React.Component{ state = { - results: [] + customURL: '', + fileURL: null, + processing: null, + model: { active: false, opacity: null, src: null } } - search(key){ - if (!key) return false - app.api_unsplash.search(key, (res) =>{ - console.log(res) - this.setState({ results: res }) + handleFileUpload = info => { + if (info.file.status === 'uploading') { + return this.setState({ processing: false }) + } + if (info.file.status === 'done') { + this.setState({ processing: true }) + getBase64(info.file.originFileObj, fileURL => { + this.setState({ fileURL: fileURL }) + this.proccessBackground(fileURL) + }) + } + } + + handleCustomURL(url){ + this.setState({ processing: true }) + this.setState({ fileURL: url }) + + toDataUrl(url, fileURL => { + this.proccessBackground(fileURL) }) } - returnString(url){ - this.props.selectImg(`url(${url})`) + + handleUpdate(payload){ + if (!payload) { + payload = this.state.model + } + this.setState({ model: payload, processing: false }) + this.props.dispatch({ + type: 'app/updateTheme', + payload: { backgroundImage: payload } + }); } + + handleErase(){ + this.handleUpdate({}) + } + + handleExport(){ + const string = JSON.stringify(this.state.model) + const exportCodeRender = () => { + if(string.length > 500){ + return
+ +

Hey, this file is too much large!

+ So it couldn't be displayed. +
+ } + return
+ {string} +
+ } + antd.Modal.confirm({ + title:
Your export JSON
, + icon: null, + onOk: () => { + let tmp = document.createElement('a') + tmp.href = `data:text/json;charset=utf-8,${encodeURIComponent(string)}` + tmp.download="export.json" + tmp.click() + }, + okText: <>Download as File , + cancelText: "Done", + content: exportCodeRender(), + }); + + + } + + proccessBackground(data){ + getOptimalOpacityFromIMG({textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: data}, (res) => { + this.handleUpdate({active: true, src: this.state.fileURL, opacity: res}) + }) + } + + schemeToRGB(values){ + const scheme = values || { r: '0', g: '0', b: '0' } + const r = scheme.r || '0' + const g = scheme.g || '0' + const b = scheme.b || '0' + return `rgb(${r}, ${g}, ${b})` + } + + rgbToScheme(rgb){ + const values = rgb.replace(/[^\d,]/g, '').split(','); + return {r: values[0], g: values[1], b: values[2]} + } + + componentDidMount(){ + const storaged = theme.get()["backgroundImage"] + if(storaged){ + this.setState({ model: storaged }) + } + + let textColor = this.rgbToScheme(get_style_rule_value('#root', 'color')) + let overlayColor = this.rgbToScheme(get_style_rule_value('#root', 'backgroundColor')) + + this.setState({ + textColor: textColor, + overlayColor: overlayColor + }) + } + + render(){ + const promiseState = async state => new Promise(resolve => this.setState(state, resolve)); + + const PreviewModel = () => { + return( +
+

Preview

+ { this.state.model.src?
+
+

Sample text

+

Sample text

+

Sample text

+

Sample text

+

Some text here

+

Some text here

+ +
+ +
:

No Background

} +
+ ) + } + return ( <> -

Upload

+
+

Background Image

+
+ +
+
+

Enabled

+ { + promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate()) + }} + checked={this.state.model.active} + /> +
+
+ +

Opacity

+ { + this.setState( + prevState => ({ + model: { + ...prevState.model, + opacity: e/100 + } + }) + ) + }} onAfterChange={() => this.handleUpdate()} value={this.state.model.opacity*100} /> +
+
+

Export Code

+ this.handleExport()}> Export +
+
+

Erase

+ this.handleErase()} okText="Yes" cancelText="No"> + Delete + +
+ +
+ + + -

Unsplash

+ +
+

Upload

+
+
+ Upload from your files
+ + } /> + +
+
+

Or

+
+
+ Upload from URL + this.handleCustomURL(this.state.customURL)} onChange={e => this.setState({ customURL: e.target.value })} value={this.state.customURL} placeholder="http://example.com/my_coolest_image.jpg" /> +
+
+ + {this.state.processing?

Processing image ...

: null} + {this.state.params? JSON.stringify(this.state.params) : null} +
+ + + + {/*

Unsplash

this.search(value)} /> - ( this.returnString(item.urls.full)} src={item.urls.small} /> ) }/> + ( this.returnString(item.urls.full)} src={item.urls.small} /> ) }/> */} ) } @@ -62,96 +274,9 @@ export default class ThemeSettings extends React.PureComponent{ state = { helper_visible: false, helper_fragment: null, - style: [], + style: theme.get(), } - componentDidMount(){ - this.decodeData() - } - - decodeData(){ - const storaged = app.app_theme.getString() - try { - if (storaged) { - this.decode(storaged, (res) => { - this.setState({ style: res }) - }) - } - } catch (error) { - console.log(error) - } - } - - encode(data, callback){ - if (!data) return false - try { - let mix = [] - const obj = Object.entries(data) - obj.forEach((e) => { - mix.push({key: e[0], value: e[1]}) - }) - return callback(JSON.stringify(mix)) - } catch (error) { - console.log(error) - return false - } - } - decode(data, callback){ - if (!data) return false - try { - const scheme = JSON.parse(data) - let mix = {}; - scheme.forEach((e)=>{ - mix[e.key] = e.value - }) - return callback(mix) - } catch (error) { - console.log(error) - return false - } - } - - handleChanges(key, value){ - let { style } = this.state - try { - switch (key) { - case "backgroundImage":{ - if(style.backgroundColor){ - this.handleRemove("backgroudColor") - } - style[key] = value - this.encode(style, (res) => { - app.app_theme.set(res) - this.setPredominantColor(value) - }) - return true - } - case "backgroundColor":{ - if(style.backgroundImage){ - this.handleRemove("backgroundImage") - } - style[key] = value - this.encode(style, (res) => { - app.app_theme.set(res) - this.handleChanges("predominantColor", value) - }) - } - default:{ - style[key] = value - this.encode(style, (res) => { - app.app_theme.set(res) - }) - } - } - this.decodeData() - } catch (error) { - console.log(error) - } - } - - resetStyles(){ - app.app_theme.set([]) - } handleRemove(key){ try { @@ -172,21 +297,6 @@ export default class ThemeSettings extends React.PureComponent{ } - setPredominantColor(furl){ - const _this = this - const img = new Image(); - const url = ((furl.replace("url", "")).substring(1)).slice(0, -1); - - img.crossOrigin = 'Anonymous'; - img.src = url - - img.addEventListener('load', function() { - const color = `rgb(${colorThief.getColor(img)})` - _this.handleChanges("predominantColor", color) - }) - - } - helper = { open: (e) => { this.setState({ helper_visible: true, helper_fragment: e }) @@ -195,25 +305,33 @@ export default class ThemeSettings extends React.PureComponent{ this.setState({ helper_visible: false, helper_fragment: null }) }, backgroundImage: () => { - this.helper.open( this.handleChanges("backgroundImage", i)} />) + this.helper.open() }, backgroundColor: () => { - this.helper.open( this.handleChanges("backgroundColor", i)} />) + this.helper.open() } } + + render(){ + const settingClick = { backgroundImage: () => this.helper.backgroundImage(), backgroundColor: () => this.helper.backgroundColor() } return(
-

Theme

-
- - - -
+

Theme

+ ( + +

{item.icon}{item.title}
{this.state.style[item.id]? "Enabled" : "Disabled"}

+

{item.description}

+
+ )} + /> + div { + width: 100%; + margin: 0 10px; + } +} + +.background_image_uploader{ + width: 100%; + margin: auto; + display: flex; + flex-direction: row; + text-align: center; + + > div { + width: 100%; + margin: 0 10px; + } +} + +.background_image_preview{ + position: relative; + height: 50%; + + img{ + z-index: 9; + width: 100%; + border-radius: 8px; + } + + .text_wrapper{ + position: absolute; + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + + h1{ + font-size: 68px; + } + } +} \ No newline at end of file diff --git a/src/theme/base/index.less b/src/theme/base/index.less index d1e2bf76..3a004d6f 100644 --- a/src/theme/base/index.less +++ b/src/theme/base/index.less @@ -11,12 +11,16 @@ @import './components/Menssaging.less'; @import './components/PostCard.less'; +@AppTheme_global_color: rgb(51, 51, 51); +@AppTheme_global_color_accent: rgb(162, 162, 162); +@AppTheme_global_background: rgb(248, 246, 248); -@__Global_backgroud_image: ""; +@AppTheme_global_color_dark: rgb(162, 162, 162); +@AppTheme_global_background_dark: rgb(51, 51, 51); @__Global_general_font_family: "Poppins", sans-serif; @__Global_texted_font: "Manrope", sans-serif; -@__Global_alternative_font: "Netflix Sans"; +@__Global_alternative_font: "Netflix Sans", sans-serif; @__Global_layout_backgroud: #F8F6F8; @__Global_layout_color: #2d2d2d; @@ -24,22 +28,19 @@ @__Global_layout_transitions-dur: 200ms; @__Global_Components_transitions_dur: 150ms; - @__Global_SwapAnimDuration: 170ms; @__Global_backdrop_backgroud: rgba(158, 158, 158, 0.5); +@app_accent_gradient: linear-gradient(90deg, rgba(237,111,86,1) 0%, rgba(234,89,89,1) 100%); + @transition-ease-in: all 0.3s ease-out; @transition-ease-out: all 0.3s ease-out; @transition-ease-inout: all 150ms ease-in-out; - - - @Overlay_wrapper_hidden_width: 22vw; @Overlay_wrapper_showFull_width: 94.2%; @Overlay_wrapper_showHalf_width: 35vw; -@Overlay_container_bg_background: #F8F6F8; @Overlay_container_1_btn_backgroud: #4c4c4c; @@ -72,12 +73,14 @@ #root { overflow: hidden; + color: @AppTheme_global_color!important; + background-color: @AppTheme_global_background!important; + --accent_color: @AppTheme_global_color_accent; } body { scroll-behavior: smooth; height: 100%; - background-color: transparent; overflow: hidden; @@ -86,9 +89,8 @@ body { font-family: @__Global_texted_font; - background-color: @primary_layout_backgroud; - + } @media (max-width: @bp-small){ diff --git a/src/theme/base/layout/LeftSider.less b/src/theme/base/layout/LeftSider.less index fb1464d7..a07a5313 100644 --- a/src/theme/base/layout/LeftSider.less +++ b/src/theme/base/layout/LeftSider.less @@ -1,4 +1,4 @@ -@left_sider_color: #333; +@left_sider_color: @AppTheme_global_color; @left_sider_sizeIcons: 19px; @left_sider_menu__onhover_backgroud: rgb(80, 80, 80); @left_sider_menu__onhover_color: rgb(80, 80, 80); \ No newline at end of file