|
-
+ selected={this.state.Release}/>
|
-
+ selected={this.state.Version}/>
|
{this.state.RepertoryVersion}
@@ -558,15 +604,24 @@ class App extends Component {
{dependencyDisplay}
{upgradeDisplay}
{downloadDisplay}
-
-
+ {configDisplay}
+
+
- |
-
+ | |
+
{'Repertory UI v' + this.props.version}
|
-
+ |
this.setState({UpgradeDismissed: false})}/>
diff --git a/src/assets/images/configure.png b/src/assets/images/configure.png
new file mode 100644
index 0000000..e6ed2e0
Binary files /dev/null and b/src/assets/images/configure.png differ
diff --git a/src/components/ConfigurationItem/ConfigurationItem.css b/src/components/ConfigurationItem/ConfigurationItem.css
new file mode 100644
index 0000000..c880836
--- /dev/null
+++ b/src/components/ConfigurationItem/ConfigurationItem.css
@@ -0,0 +1,36 @@
+.ConfigurationItem {
+ margin: 0;
+ padding: 0;
+}
+
+input.Input {
+ display: block;
+ margin: 0;
+ padding: 2px;
+ border-radius: var(--border_radius);
+ background: rgba(160, 160, 160, 0.1);
+ border: none;
+ box-shadow: none;
+ outline: none;
+ color: var(--text_color);
+ box-sizing: border-box;
+}
+
+.Select {
+ display: block;
+ margin: 0;
+ padding: 2px;
+ border-radius: var(--border_radius);
+ background: rgba(160, 160, 160, 0.1);
+ border: none;
+ box-shadow: none;
+ outline: none;
+ color: var(--text_color);
+ box-sizing: border-box;
+}
+
+.Option {
+ background: rgba(10, 10, 15, 0.8);
+ border-color: rgba(10, 10, 20, 0.9);
+ color: var(--text_color);
+}
\ No newline at end of file
diff --git a/src/components/ConfigurationItem/ConfigurationItem.js b/src/components/ConfigurationItem/ConfigurationItem.js
new file mode 100644
index 0000000..0f76ce2
--- /dev/null
+++ b/src/components/ConfigurationItem/ConfigurationItem.js
@@ -0,0 +1,107 @@
+import React from 'react';
+import CSSModules from 'react-css-modules';
+import styles from './ConfigurationItem.css';
+
+export default CSSModules((props) => {
+ const handleChanged = (e) => {
+ const target = e.target;
+ if (target.type === 'checkbox') {
+ target.value = e.target.checked ? "true" : "false";
+ }
+ props.changed(target);
+ };
+
+ let data;
+ switch (props.template.type) {
+ case "bool":
+ data = handleChanged(e)}
+ type={'checkbox'}/>;
+ break;
+
+ case "double":
+ data = handleChanged(e)}
+ step={"0.01"}
+ styleName='Input'
+ type={'number'}
+ value={parseFloat(props.value).toFixed(2)}/>;
+ break;
+
+ case "list":
+ const options = props.items.map((s, i) => {
+ return (
+
+ );
+ });
+
+ data = (
+
+ );
+ break;
+
+ case "string":
+ data = handleChanged(e)}
+ styleName='Input'
+ type={'text'}
+ value={props.value}/>;
+ break;
+
+ case "uint8":
+ data = handleChanged(e)}
+ styleName='Input'
+ type={'number'}
+ value={props.value}/>;
+ break;
+
+ case "uint16":
+ data = handleChanged(e)}
+ styleName='Input'
+ type={'number'}
+ value={props.value}/>;
+ break;
+
+ case "uint32":
+ data = handleChanged(e)}
+ styleName='Input'
+ type={'number'}
+ value={props.value}/>;
+ break;
+
+ case "uint64":
+ data = handleChanged(e)}
+ styleName='Input'
+ type={'number'}
+ value={props.value}/>;
+ break;
+
+ default:
+ data = {props.value} ;
+ }
+
+ return (
+
+
+
+
+ | {props.label} |
+ {data} |
+
+
+
+
+ );
+}, styles, {allowMultiple: true});
\ No newline at end of file
diff --git a/src/components/DependencyList/DependencyList.js b/src/components/DependencyList/DependencyList.js
index 4a5b155..1d387a4 100644
--- a/src/components/DependencyList/DependencyList.js
+++ b/src/components/DependencyList/DependencyList.js
@@ -8,15 +8,15 @@ export default CSSModules((props) => {
const items = props.dependencies.map((k, i)=> {
return (
);
});
return (
-
+
Missing Dependencies
diff --git a/src/components/DownloadProgress/DownloadProgress.js b/src/components/DownloadProgress/DownloadProgress.js
index 7263e8e..60adaf9 100644
--- a/src/components/DownloadProgress/DownloadProgress.js
+++ b/src/components/DownloadProgress/DownloadProgress.js
@@ -6,7 +6,7 @@ import styles from './DownloadProgress.css';
export default CSSModules((props) => {
return (
-
+
{'Downloading ' + props.display}
diff --git a/src/components/MountItem/MountItem.js b/src/components/MountItem/MountItem.js
index eb0c45c..cb516dc 100644
--- a/src/components/MountItem/MountItem.js
+++ b/src/components/MountItem/MountItem.js
@@ -4,21 +4,34 @@ import styles from './MountItem.css';
import DropDown from '../UI/DropDown/DropDown';
import Button from '../UI/Button/Button';
import Loader from 'react-loader-spinner';
+import configureImage from '../../assets/images/configure.png';
export default CSSModules((props) => {
+ let configButton = null;
+ if (props.allowConfig) {
+ configButton = (
+
+ );
+ }
+
let inputControl = null;
let mountWidth = '70%';
if (props.platform === 'win32') {
- inputControl = ;
+ selected={props.items.indexOf(props.location)}/>;
mountWidth = '18%';
} else {
inputControl = ;
+ value={props.location}/>;
}
let actionDisplay = null;
@@ -28,21 +41,29 @@ export default CSSModules((props) => {
} else {
actionDisplay = ;
+ type='Circles'
+ width='24px'/>;
}
+
return (
- {props.title}
-
+ | {configButton} | {props.title} |
+
- | {inputControl} |
-
+ | {inputControl}
+ |
+
{actionDisplay}
|
- Auto-mount
+ Auto-mount
|
diff --git a/src/components/UI/DropDown/DropDown.js b/src/components/UI/DropDown/DropDown.js
index 5ec0ec4..cc16361 100644
--- a/src/components/UI/DropDown/DropDown.js
+++ b/src/components/UI/DropDown/DropDown.js
@@ -1,6 +1,6 @@
import React from 'react';
-import CSSModules from 'react-css-modules';
import styles from './DropDown.css';
+import CSSModules from 'react-css-modules';
export default CSSModules((props) => {
const options = props.items.map((s, i) => {
diff --git a/src/components/UpgradeIcon/UpgradeIcon.css b/src/components/UpgradeIcon/UpgradeIcon.css
index 4225165..c70cc50 100644
--- a/src/components/UpgradeIcon/UpgradeIcon.css
+++ b/src/components/UpgradeIcon/UpgradeIcon.css
@@ -1,6 +1,6 @@
.UpgradeIcon {
display: block;
- margin: 0;
+ margin-right: 2px;
padding: 0;
width: 20px;
height: 20px;
diff --git a/src/components/UpgradeIcon/UpgradeIcon.js b/src/components/UpgradeIcon/UpgradeIcon.js
index 11d5ee2..a6ef861 100644
--- a/src/components/UpgradeIcon/UpgradeIcon.js
+++ b/src/components/UpgradeIcon/UpgradeIcon.js
@@ -10,6 +10,10 @@ export default CSSModules((props) => {
}
return props.available ?
- :
+ :
null;
}, styles, {allowMultiple: true});
\ No newline at end of file
diff --git a/src/components/UpgradeUI/UpgradeUI.js b/src/components/UpgradeUI/UpgradeUI.js
index 7e795d6..3702c65 100644
--- a/src/components/UpgradeUI/UpgradeUI.js
+++ b/src/components/UpgradeUI/UpgradeUI.js
@@ -6,22 +6,22 @@ import styles from './UpgradeUI.css';
export default CSSModules((props) => {
return (
-
+
UI Upgrade Available
-
- |
-
- |
-
-
- |
-
+
+ |
+
+ |
+
+
+ |
+
);
diff --git a/src/containers/Configuration/Configuration.css b/src/containers/Configuration/Configuration.css
new file mode 100644
index 0000000..675f8c8
--- /dev/null
+++ b/src/containers/Configuration/Configuration.css
@@ -0,0 +1,6 @@
+.Configuration {
+ width: 90vw;
+ height: 90vh;
+ padding: 4px;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/src/containers/Configuration/Configuration.js b/src/containers/Configuration/Configuration.js
new file mode 100644
index 0000000..f94e257
--- /dev/null
+++ b/src/containers/Configuration/Configuration.js
@@ -0,0 +1,287 @@
+import React, {Component} from 'react';
+import styles from './Configuration.css';
+import Box from '../../components/UI/Box/Box';
+import Button from '../../components/UI/Button/Button';
+import ConfigurationItem from '../../components/ConfigurationItem/ConfigurationItem';
+import CSSModules from 'react-css-modules';
+import Modal from '../../components/UI/Modal/Modal';
+
+let ipcRenderer = null;
+if (!process.versions.hasOwnProperty('electron')) {
+ ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
+}
+
+class Configuration extends Component {
+ constructor(props) {
+ super(props);
+ if (ipcRenderer) {
+ ipcRenderer.on('get_config_template_reply', this.onGetConfigTemplateReply);
+ ipcRenderer.on('get_config_reply', this.onGetConfigReply);
+ ipcRenderer.on('set_config_values_reply', this.onSetConfigValuesReply);
+
+ ipcRenderer.send('get_config_template', {
+ Directory: this.props.directory,
+ StorageType: this.props.storageType,
+ Version: this.props.version,
+ });
+ }
+ }
+
+ state = {
+ ChangedItems: [],
+ ChangedObjectLookup: null,
+ ObjectLookup: {},
+ OriginalItemList: [],
+ OriginalObjectLookup: {},
+ ItemList: [],
+ Saving: false,
+ ShowAdvanced: false,
+ Template: {}
+ };
+
+ checkSaveRequired = () => {
+ const changedItems = [];
+ let i = 0;
+ for (const item of this.state.ItemList) {
+ if (this.state.OriginalItemList[i++].value !== item.value) {
+ changedItems.push(item);
+ }
+ }
+
+ let changedObjectLookup = null;
+ for (const key of Object.keys(this.state.ObjectLookup)) {
+ const changedObjectItems = [];
+ let j = 0;
+ for (const item of this.state.ObjectLookup[key]) {
+ if (this.state.OriginalObjectLookup[key][j++].value !== item.value) {
+ changedObjectItems.push(item);
+ }
+ }
+
+ if (changedObjectItems.length > 0) {
+ if (changedObjectLookup === null) {
+ changedObjectLookup = {};
+ }
+ changedObjectLookup[key] = changedObjectItems;
+ }
+ }
+
+ if ((changedItems.length > 0) || changedObjectLookup) {
+ this.setState({
+ ChangedItems: changedItems,
+ ChangedObjectLookup: changedObjectLookup,
+ });
+ } else {
+ this.props.closed();
+ }
+ };
+
+ componentWillUnmount = () => {
+ if (ipcRenderer) {
+ ipcRenderer.removeListener('get_config_reply', this.onGetConfigReply);
+ ipcRenderer.removeListener('get_config_template_reply', this.onGetConfigTemplateReply);
+ ipcRenderer.removeListener('set_config_values', this.onSetConfigValuesReply);
+ }
+ };
+
+ createItemList = (config, template) => {
+ const objectList = [];
+ const itemList = Object
+ .keys(config)
+ .map(key => {
+ return {
+ advanced: template[key] ? template[key].advanced : false,
+ label: key,
+ value: config[key],
+ };
+ })
+ .filter(i=> {
+ let ret = template[i.label];
+ if (ret && (template[i.label].type === 'object')) {
+ objectList.push(i);
+ ret = false;
+ }
+ return ret;
+ });
+
+ return {
+ ObjectList: objectList,
+ ItemList: itemList,
+ }
+ };
+
+ handleItemChanged = (target, idx) => {
+ const itemList = [
+ ...this.state.ItemList
+ ];
+ itemList[idx].value = target.value.toString();
+ this.setState({
+ ItemList: itemList
+ });
+ };
+
+ handleObjectItemChanged = (target, name, idx) => {
+ const itemList = [
+ ...this.state.ObjectLookup[name]
+ ];
+ const objectLookup = {
+ ...this.state.ObjectLookup,
+ };
+
+ itemList[idx].value = target.value.toString();
+ objectLookup[name] = itemList;
+ this.setState({
+ ObjectLookup: objectLookup,
+ });
+ };
+
+ onGetConfigReply = (event, arg) => {
+ if (arg.data.Success) {
+ const list = this.createItemList(arg.data.Config, this.state.Template);
+ const itemListCopy = JSON.parse(JSON.stringify(list.ItemList));
+
+ let objectLookup = {};
+ for (const obj of list.ObjectList) {
+ const list2 = this.createItemList(obj.value, this.state.Template[obj.label].template);
+ objectLookup[obj.label] = list2.ItemList;
+ }
+ const objectLookupCopy = JSON.parse(JSON.stringify(objectLookup));
+
+ this.setState({
+ ItemList: list.ItemList,
+ ObjectLookup: objectLookup,
+ OriginalItemList: itemListCopy,
+ OriginalObjectLookup: objectLookupCopy,
+ });
+ }
+ };
+
+ onGetConfigTemplateReply = (event, arg) => {
+ if (arg.data.Success) {
+ this.setState({
+ Template: arg.data.Template,
+ });
+ ipcRenderer.send('get_config', {
+ Directory: this.props.directory,
+ StorageType: this.props.storageType,
+ Version: this.props.version,
+ });
+ }
+ };
+
+ onSetConfigValuesReply = () => {
+ this.props.closed();
+ };
+
+ saveAndClose = () => {
+ if (ipcRenderer) {
+ this.setState({
+ Saving: true,
+ });
+
+ const changedItems = [];
+ for (const item of this.state.ChangedItems) {
+ changedItems.push({
+ Name: item.label,
+ Value: item.value,
+ });
+ }
+
+ if (this.state.ChangedObjectLookup) {
+ for (const key of Object.keys(this.state.ChangedObjectLookup)) {
+ for (const item of this.state.ChangedObjectLookup[key]) {
+ changedItems.push({
+ Name: key + '.' + item.label,
+ Value: item.value,
+ });
+ }
+ }
+ }
+
+ ipcRenderer.send('set_config_values', {
+ Directory: this.props.directory,
+ Items: changedItems,
+ StorageType: this.props.storageType,
+ Version: this.props.version,
+ });
+ }
+ };
+
+ render() {
+ let confirmSave = null;
+ if ((this.state.ChangedItems.length > 0) || this.state.ChangedObjectLookup) {
+ confirmSave = (
+
+
+ Save Changes?
+
+
+
+ );
+ }
+
+ let configurationItems = this.state.ItemList
+ .map((k, i) => {
+ return (
+ ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
+ this.handleItemChanged(e, i)}
+ items={this.state.Template[k.label].items}
+ key={i}
+ label={k.label}
+ template={this.state.Template[k.label]}
+ value={k.value}/> :
+ null)
+ });
+
+ let objectItems = [];
+ for (const key of Object.keys(this.state.ObjectLookup)) {
+ objectItems.push((
+
+ {key}
+
+ {
+ this.state.ObjectLookup[key].map((k, i) => {
+ return (
+ ((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
+ this.handleObjectItemChanged(e, key, i)}
+ items={this.state.Template[key].template[k.label].items}
+ key={i}
+ label={k.label}
+ template={this.state.Template[key].template[k.label]}
+ value={k.value}/> :
+ null)
+ })
+ }
+
+
+ ));
+ }
+
+ return (
+
+ {confirmSave}
+
+
+ X
+
+ {this.props.storageType + ' Configuration'}
+
+ {objectItems}
+ Settings
+ {configurationItems}
+
+
+
+ );
+ }
+}
+
+export default CSSModules(Configuration, styles, {allowMultiple: true});
\ No newline at end of file
diff --git a/src/containers/MountItems/MountItems.js b/src/containers/MountItems/MountItems.js
index d0ce06d..a3268a9 100644
--- a/src/containers/MountItems/MountItems.js
+++ b/src/containers/MountItems/MountItems.js
@@ -13,66 +13,9 @@ class MountItems extends Component {
constructor(props) {
super(props);
if (ipcRenderer) {
- ipcRenderer.on('detect_mounts_reply', (event, arg) => {
- if (arg.data.Success) {
- const sia = {
- ...this.state.Sia,
- AllowMount: true,
- DriveLetters: (arg.data.DriveLetters.Sia),
- Mounted: (arg.data.Locations.Sia.length > 0),
- PID: arg.data.PIDS.Sia,
- };
- const hs = {
- ...this.state.Hyperspace,
- AllowMount: true,
- DriveLetters: (arg.data.DriveLetters.Hyperspace),
- Mounted: (arg.data.Locations.Hyperspace.length > 0),
- PID: arg.data.PIDS.Hyperspace,
- };
-
- this.setState({
- Hyperspace: hs,
- Sia: sia,
- });
-
- this.props.mountsBusy(hs.Mounted || sia.Mounted);
-
- let hsLocation = arg.data.Locations.Hyperspace;
- if ((hsLocation.length === 0) && (this.props.platform === 'win32')) {
- hsLocation = this.props.hyperspace.MountLocation || arg.data.DriveLetters.Hyperspace[0];
- }
- if (hsLocation !== this.props.hyperspace.MountLocation) {
- this.props.changed('Hyperspace', hsLocation);
- }
-
- let siaLocation = arg.data.Locations.Sia;
- if ((siaLocation.length === 0) && (this.props.platform === 'win32')) {
- siaLocation = this.props.sia.MountLocation || arg.data.DriveLetters.Sia[0];
- }
- if (siaLocation !== this.props.sia.MountLocation) {
- this.props.changed('Sia', siaLocation);
- }
-
- this.performAutoMount();
- }
- });
-
- ipcRenderer.on('mount_drive_reply', (event, arg) => {
- const state = {
- ...this.state[arg.data.StorageType],
- PID: arg.data.PID,
- Mounted: arg.data.Success,
- };
- this.setState({
- [arg.data.StorageType]: state,
- });
-
- this.detectMounts();
- });
-
- ipcRenderer.on('unmount_drive_reply', (event, arg) => {
- this.detectMounts();
- });
+ ipcRenderer.on('detect_mounts_reply', this.onDetectMountsReply);
+ ipcRenderer.on('mount_drive_reply', this.onMountDriveReply);
+ ipcRenderer.on('unmount_drive_reply', this.onUnmountDriveReply);
this.detectMounts();
}
@@ -93,6 +36,14 @@ class MountItems extends Component {
},
};
+ componentWillUnmount = () => {
+ if (ipcRenderer) {
+ ipcRenderer.removeListener('detect_mounts_reply', this.onDetectMountsReply);
+ ipcRenderer.removeListener('mount_drive_reply', this.onMountDriveReply);
+ ipcRenderer.removeListener('unmount_drive_reply', this.onUnmountDriveReply);
+ }
+ };
+
detectMounts = ()=> {
this.props.mountsBusy(true);
ipcRenderer.send('detect_mounts', {
@@ -141,6 +92,67 @@ class MountItems extends Component {
}
};
+ onDetectMountsReply = (event, arg) => {
+ if (arg.data.Success) {
+ const sia = {
+ ...this.state.Sia,
+ AllowMount: true,
+ DriveLetters: (arg.data.DriveLetters.Sia),
+ Mounted: (arg.data.Locations.Sia.length > 0),
+ PID: arg.data.PIDS.Sia,
+ };
+ const hs = {
+ ...this.state.Hyperspace,
+ AllowMount: true,
+ DriveLetters: (arg.data.DriveLetters.Hyperspace),
+ Mounted: (arg.data.Locations.Hyperspace.length > 0),
+ PID: arg.data.PIDS.Hyperspace,
+ };
+
+ this.setState({
+ Hyperspace: hs,
+ Sia: sia,
+ });
+
+ this.props.mountsBusy(hs.Mounted || sia.Mounted);
+
+ let hsLocation = arg.data.Locations.Hyperspace;
+ if ((hsLocation.length === 0) && (this.props.platform === 'win32')) {
+ hsLocation = this.props.hyperspace.MountLocation || arg.data.DriveLetters.Hyperspace[0];
+ }
+ if (hsLocation !== this.props.hyperspace.MountLocation) {
+ this.props.changed('Hyperspace', hsLocation);
+ }
+
+ let siaLocation = arg.data.Locations.Sia;
+ if ((siaLocation.length === 0) && (this.props.platform === 'win32')) {
+ siaLocation = this.props.sia.MountLocation || arg.data.DriveLetters.Sia[0];
+ }
+ if (siaLocation !== this.props.sia.MountLocation) {
+ this.props.changed('Sia', siaLocation);
+ }
+
+ this.performAutoMount();
+ }
+ };
+
+ onMountDriveReply = (event, arg) => {
+ const state = {
+ ...this.state[arg.data.StorageType],
+ PID: arg.data.PID,
+ Mounted: arg.data.Success,
+ };
+ this.setState({
+ [arg.data.StorageType]: state,
+ });
+
+ this.detectMounts();
+ };
+
+ onUnmountDriveReply = (event, arg) => {
+ this.detectMounts();
+ };
+
performAutoMount = ()=> {
if (this.props.processAutoMount) {
this.props.autoMountProcessed();
@@ -160,28 +172,32 @@ class MountItems extends Component {
render() {
return (
- this.props.autoMountChanged('Hyperspace', e)}
- mounted={this.state.Hyperspace.Mounted}
- items={this.state.Hyperspace.DriveLetters}
- platform={this.props.platform}
- title={'Hyperspace'}
- location={this.props.hyperspace.MountLocation}
changed={(e) => this.handleMountLocationChanged('Hyperspace', e.target.value)}
clicked={this.handleMountUnMount}
- pid={this.state.Hyperspace.PID}/>
- this.props.configClicked('Hyperspace')}
+ items={this.state.Hyperspace.DriveLetters}
+ location={this.props.hyperspace.MountLocation}
+ mounted={this.state.Hyperspace.Mounted}
+ pid={this.state.Hyperspace.PID}
+ platform={this.props.platform}
+ title={'Hyperspace'}/>
+ this.props.autoMountChanged('Sia', e)}
- mounted={this.state.Sia.Mounted}
- items={this.state.Sia.DriveLetters}
- platform={this.props.platform}
- title={'Sia'}
- location={this.props.sia.MountLocation}
changed={(e) => this.handleMountLocationChanged('Sia', e.target.value)}
clicked={this.handleMountUnMount}
- pid={this.state.Sia.PID}/>
+ configClicked={()=>this.props.configClicked('Sia')}
+ items={this.state.Sia.DriveLetters}
+ location={this.props.sia.MountLocation}
+ mounted={this.state.Sia.Mounted}
+ pid={this.state.Sia.PID}
+ platform={this.props.platform}
+ title={'Sia'}/>
);
}
}
diff --git a/src/index.css b/src/index.css
index fe9fcd3..a2b349f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -6,18 +6,18 @@
--control_border: 1px solid rgba(70, 70, 70, 0.9);
--control_box_shadow: 1px 1px 1px black;
--control_transparent_background: rgba(60, 60, 70, 0.4);
- --control_dark_transparent_background: rgba(60, 60, 70, 0.4);
+ --control_dark_transparent_background: rgba(15, 15, 15, 0.8);
- --text_color: rgba(200, 205, 220, 0.7);
- --text_color_hover: rgba(200, 205, 220, 0.7);
- --heading_text_color: rgba(194, 217, 255, 0.6);
- --heading_other_text_color: rgba(200, 205, 220, 0.7);
+ --text_color: rgba(200, 205, 220, 0.75);
+ --text_color_hover: rgba(200, 205, 220, 0.75);
+ --heading_text_color: rgba(140, 169, 203, 0.75);
+ --heading_other_text_color: rgba(200, 205, 220, 0.75);
--text_color_transition: color 0.3s;
}
* {
font-family: 'Nunito', sans-serif;
- font-size: 15px;
+ font-size: 14px;
}
*::-moz-focus-inner {
| |