refactors for #27 & removed unused code

This commit is contained in:
srgooglo 2020-10-28 17:56:45 +01:00
parent 69384845f5
commit 11d1990536
16 changed files with 12388 additions and 9143 deletions

View File

@ -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,
// },
// },
// },
// },
// })
// },
})

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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()
},

View File

@ -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>
)
}
}

View File

@ -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>
)
}
}

View File

@ -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>
)
}
}

View 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' })
}
}

View File

@ -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>