update: app theme update

This commit is contained in:
unknown 2020-08-06 02:18:38 +02:00
parent 9b4c382df2
commit 224d46e20f
15 changed files with 214 additions and 355 deletions

2
.gitignore vendored
View File

@ -7,8 +7,6 @@
# production
/dist
/out
/api
# umi
/src/.umi

View File

@ -13,6 +13,7 @@ export default defineConfig({
loading: 'components/Loader/Loader.js',
},
dva: { immer: true },
antd: {},
nodeModulesTransform: {
type: 'none',
},

View File

@ -3,7 +3,7 @@
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
"title": "Comty™",
"DevBuild": true,
"version": "0.4.04",
"version": "0.8.08",
"stage": "dev-pre",
"description": "",
"author": "RageStudio",

View File

@ -25,7 +25,6 @@ export let Swapper = {
}
}
@connect(({ app }) => ({ app }))
export default class Overlay extends React.PureComponent {
constructor(props) {

View File

@ -22,7 +22,7 @@
float: right;
.ant-menu {
font-weight: 700;
color: @AppTheme_global_color;
color: unset;
vertical-align: middle;
// margin: 0 0 0 5px;
}

View File

@ -5,11 +5,19 @@ import verbosity from 'core/libs/verbosity'
export const OVERLAY_BADPOSITION = `Invalid overlay position! Was expected "primary" or "secondary"`
export const INTERNAL_PROCESS_FAILED = `An internal error has occurred! `
export const INVALID_DATA = `A function has been executed with invalid data and has caused an error!`
export const INVALID_PROPS = `Some props failed!`
// HANDLERS
export const onError = {
internal_proccess: (...rest) => {
verbosity.error(...rest)
notify.warn(INTERNAL_PROCESS_FAILED, ...rest)
notify.open({
message: INTERNAL_PROCESS_FAILED,
description:
<div style={{ display: 'flex', flexDirection: 'column', margin: 'auto' }}>
<div style={{ margin: '10px 0' }}> {JSON.stringify(...rest)} </div>
</div>,
})
return false
},
invalid_data: (error, expecting) => {

View File

@ -1,161 +0,0 @@
// 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

View File

@ -16,7 +16,7 @@ const LayoutMap = {
}
@withRouter
@connect(({ loading }) => ({ loading }))
@connect(({ app, loading }) => ({ app, loading }))
class BaseLayout extends PureComponent {
previousPath = ''
@ -33,7 +33,6 @@ class BaseLayout extends PureComponent {
NProgress.done()
this.previousPath = currentPath
}
return (
<Fragment>
<Helmet>

View File

@ -19,10 +19,11 @@ import styles from './PrimaryLayout.less'
const { Content } = antd.Layout
const { Sider, Control, Overlay } = MyLayout
const isActive = (key) => { return key? key.active : false }
@withRouter
@connect(({ app, loading }) => ({ app, loading }))
class PrimaryLayout extends React.PureComponent {
class PrimaryLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
@ -30,7 +31,6 @@ class PrimaryLayout extends React.PureComponent {
isMobile: false,
},
window.PrimaryComponent = this;
}
componentDidMount() {
@ -54,11 +54,41 @@ class PrimaryLayout extends React.PureComponent {
store.set('collapsed', !fromStore)
}
renderThemeComponents() {
render() {
const { location, dispatch, children } = this.props
const { collapsed, isMobile } = this.state
const { onCollapseChange } = this
const currentTheme = theme.get()
if (!currentTheme) return false
if (currentTheme.backgroundImage) {
return currentTheme.backgroundImage.active? <div style={{
const breakpoint = {
xs: '480px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
xxl: '1600px',
}
const SiderProps = {
breakpoint,
isMobile,
collapsed,
onCollapseChange,
theme: isActive(currentTheme["darkmode"])? "dark" : null
}
const OverlayProps = {
breakpoint,
isMobile,
theme: isActive(currentTheme["darkmode"])? "dark" : null
}
window.DarkMode = isActive(currentTheme["darkmode"])? true : false
return (
<React.Fragment>
<Control />
{isActive(currentTheme['backgroundImage'])? <div style={{
backgroundImage: `url(${currentTheme.backgroundImage.src})`,
transition: "all 150ms linear",
position: 'absolute',
@ -69,34 +99,8 @@ class PrimaryLayout extends React.PureComponent {
backgroundPositionY: "center",
overflow: "hidden",
opacity: currentTheme.backgroundImage.opacity
}} /> : null
}
}
render() {
const { location, dispatch, children } = this.props
const { collapsed, isMobile } = this.state
const { onCollapseChange } = this
const SiderProps = {
breakpoint:{
xs: '480px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
xxl: '1600px',
},
isMobile,
collapsed,
onCollapseChange
}
return (
<React.Fragment>
<Control />
{this.renderThemeComponents()}
<antd.Layout id="primaryLayout" className={classnames(styles.primary_layout, {[styles.mobile]: isMobile})}>
}} /> : null}
<antd.Layout id="app" className={isActive(currentTheme['darkmode'])? "dark_mode" : null }>
<Sider {...SiderProps} />
<div className={styles.primary_layout_container}>
<PageTransition
@ -111,7 +115,7 @@ class PrimaryLayout extends React.PureComponent {
</Content>
</PageTransition>
</div>
<Overlay />
<Overlay {...OverlayProps} />
</antd.Layout>
</React.Fragment>
)

View File

@ -1,31 +1,5 @@
@import '~theme/index.less';
.primary_layout {
background-repeat: repeat-x;
background-size: cover;
background-position-y: center;
overflow: hidden;
background-color: transparent;
margin: auto;
height: 100vh;
width: 100vw;
transition: all @__Global_Components_transitions_dur linear;
&.mobile{
>.primary_layout_container {
padding: 0;
overflow-y: overlay;
overflow-x: hidden;
min-width: unset;
}
.primary_layout_content{
padding: 35px 15px 15px 15px;
}
}
}
// PRIMARY LAYOUT
.primary_layout_container {
background-color: transparent;

View File

@ -31,7 +31,6 @@ export default {
app_theme: store.get(app_config.appTheme_container) || [],
notifications: [],
locationQuery: {},
},
subscriptions: {
setup({ dispatch }) {
@ -116,24 +115,28 @@ export default {
});
},
*updateTheme({payload}, {put, select}){
if (!payload) return false;
let container = yield select(state => state.app.app_theme);
let container_2 = []
if (!payload) return false
let container = yield select(state => state.app.app_theme)
let style_keys = []
let tmp = []
const containerlength = Object.entries(container).length
container.forEach((e)=>{style_keys[e.key] = e.value})
if (container && containerlength > 1) {
container.forEach(e =>{
let tmp = {key: e.key}
e.key === payload.key? (tmp.value = payload.value) : (tmp.value = e.value)
container_2.push(tmp)
if(!style_keys[payload.key]){
tmp.push({key: payload.key, value: payload.value})
}
container.forEach((e) => {
let obj = {}
if(e.key === payload.key){
obj = { key: payload.key, value: payload.value }
}else{
obj = { key: e.key, value: e.value }
}
tmp.push(obj)
})
}else{
container_2 = [payload]
}
return container_2? yield put({ type: 'handleUpdateTheme', payload: container_2 }) : null
return tmp? yield put({ type: 'handleUpdateTheme', payload: tmp }) : null
},
},
reducers: {

View File

@ -4,6 +4,9 @@ import * as antd from 'antd'
@connect(({ app }) => ({ app }))
class PageIndex extends React.PureComponent {
constructor(props){
super(props)
}
state = {
app_state: null
}
@ -39,9 +42,10 @@ class PageIndex extends React.PureComponent {
return map
}
return (
<div>
<antd.Card title="APP STATE">
<antd.Card theme={window.DarkMode? "dark" : null} title="APP STATE">
{AppState()}
</antd.Card>

View File

@ -5,39 +5,30 @@ import themeSettings from 'globals/theme_settings'
import {connect} from 'umi'
import styles from './index.less'
import { SketchPicker } from 'react-color';
import { onError } from 'core/libs/errorhandler'
import { theme, getOptimalOpacityFromIMG, get_style_rule_value } from 'core/libs/style'
import { urlToBase64, imageToBase64, arrayToObject } from 'core'
import exportDataAsFile from 'core/libs/interface/export_data'
class BackgroundColor extends React.Component{
state = {
selected: "#fff"
class ThemeConfigurator extends React.Component{
componentDidMount(){
this.applyStoraged()
}
sendChanges(){
this.props.changeColor(this.state.selected)
}
selectColor = (color) =>{
this.setState({selected: color.hex})
}
render(){
return <>
<SketchPicker
color={this.state.selected}
onChangeComplete={this.selectColor}
/>
<antd.Button onClick={() => this.sendChanges()}> Change </antd.Button>
</>
applyStoraged(){
const storaged = theme.get()
if(storaged && this.state){
storaged[this.state.key]? this.setState({ model: storaged[this.state.key]}) : onError.internal_proccess(`"Config key" or "Dispatcher" is missing`)
}
}
@connect(({ app }) => ({ app }))
class DarkMode extends React.Component{
state = {
model: { active: false, autoTime: '' }
}
promiseState = async state => new Promise(resolve => this.setState(state, resolve));
handleUpdate(payload){
if(!this.state.key || !this.props.dispatch) {
return onError.internal_proccess(`"Config key" or "Dispatcher" is missing`)
}
if (!payload) {
payload = this.state.model
}
@ -45,13 +36,33 @@ class DarkMode extends React.Component{
this.props.dispatch({
type: 'app/updateTheme',
payload: {
key: 'darkmode',
key: this.state.key,
value: payload
}
});
}
handleErase(){
this.handleUpdate({})
}
handleExport(){
exportDataAsFile({data: JSON.stringify(this.state.model), type: 'text/json'})
}
}
@connect(({ app }) => ({ app }))
class DarkMode extends ThemeConfigurator{
constructor(props){
super(props),
this.state = {
key: "darkmode",
model: { active: false }
}
}
render(){
const promiseState = async state => new Promise(resolve => this.setState(state, resolve));
return <>
<div>
<h2><Icons.Moon /> Dark Mode</h2>
@ -63,8 +74,7 @@ class DarkMode extends React.Component{
<antd.Switch
checkedChildren="Enabled"
unCheckedChildren="Disabled"
loading={this.state.processing}
onChange={(e) => {promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
onChange={(e) => {this.promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
checked={this.state.model.active}
/>
</div>
@ -76,14 +86,55 @@ class DarkMode extends React.Component{
}
}
@connect(({ app }) => ({ app }))
class Colors extends ThemeConfigurator{
constructor(props){
super(props),
this.state = {
key: "darkmode",
model: { active: false }
}
}
render(){
return <>
<div>
<h2><Icons.Moon /> Dark Mode</h2>
</div>
<div>
<div className={styles.background_image_controls} >
<div>
<h4><Icons.Eye />Enabled</h4>
<antd.Switch
checkedChildren="Enabled"
unCheckedChildren="Disabled"
onChange={(e) => {this.promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
checked={this.state.model.active}
/>
</div>
</div>
</div>
</>
}
}
@connect(({ app }) => ({ app }))
class BackgroundImage extends React.Component{
state = {
class BackgroundImage extends ThemeConfigurator{
constructor(props){
super(props),
this.state = {
key: "backgroundImage",
model: { active: false, opacity: null, src: null },
textColor: this.rgbToScheme(getComputedStyle(document.getElementById("app")).color),
overlayColor: this.rgbToScheme(getComputedStyle(document.getElementById("app")).backgroundColor),
processing: null,
customURL: '',
fileURL: null,
processing: null,
model: { active: false, opacity: null, src: null }
}
}
handleFileUpload = info => {
@ -106,28 +157,6 @@ class BackgroundImage extends React.Component{
})
}
handleUpdate(payload){
if (!payload) {
payload = this.state.model
}
this.setState({ model: payload, processing: false })
this.props.dispatch({
type: 'app/updateTheme',
payload: {
key: 'backgroundImage',
value: payload
}
});
}
handleErase(){
this.handleUpdate({})
}
handleExport(){
exportDataAsFile({data: JSON.stringify(this.state.model), type: 'text/json'})
}
proccessBackground(data){
getOptimalOpacityFromIMG({textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: data}, (res) => {
this.handleUpdate({active: true, src: this.state.fileURL, opacity: res})
@ -135,7 +164,7 @@ class BackgroundImage extends React.Component{
}
schemeToRGB(values){
const scheme = values || { r: '0', g: '0', b: '0' }
const scheme = values? values : { r: '0', g: '0', b: '0' }
const r = scheme.r || '0'
const g = scheme.g || '0'
const b = scheme.b || '0'
@ -147,26 +176,7 @@ class BackgroundImage extends React.Component{
return {r: values[0], g: values[1], b: values[2]}
}
componentDidMount(){
const storaged = theme.get()
if(storaged){
this.setState({ model: storaged["backgroundImage"] })
}
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(
<div>
@ -200,7 +210,7 @@ class BackgroundImage extends React.Component{
checkedChildren="Enabled"
unCheckedChildren="Disabled"
loading={this.state.processing}
onChange={(e) => {promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
onChange={(e) => {this.promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
checked={this.state.model.active}
/>
</div>
@ -266,30 +276,17 @@ class BackgroundImage extends React.Component{
@connect(({ app }) => ({ app }))
export default class ThemeSettings extends React.Component{
state = {
helper_visible: false,
helper_fragment: null,
helper_fragment: null
}
helper = {
open: (e) => {
this.setState({ helper_visible: true, helper_fragment: e })
},
close: () => {
this.setState({ helper_visible: false, helper_fragment: null })
}
}
render(){
const settingClick = {
backgroundImage: () => this.helper.open(<BackgroundImage />),
overlay: () => this.helper.open(<BackgroundColor />) ,
darkmode: () => this.helper.open(<DarkMode />)
}
const isActive = (key) => {
return key? key.active : false
const idToComponent = {
backgroundImage: <BackgroundImage />,
darkmode: <DarkMode />,
color: <Colors />,
}
const handleClick = (key) => key? this.setState({helper_fragment: idToComponent[key]}) : null
const isActive = (key) => { return key? key.active : false }
return(
<div>
<h2><Icons.Layers/> Theme</h2>
@ -298,7 +295,7 @@ export default class ThemeSettings extends React.Component{
dataSource={themeSettings}
renderItem={item => (
<div style={{ margin: '10px 0 10px 0' }} >
<antd.Card size="small" bodyStyle={{ width: '100%' }} style={{ display: "flex", flexDirection: "row", margin: 'auto', borderRadius: '12px' }} hoverable onClick={settingClick[item.id]}>
<antd.Card size="small" bodyStyle={{ width: '100%' }} style={{ display: "flex", flexDirection: "row", margin: 'auto', borderRadius: '12px' }} hoverable onClick={() => handleClick(item.id)}>
<h3>{item.icon}{item.title} <div style={{ float: "right" }}><antd.Tag color={isActive(arrayToObject(this.props.app.app_theme)[item.id])? "green" : "default"} > {isActive(arrayToObject(this.props.app.app_theme)[item.id])? "Enabled" : "Disabled"} </antd.Tag></div></h3>
<p>{item.description}</p>
</antd.Card>
@ -310,8 +307,8 @@ export default class ThemeSettings extends React.Component{
placement="right"
width="700px"
closable={true}
onClose={this.helper.close}
visible={this.state.helper_visible}
onClose={() => this.setState({ helper_fragment: null })}
visible={this.state.helper_fragment? true : false}
>
<React.Fragment>
{this.state.helper_fragment}

View File

@ -2,7 +2,7 @@
@import '../../../node_modules/antd/lib/style/themes/default.less';
// -------- Colors -----------
@primary-color: @blue-6;
@primary-color: #fff;
@info-color: @blue-6;
@success-color: @green-6;
@processing-color: @blue-6;

View File

@ -11,8 +11,9 @@
@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_color: rgb(51, 51, 51);
@AppTheme_global_background: rgb(248, 246, 248);
@AppTheme_global_color_dark: rgb(162, 162, 162);
@ -71,11 +72,45 @@
background-color: transparent;
}
#root {
#app {
overflow: hidden;
color: @AppTheme_global_color!important;
background-color: @AppTheme_global_background!important;
--accent_color: @AppTheme_global_color_accent;
background-repeat: repeat-x;
background-size: cover;
background-position-y: center;
overflow: hidden;
background-color: transparent;
margin: auto;
height: 100vh;
width: 100vw;
transition: all @__Global_Components_transitions_dur linear;
&.dark_mode{
color: @AppTheme_global_color_dark!important;
background-color: @AppTheme_global_background_dark!important;
:global{
.ant-card{
background: @AppTheme_global_background_dark!important;
}
}
}
&.mobile{
>.primary_layout_container {
padding: 0;
overflow-y: overlay;
overflow-x: hidden;
min-width: unset;
}
.primary_layout_content{
padding: 35px 15px 15px 15px;
}
}
}
body {
@ -89,8 +124,6 @@ body {
font-family: @__Global_texted_font;
}
@media (max-width: @bp-small){