mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
refactors for #27 & removed unused code
This commit is contained in:
parent
69384845f5
commit
11d1990536
51
.umirc.js
51
.umirc.js
@ -2,13 +2,10 @@ import { defineConfig } from 'umi';
|
||||
const { resolve, join } = require('path');
|
||||
|
||||
export default defineConfig({
|
||||
hash: false,
|
||||
hash: true,
|
||||
dynamicImport: {
|
||||
loading: 'components/Loader',
|
||||
},
|
||||
// dynamicImport: false,
|
||||
// history: { type: "hash" },
|
||||
|
||||
targets: { ie: 11 },
|
||||
dva: { immer: true, hmr: true },
|
||||
ignoreMomentLocale: true,
|
||||
@ -16,6 +13,9 @@ export default defineConfig({
|
||||
nodeModulesTransform: {
|
||||
type: 'none',
|
||||
},
|
||||
exportStatic: {
|
||||
dynamicRoot: false,
|
||||
},
|
||||
// ssr: {
|
||||
// devServerRender: true,
|
||||
// },
|
||||
@ -45,47 +45,4 @@ export default defineConfig({
|
||||
],
|
||||
],
|
||||
|
||||
// chainWebpack: function(config, { webpack }) {
|
||||
// config.module
|
||||
// .rule('js-in-node_modules')
|
||||
// .exclude.add(/node_modules/)
|
||||
// .end()
|
||||
// config.module
|
||||
// .rule('ts-in-node_modules')
|
||||
// .exclude.add(/node_modules/)
|
||||
// .end()
|
||||
// config.merge({
|
||||
// optimization: {
|
||||
// minimize: true,
|
||||
// splitChunks: {
|
||||
// chunks: 'all',
|
||||
// minSize: 30000,
|
||||
// minChunks: 3,
|
||||
// automaticNameDelimiter: '.',
|
||||
// cacheGroups: {
|
||||
// react: {
|
||||
// name: 'react',
|
||||
// priority: 20,>
|
||||
// test: /[\\/]node_modules[\\/](react|react-dom|react-dom-router)[\\/]/,
|
||||
// },
|
||||
// antd: {
|
||||
// name: 'antd',
|
||||
// priority: 20,
|
||||
// test: /[\\/]node_modules[\\/](antd|@ant-design\/icons)[\\/]/,
|
||||
// },
|
||||
// async: {
|
||||
// chunks: 'async',
|
||||
// minChunks: 2,
|
||||
// name: 'async',
|
||||
// maxInitialRequests: 1,
|
||||
// minSize: 0,
|
||||
// priority: 5,
|
||||
// reuseExistingChunk: true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// },
|
||||
|
||||
})
|
||||
|
@ -10,18 +10,14 @@ module.exports = {
|
||||
DarkFullLogoPath: '/dark_full_logo.svg',
|
||||
DarkLogoPath: '/dark_logo.svg',
|
||||
|
||||
api_interface: 'https://api.ragestudio.net',
|
||||
api_prefix: 'ycorejs_apiv3',
|
||||
|
||||
app_settings_storage: 'app_settings',
|
||||
endpoint_global: 'https://comty.pw',
|
||||
endpoint_v3prefix: 'ycorejs_apiv3',
|
||||
endpoint_websocket: 'eu_es01.ragestudio.net',
|
||||
proxy_local: 'http://localhost:8000',
|
||||
|
||||
session_token_storage: 'cid',
|
||||
session_data_storage: 'data',
|
||||
|
||||
appTheme_container: 'app_theme',
|
||||
storage_appSettings: 'app_settings',
|
||||
storage_authFrame: 'cid',
|
||||
storage_dataFrame: 'data',
|
||||
storage_theme: 'app_theme',
|
||||
|
||||
appTheme_desiredContrast: 7,
|
||||
// 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
|
||||
|
@ -23,7 +23,7 @@ const waitOn = require('wait-on');
|
||||
const { getDoNotDisturb } = require('electron-notification-state');
|
||||
const { app_config } = require("../config");
|
||||
|
||||
let app_path = is.dev()? app_config.proxy_local : `file://${path.join(__dirname, '..', 'renderer')}/index.html`;
|
||||
let app_path = is.dev()? "localhost:8000" : `file://${path.join(__dirname, '..', 'renderer')}/index.html`;
|
||||
let mainWindow;
|
||||
let tray;
|
||||
let watcher;
|
||||
|
20819
package-lock.json
generated
20819
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -24,9 +24,10 @@
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"electronDev": "concurrently \"electron .\" \"npm start\"",
|
||||
"electron": "electron .",
|
||||
"umibuild": "umi build",
|
||||
"build": "npm run build:main && npm run build:renderer",
|
||||
"build:main": "ESLINT=none roadhog build",
|
||||
"build:renderer": "ESLINT=none umi build",
|
||||
"build:main": "roadhog build",
|
||||
"build:renderer": "umi build",
|
||||
"pack": "npm run build && npm run rebuild && build",
|
||||
"pack:dir": "npm run build && npm run rebuild && build --dir",
|
||||
"pack:dirOnly": "build --dir"
|
||||
@ -123,6 +124,6 @@
|
||||
"less-loader": "^7.0.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"typescript": "^3.9.7",
|
||||
"umi": "^3.2.23"
|
||||
"umi": "^3.2.24"
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export function createScreenshotFromElement(element){
|
||||
if (!element) return false
|
||||
html2canvas(element, {
|
||||
useCORS: true,
|
||||
proxy: app_config.proxy_local,
|
||||
proxy: "localhost:8000",
|
||||
scale: 4,
|
||||
backgroundColor: "transparent"
|
||||
}).then(canvas => {
|
||||
@ -91,8 +91,8 @@ export function createScreenshotFromElement(element){
|
||||
}
|
||||
|
||||
export function generatePostURI(id){
|
||||
if(app_config.endpoint_global && id){
|
||||
return `${app_config.endpoint_global}/post/${id}`
|
||||
if(window.location.origin && id){
|
||||
return `${window.location.origin}/post/${id}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { defaults, app_config } from 'config'
|
||||
|
||||
export function parseLocalStorage(){
|
||||
const a = localStorage.getItem(app_config.app_settings_storage)
|
||||
const a = localStorage.getItem(app_config.storage_appSettings)
|
||||
try {
|
||||
return JSON.parse(a)
|
||||
} catch (error) {
|
||||
@ -67,7 +67,7 @@ export const settings = {
|
||||
}
|
||||
data = tmp
|
||||
try {
|
||||
localStorage.setItem( app_config.app_settings_storage, JSON.stringify(data) )
|
||||
localStorage.setItem( app_config.storage_appSettings, JSON.stringify(data) )
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return false
|
||||
|
@ -3,11 +3,11 @@ import { app_config } from 'config';
|
||||
import verbosity from 'core/libs/verbosity'
|
||||
import ErrorHandler from 'core/libs/errorhandler'
|
||||
|
||||
const { appTheme_desiredContrast, appTheme_container } = app_config
|
||||
const { appTheme_desiredContrast, storage_theme } = app_config
|
||||
|
||||
export const theme = {
|
||||
get: (key) => {
|
||||
const raw = store.get(appTheme_container)
|
||||
const raw = store.get(storage_theme)
|
||||
if(!raw) return false
|
||||
let container = []
|
||||
try {
|
||||
@ -25,14 +25,14 @@ export const theme = {
|
||||
obj.forEach((e) => {
|
||||
mix.push({key: e[0], value: e[1]})
|
||||
})
|
||||
return store.set(appTheme_container, mix)
|
||||
return store.set(storage_theme, mix)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
raw: () => {
|
||||
return store.get(appTheme_container)
|
||||
return store.get(storage_theme)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import v3_request from 'api/lib/v3_request'
|
||||
import endpointList from 'config/endpoints'
|
||||
import { app_config } from 'config'
|
||||
|
||||
const { api_prefix } = app_config;
|
||||
const { endpoint_v3prefix } = app_config;
|
||||
|
||||
export async function api_request(payload, callback) {
|
||||
if (!payload) return false;
|
||||
const { endpoint, body, serverKey, userToken } = payload;
|
||||
|
||||
let petition = {
|
||||
prefix: api_prefix,
|
||||
prefix: endpoint_v3prefix,
|
||||
endpointList,
|
||||
endpoint
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ class PrimaryLayout extends React.Component {
|
||||
}
|
||||
|
||||
// include API extensions
|
||||
window.dispatcher = this.props.dispatch
|
||||
window.openLink = (e) => {
|
||||
if (this.props.app.embedded) {
|
||||
this.props.app.electron.shell.openExternal(e)
|
||||
|
@ -38,8 +38,8 @@ export default {
|
||||
feedOutdated: false,
|
||||
|
||||
electron: null,
|
||||
app_settings: store.get(app_config.app_settings_storage),
|
||||
app_theme: store.get(app_config.appTheme_container) || [],
|
||||
app_settings: store.get(app_config.storage_appSettings),
|
||||
app_theme: store.get(app_config.storage_theme) || [],
|
||||
notifications: [],
|
||||
},
|
||||
subscriptions: {
|
||||
@ -236,7 +236,7 @@ export default {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
sessionStorage.setItem(app_config.session_data_storage, btoa(JSON.stringify(callbackResponse.response)))
|
||||
sessionStorage.setItem(app_config.storage_dataFrame, btoa(JSON.stringify(callbackResponse.response)))
|
||||
state.dispatcher({ type: "updateState", payload: { session_data: callbackResponse.response } })
|
||||
} catch (error) {
|
||||
verbosity([error])
|
||||
@ -268,8 +268,8 @@ export default {
|
||||
},
|
||||
*updateFrames({ payload }, { select, put }) {
|
||||
try {
|
||||
let sessionAuthframe = cookie.get(app_config.session_token_storage)
|
||||
let sessionDataframe = atob(sessionStorage.getItem(app_config.session_data_storage))
|
||||
let sessionAuthframe = cookie.get(app_config.storage_authFrame)
|
||||
let sessionDataframe = atob(sessionStorage.getItem(app_config.storage_dataFrame))
|
||||
|
||||
if (sessionAuthframe) {
|
||||
try {
|
||||
@ -283,7 +283,7 @@ export default {
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
cookie.remove(app_config.session_token_storage)
|
||||
cookie.remove(app_config.storage_authFrame)
|
||||
}
|
||||
}
|
||||
if (sessionDataframe) {
|
||||
@ -321,8 +321,8 @@ export default {
|
||||
state.session_authframe = jwt.decode(payload.token)
|
||||
|
||||
|
||||
cookie.set(app_config.session_token_storage, payload.token)
|
||||
sessionStorage.setItem(app_config.session_data_storage, btoa(JSON.stringify(payload.dataFrame)))
|
||||
cookie.set(app_config.storage_authFrame, payload.token)
|
||||
sessionStorage.setItem(app_config.storage_dataFrame, btoa(JSON.stringify(payload.dataFrame)))
|
||||
|
||||
state.session_valid = true
|
||||
},
|
||||
@ -331,7 +331,7 @@ export default {
|
||||
},
|
||||
handleUpdateTheme(state, { payload }) {
|
||||
verbosity([payload])
|
||||
store.set(app_config.appTheme_container, payload);
|
||||
store.set(app_config.storage_theme, payload);
|
||||
state.app_theme = payload
|
||||
},
|
||||
requireQuery(state, { payload, callback }) {
|
||||
@ -380,7 +380,7 @@ export default {
|
||||
state.session_data = null;
|
||||
state.session_token = null;
|
||||
state.session_authframe = null;
|
||||
cookie.remove(app_config.session_token_storage)
|
||||
cookie.remove(app_config.storage_authFrame)
|
||||
sessionStorage.clear()
|
||||
location.reload()
|
||||
},
|
||||
|
@ -0,0 +1,153 @@
|
||||
import React from 'react'
|
||||
import * as Icons from 'components/Icons'
|
||||
import * as antd from 'antd'
|
||||
import { connect } from 'umi'
|
||||
import styles from '../../index.less'
|
||||
|
||||
import { theme, getOptimalOpacityFromIMG, get_style_rule_value } from 'core/libs/style'
|
||||
import { urlToBase64, imageToBase64, arrayToObject } from 'core'
|
||||
import ThemeConfigurator from '../../configurator'
|
||||
|
||||
@connect(({ app }) => ({ app }))
|
||||
export default class BackgroundImage extends ThemeConfigurator {
|
||||
state = {
|
||||
configKey: "backgroundImage",
|
||||
model: { active: false, opacity: null, src: null },
|
||||
|
||||
textColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).color),
|
||||
overlayColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).backgroundColor),
|
||||
|
||||
processing: null,
|
||||
fileURL: null,
|
||||
customURL: '',
|
||||
}
|
||||
|
||||
handleFileUpload = info => {
|
||||
if (info.file.status === 'uploading') {
|
||||
return this.setState({ processing: false })
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
this.setState({ processing: true })
|
||||
imageToBase64(info.file.originFileObj, fileURL => {
|
||||
this.setState({ fileURL: fileURL })
|
||||
this.proccessBackground(fileURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleCustomURL(url) {
|
||||
this.setState({ processing: true, fileURL: url })
|
||||
urlToBase64(url, fileURL => {
|
||||
this.proccessBackground(fileURL)
|
||||
})
|
||||
}
|
||||
|
||||
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 ? 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] }
|
||||
}
|
||||
|
||||
render() {
|
||||
const PreviewModel = () => {
|
||||
return (
|
||||
<div>
|
||||
<h3><Icons.Layout /> Preview</h3>
|
||||
{ this.state.model.src ? <div className={styles.background_image_preview} style={{ backgroundColor: this.schemeToRGB(this.state.overlayColor) }}>
|
||||
<div style={{ color: `${this.schemeToRGB(this.state.textColor)}!important` }} className={styles.text_wrapper}>
|
||||
<h1 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h1>
|
||||
<h2 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h2>
|
||||
<h3 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h3>
|
||||
<h4 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h4>
|
||||
<p style={{ color: this.schemeToRGB(this.state.textColor) }}>Some text here</p>
|
||||
<p style={{ color: this.schemeToRGB(this.state.accentColor) }}>Some text here</p>
|
||||
|
||||
</div>
|
||||
<img style={{ opacity: this.state.model.opacity }} src={this.state.model.src} />
|
||||
</div> : <h3 style={{ textAlign: 'center' }} > No Background </h3>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.background_image_controls} >
|
||||
<div>
|
||||
<h4><Icons.Eye />Enabled</h4>
|
||||
<antd.Switch
|
||||
checkedChildren="Enabled"
|
||||
unCheckedChildren="Disabled"
|
||||
loading={this.state.processing}
|
||||
onChange={(e) => { this.promiseState(prevState => ({ model: { ...prevState.model, active: e } })).then(() => this.handleUpdate()) }}
|
||||
checked={this.state.model.active}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<h4><Icons.Layers />Opacity</h4>
|
||||
<antd.Slider disabled={!this.state.model.src} onChange={(e) => { this.setState(prevState => ({ model: { ...prevState.model, opacity: e / 100 } })) }} onAfterChange={() => this.handleUpdate()} value={this.state.model.opacity * 100} />
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Code />Export Code</h4>
|
||||
<antd.Button disabled={!this.state.model.src} size="small" onClick={() => this.handleExport()}> Export </antd.Button>
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Copy />Import Code</h4>
|
||||
<antd.Button size="small" onClick={() => null}> Import </antd.Button>
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Trash />Erase</h4>
|
||||
<antd.Popconfirm disabled={!this.state.model.src} placement="topLeft" title="Are you sure?" onConfirm={() => this.handleErase()} okText="Yes" cancelText="No">
|
||||
<antd.Button disabled={!this.state.model.src} size="small" type="primary" danger > Delete </antd.Button>
|
||||
</antd.Popconfirm>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
<PreviewModel />
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
|
||||
|
||||
<div>
|
||||
<h3><Icons.Upload /> Upload </h3>
|
||||
<div className={styles.background_image_uploader} >
|
||||
<div>
|
||||
Upload from your files <br />
|
||||
<antd.Upload onChange={this.handleFileUpload}>
|
||||
<antd.Button icon={<Icons.Upload type="primary" style={{ margin: '5px 0 0 0' }} />} />
|
||||
</antd.Upload>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Or</h3>
|
||||
</div>
|
||||
<div>
|
||||
Upload from URL
|
||||
<antd.Input onPressEnter={() => 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" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.state.processing ? <h4><Icons.LoadingOutlined spin /> Processing image ... </h4> : null}
|
||||
{this.state.params ? JSON.stringify(this.state.params) : null}
|
||||
</div>
|
||||
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import * as Icons from 'components/Icons'
|
||||
import * as antd from 'antd'
|
||||
import styles from '../../index.less'
|
||||
import ThemeConfigurator from '../../configurator'
|
||||
|
||||
export default class Colors extends ThemeConfigurator {
|
||||
state = {
|
||||
configKey: "colors",
|
||||
model: { active: false }
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.background_image_controls} >
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import * as Icons from 'components/Icons'
|
||||
import * as antd from 'antd'
|
||||
import styles from '../../index.less'
|
||||
import ThemeConfigurator from '../../configurator'
|
||||
|
||||
export default class DarkMode extends ThemeConfigurator {
|
||||
state = {
|
||||
configKey: "darkmode",
|
||||
model: { active: false }
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
}
|
||||
}
|
50
src/pages/settings/components/theme/configurator.js
Normal file
50
src/pages/settings/components/theme/configurator.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import ErrorHandler from 'core/libs/errorhandler'
|
||||
import { theme } from 'core/libs/style'
|
||||
import exportDataAsFile from 'core/libs/appInterface/export_data'
|
||||
import verbosity from 'core/libs/verbosity'
|
||||
|
||||
export default class ThemeConfigurator extends React.Component {
|
||||
componentDidMount() {
|
||||
this.applyStoraged()
|
||||
}
|
||||
|
||||
applyStoraged() {
|
||||
const storaged = theme.get()
|
||||
if (storaged && this.state) {
|
||||
if (storaged[this.state.configKey]) {
|
||||
return this.setState({ model: storaged[this.state.configKey] })
|
||||
} else {
|
||||
return verbosity(`cannot get storagedSetting for ${this.state.configKey}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promiseState = async state => new Promise(resolve => this.setState(state, resolve));
|
||||
|
||||
handleUpdate(payload) {
|
||||
if (!this.state.configKey) {
|
||||
return ErrorHandler({ msg: `cannot update without 'configKey', is missing`, code: 140 })
|
||||
}
|
||||
if (!payload) {
|
||||
payload = this.state.model
|
||||
}
|
||||
this.setState({ model: payload, processing: false })
|
||||
window.dispatcher({
|
||||
type: 'app/updateTheme',
|
||||
payload: {
|
||||
key: this.state.configKey,
|
||||
value: payload
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleErase() {
|
||||
this.handleUpdate({})
|
||||
}
|
||||
|
||||
handleExport() {
|
||||
exportDataAsFile({ data: JSON.stringify(this.state.model), type: 'text/json' })
|
||||
}
|
||||
|
||||
}
|
@ -1,316 +1,67 @@
|
||||
import React from 'react'
|
||||
import * as Icons from 'components/Icons'
|
||||
import * as antd from 'antd'
|
||||
import {connect} from 'umi'
|
||||
import styles from './index.less'
|
||||
|
||||
import ErrorHandler 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/appInterface/export_data'
|
||||
import { connect } from 'umi'
|
||||
import { arrayToObject, __legacy__ObjectToArray } from 'core'
|
||||
import ThemeSettingsList from 'globals/theme_settings.js'
|
||||
|
||||
class ThemeConfigurator extends React.Component{
|
||||
|
||||
componentDidMount(){
|
||||
this.applyStoraged()
|
||||
}
|
||||
|
||||
applyStoraged(){
|
||||
const storaged = theme.get()
|
||||
if(storaged && this.state){
|
||||
storaged[this.state.key]? this.setState({ model: storaged[this.state.key]}) : ErrorHandler({ msg: `"Config key" or "Dispatcher" is missing`, code: 140 })
|
||||
}
|
||||
}
|
||||
import BackgroundSetting from './components/background'
|
||||
import DarkmodeSetting from './components/darkmode'
|
||||
import ColorSetting from './components/color'
|
||||
|
||||
promiseState = async state => new Promise(resolve => this.setState(state, resolve));
|
||||
|
||||
handleUpdate(payload){
|
||||
if(!this.state.key || !this.props.dispatch) {
|
||||
return ErrorHandler({ msg: `"Config key" or "App/Dispatcher" is missing`, code: 140 })
|
||||
}
|
||||
if (!payload) {
|
||||
payload = this.state.model
|
||||
}
|
||||
this.setState({ model: payload, processing: false })
|
||||
this.props.dispatch({
|
||||
type: 'app/updateTheme',
|
||||
payload: {
|
||||
key: this.state.key,
|
||||
value: payload
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleErase(){
|
||||
this.handleUpdate({})
|
||||
}
|
||||
|
||||
handleExport(){
|
||||
exportDataAsFile({data: JSON.stringify(this.state.model), type: 'text/json'})
|
||||
}
|
||||
|
||||
const componentsMap = {
|
||||
backgroundImage: <BackgroundSetting />,
|
||||
darkmode: <DarkmodeSetting />,
|
||||
color: <ColorSetting />,
|
||||
}
|
||||
|
||||
@connect(({ app }) => ({ app }))
|
||||
class DarkMode 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 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 ThemeConfigurator{
|
||||
constructor(props){
|
||||
super(props),
|
||||
this.state = {
|
||||
key: "backgroundImage",
|
||||
model: { active: false, opacity: null, src: null },
|
||||
|
||||
textColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).color),
|
||||
overlayColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).backgroundColor),
|
||||
|
||||
processing: null,
|
||||
customURL: '',
|
||||
fileURL: null,
|
||||
}
|
||||
}
|
||||
|
||||
handleFileUpload = info => {
|
||||
if (info.file.status === 'uploading') {
|
||||
return this.setState({ processing: false })
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
this.setState({ processing: true })
|
||||
imageToBase64(info.file.originFileObj, fileURL => {
|
||||
this.setState({ fileURL: fileURL })
|
||||
this.proccessBackground(fileURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleCustomURL(url){
|
||||
this.setState({ processing: true, fileURL: url })
|
||||
urlToBase64(url, fileURL => {
|
||||
this.proccessBackground(fileURL)
|
||||
})
|
||||
}
|
||||
|
||||
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? 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]}
|
||||
}
|
||||
|
||||
render(){
|
||||
const PreviewModel = () => {
|
||||
return(
|
||||
<div>
|
||||
<h3><Icons.Layout /> Preview</h3>
|
||||
{ this.state.model.src? <div className={styles.background_image_preview} style={{ backgroundColor: this.schemeToRGB(this.state.overlayColor) }}>
|
||||
<div style={{ color: `${this.schemeToRGB(this.state.textColor)}!important` }} className={styles.text_wrapper}>
|
||||
<h1 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h1>
|
||||
<h2 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h2>
|
||||
<h3 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h3>
|
||||
<h4 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h4>
|
||||
<p style={{ color: this.schemeToRGB(this.state.textColor) }}>Some text here</p>
|
||||
<p style={{ color: this.schemeToRGB(this.state.accentColor) }}>Some text here</p>
|
||||
|
||||
</div>
|
||||
<img style={{ opacity: this.state.model.opacity }} src={this.state.model.src} />
|
||||
</div> : <h3 style={{ textAlign: 'center' }} > No Background </h3> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<h2><Icons.Image/> Background Image</h2>
|
||||
</div>
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
<div className={styles.background_image_controls} >
|
||||
<div>
|
||||
<h4><Icons.Eye />Enabled</h4>
|
||||
<antd.Switch
|
||||
checkedChildren="Enabled"
|
||||
unCheckedChildren="Disabled"
|
||||
loading={this.state.processing}
|
||||
onChange={(e) => {this.promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())}}
|
||||
checked={this.state.model.active}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<h4><Icons.Layers />Opacity</h4>
|
||||
<antd.Slider disabled={!this.state.model.src} onChange={(e) => {this.setState(prevState => ({model: {...prevState.model, opacity: e/100}}))}} onAfterChange={() => this.handleUpdate()} value={this.state.model.opacity*100} />
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Code />Export Code</h4>
|
||||
<antd.Button disabled={!this.state.model.src} size="small" onClick={() => this.handleExport()}> Export </antd.Button>
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Copy />Import Code</h4>
|
||||
<antd.Button size="small" onClick={() => null}> Import </antd.Button>
|
||||
</div>
|
||||
<div>
|
||||
<h4><Icons.Trash />Erase</h4>
|
||||
<antd.Popconfirm disabled={!this.state.model.src} placement="topLeft" title="Are you sure?" onConfirm={() => this.handleErase()} okText="Yes" cancelText="No">
|
||||
<antd.Button disabled={!this.state.model.src} size="small" type="primary" danger > Delete </antd.Button>
|
||||
</antd.Popconfirm>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
<PreviewModel />
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
|
||||
|
||||
<div>
|
||||
<h3><Icons.Upload /> Upload </h3>
|
||||
<div className={styles.background_image_uploader} >
|
||||
<div>
|
||||
Upload from your files <br/>
|
||||
<antd.Upload onChange={this.handleFileUpload}>
|
||||
<antd.Button icon={<Icons.Upload type="primary" style={{ margin: '5px 0 0 0' }} />} />
|
||||
</antd.Upload>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Or</h3>
|
||||
</div>
|
||||
<div>
|
||||
Upload from URL
|
||||
<antd.Input onPressEnter={() => 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" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.state.processing? <h4><Icons.LoadingOutlined spin /> Processing image ... </h4> : null}
|
||||
{this.state.params? JSON.stringify(this.state.params) : null}
|
||||
</div>
|
||||
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
|
||||
{/* <h3><Icons.Unsplash style={{ marginRight: "10px", verticalAlign: "-0.125em", width: "1em", height: "1em" }} /> Unsplash </h3>
|
||||
<antd.Input.Search onSearch={value => this.search(value)} />
|
||||
<antd.List itemLayout="vertical" dataSource={this.state.results} renderItem={item => ( <antd.List.Item> <img onClick={() => this.returnString(item.urls.full)} src={item.urls.small} /> </antd.List.Item>) }/> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@connect(({ app }) => ({ app }))
|
||||
export default class ThemeSettings extends React.Component{
|
||||
export default class ThemeSettings extends React.Component {
|
||||
state = {
|
||||
helper_fragment: null
|
||||
selectedKey: null,
|
||||
keys: []
|
||||
}
|
||||
|
||||
render(){
|
||||
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(
|
||||
componentDidMount() {
|
||||
let mix = []
|
||||
ThemeSettingsList.forEach(e => {
|
||||
mix[e.id] = e
|
||||
})
|
||||
this.setState({ keys: mix })
|
||||
}
|
||||
|
||||
render() {
|
||||
const selectedKeyItem = this.state.keys[this.state.selectedKey] ?? { icon: null, title: null }
|
||||
const handleClick = (key) => key ? this.setState({ selectedKey: key }) : null
|
||||
const isActive = (key) => { return key ? key.active : false }
|
||||
return (
|
||||
<div>
|
||||
<antd.List
|
||||
itemLayout="horizontal"
|
||||
dataSource={ThemeSettingsList}
|
||||
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={() => 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>
|
||||
</div>
|
||||
)}
|
||||
itemLayout="horizontal"
|
||||
dataSource={ThemeSettingsList}
|
||||
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={() => 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>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
|
||||
<antd.Drawer
|
||||
placement="right"
|
||||
width="700px"
|
||||
closable={true}
|
||||
onClose={() => this.setState({ helper_fragment: null })}
|
||||
visible={this.state.helper_fragment? true : false}
|
||||
width="50%"
|
||||
closable
|
||||
onClose={() => this.setState({ selectedKey: null })}
|
||||
visible={this.state.selectedKey ? true : false}
|
||||
>
|
||||
<React.Fragment>
|
||||
{this.state.helper_fragment}
|
||||
<React.Fragment>
|
||||
<div>
|
||||
<h2>{selectedKeyItem.icon} {selectedKeyItem.title}</h2>
|
||||
</div>
|
||||
<antd.Divider type="horizontal" dashed />
|
||||
{componentsMap[this.state.selectedKey]}
|
||||
</React.Fragment>
|
||||
</antd.Drawer>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user