From 9968116242f2b5c6543246a4ee49c164233df0f5 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 30 Sep 2018 11:00:23 -0500 Subject: [PATCH] [Application configuration support] [Fix component unmount leak] --- electron.js | 94 +++++- helpers.js | 110 +++++++ src/App.js | 145 ++++++--- src/assets/images/configure.png | Bin 0 -> 13414 bytes .../ConfigurationItem/ConfigurationItem.css | 36 +++ .../ConfigurationItem/ConfigurationItem.js | 107 +++++++ .../DependencyList/DependencyList.js | 4 +- .../DownloadProgress/DownloadProgress.js | 2 +- src/components/MountItem/MountItem.js | 45 ++- src/components/UI/DropDown/DropDown.js | 2 +- src/components/UpgradeIcon/UpgradeIcon.css | 2 +- src/components/UpgradeIcon/UpgradeIcon.js | 6 +- src/components/UpgradeUI/UpgradeUI.js | 22 +- .../Configuration/Configuration.css | 6 + src/containers/Configuration/Configuration.js | 287 ++++++++++++++++++ src/containers/MountItems/MountItems.js | 164 +++++----- src/index.css | 12 +- 17 files changed, 880 insertions(+), 164 deletions(-) create mode 100644 src/assets/images/configure.png create mode 100644 src/components/ConfigurationItem/ConfigurationItem.css create mode 100644 src/components/ConfigurationItem/ConfigurationItem.js create mode 100644 src/containers/Configuration/Configuration.css create mode 100644 src/containers/Configuration/Configuration.js diff --git a/electron.js b/electron.js index 48c4000..c943087 100644 --- a/electron.js +++ b/electron.js @@ -275,7 +275,8 @@ ipcMain.on('extract_release', (event, data) => { helpers.mkDirByPathSync(destination); const stream = fs.createReadStream(data.Source); - stream.pipe(unzip.Extract({ path: destination })) + stream + .pipe(unzip.Extract({ path: destination })) .on('error', (e) => { try { helpers.removeDirectoryRecursively(destination); @@ -301,6 +302,59 @@ ipcMain.on('extract_release', (event, data) => { }); }); +ipcMain.on('get_config', (event, data) => { + const dataDirectory = helpers.resolvePath(data.Directory); + helpers + .getConfig(dataDirectory, data.Version, data.StorageType) + .then((data) => { + if (data.Code === 0) { + event.sender.send('get_config_reply', { + data: { + Success: true, + Config: data.Data, + } + }); + } else { + event.sender.send('get_config_reply', { + data: { + Error: data.Code, + Success: false, + } + }); + } + }) + .catch((e)=> { + event.sender.send('get_config_reply', { + data: { + Error: e, + Success: false, + } + }); + }); +}); + +ipcMain.on('get_config_template', (event, data) => { + const dataDirectory = helpers.resolvePath(data.Directory); + helpers + .getConfigTemplate(dataDirectory, data.Version, data.StorageType) + .then((data) => { + event.sender.send('get_config_template_reply', { + data: { + Success: true, + Template: data, + } + }); + }) + .catch((e)=> { + event.sender.send('get_config_template_reply', { + data: { + Error: e, + Success: false, + } + }); + }); +}); + ipcMain.on('get_platform', (event) => { event.sender.send('get_platform_reply', { data: os.platform() @@ -408,14 +462,34 @@ ipcMain.on('save_state', (event, data) => { fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8'); }); -ipcMain.on('unmount_drive', (event, data) => { - helpers.stopProcessByPID(data.PID) - .then((pid)=> { - if (mountedPIDs.indexOf(pid) === -1) { - event.sender.send('unmount_drive_reply'); +ipcMain.on('set_config_values', (event, data) => { + const dataDirectory = helpers.resolvePath(data.Directory); + const setConfigValue = (i) => { + if (i < data.Items.length) { + helpers + .setConfigValue(data.Items[i].Name, data.Items[i].Value, dataDirectory, data.StorageType, data.Version) + .then(() => { + setConfigValue(++i); + }) + .catch(() => { + setConfigValue(++i); + }); + } else { + event.sender.send('set_config_values_reply', {}); } - }) - .catch((e) => { - console.log(e); - }); + }; + setConfigValue(0); +}); + +ipcMain.on('unmount_drive', (event, data) => { + helpers + .stopProcessByPID(data.PID) + .then((pid)=> { + if (mountedPIDs.indexOf(pid) === -1) { + event.sender.send('unmount_drive_reply'); + } + }) + .catch((e) => { + console.log(e); + }); }); diff --git a/helpers.js b/helpers.js index 69ac777..7564a12 100644 --- a/helpers.js +++ b/helpers.js @@ -195,6 +195,85 @@ module.exports.executeMount = (directory, version, storageType, location, exitCa }); }; +module.exports.getConfig = (directory, version, storageType) => { + return new Promise((resolve, reject) => { + const processOptions = { + detached: true, + shell: false, + windowsHide: true, + }; + + const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); + const args = []; + args.push('-dc'); + if (storageType.toLowerCase() === 'hyperspace') { + args.push('-hs'); + } + + const process = new spawn(command, args, processOptions); + let result = ''; + + process.on('error', (err) => { + reject(err); + }); + + process.stdout.on('data', (d)=> { + result += d; + }); + + process.on('exit', () => { + const lines = result + .replace(/\r\n/g, '\n') + .split('\n'); + + const code = parseInt(lines[0], 10); + if (code === 0) { + lines.shift(); + resolve({ + Code: code, + Data: JSON.parse(lines.join('\n')), + }); + } else { + resolve(code); + } + }); + process.unref(); + }); +}; + +module.exports.getConfigTemplate = (directory, version, storageType) => { + return new Promise((resolve, reject) => { + const processOptions = { + detached: true, + shell: false, + windowsHide: true, + }; + + const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); + const args = []; + args.push('-gt'); + if (storageType.toLowerCase() === 'hyperspace') { + args.push('-hs'); + } + + const process = new spawn(command, args, processOptions); + let result = ''; + + process.on('error', (err) => { + reject(err); + }); + + process.stdout.on('data', (d)=> { + result += d; + }); + + process.on('exit', () => { + resolve(JSON.parse(result)); + }); + process.unref(); + }); +}; + module.exports.getMissingDependencies = dependencies => { return new Promise((resolve, reject) => { if (!dependencies || (dependencies.length === 0)) { @@ -323,6 +402,37 @@ module.exports.resolvePath = str => { } }; +module.exports.setConfigValue = (name, value, directory, storageType, version) => { + return new Promise((resolve, reject) => { + const processOptions = { + detached: true, + shell: false, + windowsHide: true, + }; + + const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory'); + const args = []; + args.push('-set'); + args.push(name); + args.push(value); + if (storageType.toLowerCase() === 'hyperspace') { + args.push('-hs'); + } + + const process = new spawn(command, args, processOptions); + + process.on('error', (err) => { + reject(err); + }); + + process.on('exit', () => { + resolve(); + }); + + process.unref(); + }); +}; + module.exports.stopProcessByPID = pid => { return new Promise((resolve, reject) => { const processOptions = { diff --git a/src/App.js b/src/App.js index 73be628..2635a6f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,18 +1,20 @@ import React, {Component} from 'react'; -import CSSModules from 'react-css-modules'; -import styles from './App.css'; -import Box from './components/UI/Box/Box'; -import DropDown from './components/UI/DropDown/DropDown'; import * as Constants from './constants'; import axios from 'axios'; -import MountItems from './containers/MountItems/MountItems'; -import DependencyList from './components/DependencyList/DependencyList'; +import styles from './App.css'; +import Box from './components/UI/Box/Box'; import Button from './components/UI/Button/Button'; -import Modal from './components/UI/Modal/Modal'; +import Configuration from './containers/Configuration/Configuration'; +import CSSModules from 'react-css-modules'; +import DependencyList from './components/DependencyList/DependencyList'; import DownloadProgress from './components/DownloadProgress/DownloadProgress'; -import UpgradeUI from './components/UpgradeUI/UpgradeUI'; -import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; +import DropDown from './components/UI/DropDown/DropDown'; import Loading from './components/UI/Loading/Loading'; +import Modal from './components/UI/Modal/Modal'; +import MountItems from './containers/MountItems/MountItems'; +import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; +import UpgradeUI from './components/UpgradeUI/UpgradeUI'; + const Scheduler = require('node-schedule'); let ipcRenderer = null; @@ -208,6 +210,7 @@ class App extends Component { AllowOptions: false, AllowDownload: false, AutoMountChecked: false, + ConfigStorageType: null, DownloadActive: false, DownloadProgress: 0.0, DownloadingDependency: false, @@ -313,6 +316,18 @@ class App extends Component { this.saveState(this.state.Release, this.state.Version, sia, hyperspace); }; + handleConfigClicked = (storageType) => { + this.setState({ + ConfigStorageType: storageType, + }) + }; + + handleConfigClosed = () => { + this.setState({ + ConfigStorageType: null, + }); + }; + handleDependencyDownload = (url) => { if (ipcRenderer) { const items = url.split('/'); @@ -440,30 +455,62 @@ class App extends Component { const selectedVersion = (this.state.Version === -1) ? 'unavailable' : this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version]; + const downloadEnabled = this.state.AllowDownload && !this.state.MountsBusy && !this.state.DownloadActive && - (((selectedVersion !== 'unavailable') && (selectedVersion !== this.state.RepertoryVersion))); + (selectedVersion !== 'unavailable') && + (selectedVersion !== this.state.RepertoryVersion); + const allowMount = this.state.RepertoryVersion !== 'none'; const missingDependencies = (this.state.MissingDependencies.length > 0); + const allowConfig = this.state.LocationsLookup[selectedVersion] && + !this.state.LocationsLookup[selectedVersion].config_allowed; + + const showDependencies = missingDependencies && + !this.state.DownloadActive; + + const showConfig = !missingDependencies && + this.state.ConfigStorageType && + allowConfig; + + const showUpgrade = !showConfig && + !missingDependencies && + !this.state.DownloadActive && + this.state.UpgradeAvailable && + !this.state.UpgradeDismissed; + + let configDisplay = null; + if (showConfig) { + configDisplay = ( + + + + ); + } let mountDisplay = null; if (allowMount) { - mountDisplay = ; + disabled={!allowMount} + hyperspace={this.state.Hyperspace} + mountsBusy={this.notifyMountsBusy} + platform={this.state.Platform} + processAutoMount={!this.state.AutoMountChecked} + sia={this.state.Sia} + version={this.state.RepertoryVersion}/>; } let dependencyDisplay = null; - if (missingDependencies && !this.state.DownloadActive) { + if (showDependencies) { dependencyDisplay = ( - + ); } @@ -487,19 +534,16 @@ class App extends Component { if (this.state.ExtractActive) { releaseDisplay =

{'Activating <' + selectedVersion + '>'}

} else { - releaseDisplay = ; + releaseDisplay = ; } let upgradeDisplay = null; - if (!missingDependencies && - !this.state.DownloadActive && - this.state.UpgradeAvailable && - !this.state.UpgradeDismissed) { + if (showUpgrade) { upgradeDisplay = ( - this.setState({UpgradeDismissed: true})}/> + this.setState({UpgradeDismissed: true})} + upgrade={this.handleUIDownload}/> ); } @@ -510,28 +554,30 @@ class App extends Component { - - -
+

Release

-

Version

+
+

Version

+

Installed

- + 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 0000000000000000000000000000000000000000..e6ed2e02b6edf0f1e63ff818a00282af574ef610 GIT binary patch literal 13414 zcmZ|0c|27A7e7v9AIsamZ;i~@WnV*M8S9KCgi^M_*s|~IC?Yf>WEtDoB_uoZt`KEk z6N<8vB3XYI?>^t}@9}&5{Bf>vp68tBS?-;CU-#vqiIEP7ii3)XhzO*odjm;CbP4$N z5)lOna3GXi=^-K_Au=&Ag8?6bpNNPEb8~Y*IypH32Q(Ue`*wu4x3`m1#QppCU0oyY z`uU;JfHuI>GXjMQyyqR^<`(GW10aK3Tp~O?f&h%8SFoE~gtK=rfJFI(IC(_4x`sG= zM!36&pu7NNh^u#mvqvak=;9gb?jD5l40T2axq5}Vcm%n*g`zxy+}%T6J%e4)p>AHm zD335Vw=?PN9_;EF=7J7(^9)0wL)_iNTs=Zu0O=WmLWjAXOBeSLSC251dx#q#(IEiR z6%Am5T|Gik?!j(=L3=oX&J`Z~KMdruKWs1f0~!6#5d9zj{}%=*3t$Tn_=|J_s&Pi_tR_Hx&MR<{ z!hh8R2%MSxFC*aNIsgB$bpZ^|I&vQRFY==J7c~K#pI0FG91{$b6bO1I&ujG`#{E1r zATR2F(Tl$lVEdoRh2aHT!0lPq|H(6+=NmM*<)9B%P0e6X4Vr zX=F-7e2IkgG8s7qB^5Odh?b6?;U7jOW)@a9b`DN1ZXRAfegVNNV4V^p3DVV|@owL`x6{cfkNU=}|uAthv#)Ix*+N_UY+Sj>|daUjsenB?OATxI=1I@MNgsvV6 z45_UYC3}>~%baS<=ttL=bpzIE`2{Je3-ql81Sy1u!K}b1HJtui;1hf~?Ntf`kO>n62m9dQkEb zEgjoA%4DX2 zd$wO+O5h+AeT7VMYhpTVVZCyh<`vOtPkXjsQDR%kM=X#&4AMtv1~6S$J*)6*UiOp5 zCuzizXa%sR0AYf*6fbMA2J3Zl*tg~fA9hPt@xwqV3(kd*KQt`v1w~h-kJdqOAwu*k z;nyJc@W#|M{@OdD;Fl>2_kuXUSG%JVh4uId?~IDCq?1rvn;>(OXil4-rH$1Jm}&NU z_iWdou&p%uY9$VQ(no7e8Y;Tj86E&wtKW--VNP;%0<{ z7&n!^j89m2%r@`rF6b0YD+H}d9C=R52gqVVXp8Z(qN^IcPEBPaEgYv4xJ^7uvR(h_ z%LsS5Pd>Cj&b4?o7-&9gT}R}P&NWxT{c5Goh?lDhO^ZBmwHQZr#4w}B$}jHI#cbhd z@sc0LeLArDgbCqiH1EG5d^kTbu$y+BraTasyvuPq!dkU}*~QXCIsQgSQ&+njg24}k zqeV*)brF^n$rTj{9|6&p$TZJ|=xwwk7L22)&eV>hqm3CcDgf&DJ~_XrRfuaMR4V-+ z1gW_};qYd~LMWTcYF z+!aZT6ti$|YZolx@cEpV5e58!4Y*{8eq+T(;j0hOkmRf-EEjS7=&^DV{074nsU+>H z4Jw^SrHiVQbSd7{Ox3BRT@!tYvw2Y^USQb{G1 z3S@ie(#5-zKGfi!^N~h#BSnA_{fGB9k5?`rI6W0nzPfYJXell6Ng zwf4I^d=F#)UV~{Y-f*KG$NW(ENTOsYAxvu0hZU@EKOzJV?tHqvEp7eeLHE{O4za8` z8o5?@@3mT#IQUg}vY;By5ik)e8LG3*AT&2%ZOF6Vnn*Ls*|cQHUBPX?%+yv+;ON3B zrDJ_DhTjo&(liyc;qiNbswPuABwUsCDVO@gG&5Rvr5xNBR`SKJ1bzR(>Kjbq%p)!{ zKmF{H%h;=OrhBUHGkS45m$iV?5mPhx!0^o|z$XvrO6##|(&Q+&Q_lXs7SI6KzmnxfdwLHoEiKZ}dL7dq_?*^mCgE>)X-i zaKXmsB>5L;<1;ixDk@inAIU6(J4)vn^zL@$km_JRDZW7KU7(A*xHu60x7euAc~kmYn~@~i zgZW!`8~7`{Z(jyY$}>fnnHZ=7eGmi&7(p5#bB8?1@)K9IN#1XiNjW>$;384dwCsr_ zMe<3OcIfm!r_~qUVHe(KyS4@&sK#tN%aVud5bWXvg+`hZrnrAxUFhJ+C+=miKeE!0 z+67t`pQ)`1V>pWM3UT@Omf;T_KaV;1*ZNBeUsA$k?scK|3t4za9x%VVZgRnf@0?Ba zw?xzp?}#J)nzRg-dd8Fs-nT;7f%sA(qFbIiyjI+&@W#WmGe#ctf;^6&JIzv)`$39Pyg4V(q)8t#-}7cB z_$Hh+GphQl#n1yL!w}l!)z^>3gBJMA3eM|QbTJv_DXQ&h9+^y-mr9<6oZ%f5<{}p< z{BxE^kpF<)t|489MzT@s{9D7(r43;tJT6_U{TK*@Bume(kjx?4Vd(5j^- zO5^g`EDLZ0v&;m$GiY^dB|lC1|WRu%QvR;S@nE#P9aCgbGT&d z9S^nVqK!g`1iFlkG0N&9ivC5EjjY*vkK@U`ZR7`74R^ldoL}_i3xM)Dpmu(n4mFTm z+R_;k6r)}OPJcWW4L$?7kk-(QpY>5+ldv~Z-*=hSSJ* zkh4mZkKZ25nL8QhTEY5#P)?U|bhWj-2{iZX!oyFzWq^;hx=l!_x4uNh!+PbrpJ+K6 zH@GH&?sGF&&Bt{hfjMvVwb8o=wNoNKNx8ofAX9?pcL#!VvIU7R5bpSo&+K^%uadTW?dCd=xl#7@sdhedm{d0j>65t_R9nFvC((ScViDX zan=Y@Bm6PVu6suzZRqlHhDceX1X(;#M-=HN`D{3hL1D2!udMFA1A5=A<)#cYH24aa zsfj`Q_xbiWW4A3P>^3B6&D!{hvkoAbJx0jso-N^P| z^_gq!3_0c|@F{(-xp=JD<4${3JIGhwg^Lj61`w3G4gC zeO=8t-T#8-=-t`!+r*>S!YcE)l#@FDU z{TM|v6!q_hg1##bfeUxbE2CM}N~6cA**D#+4bnBXyA{o*a^h(w+Vs*rU(P=aGJ6(9 z^U78$eJ6Qad*p68pbFq_%!U@ur=OVp5lf(vk2}#wcVq~3k~6o{!DM?B!L2w0OHVAy zk1n%sDwboRuR8VH%IZXT**$u#;ps5`Kv!vV7CwTQAil0tvDG|8)%w+8FB1QnsJx{FzZFutS)3`imZ~@v?NYl^9=?IXuqH*+HS~16jFB;;+}K z&%@5qgfEsiXYfqZY<8zQGRXk zc!Z$G>+nJ5<6wW6r=YpuDCnG{;K1knKC^wprl7}en|TcmBWekI=!3v- zVK)QRMM?8sM?#sOr+5s_;eBY23N;?vf0Pg@=09xTKQx?OBgo@s%B0Cw0@TbKWn+0` zmH)N-q&p(l-@Lg@e>4v&4`q*{>(B2K(QTm>{FX6T_k4@SGv4=*+PrVNn{Fo@*9X=u zZA~mWF_;O@^0DJCLcFv!d(C_@Xj<5v9Fr;zB5S*?5r1W_4NA8nod-?S+J1PoY)5=p zV5>af{R*Dsr+7t-FPmn%>c9f4q8r{vOoesz#}0AxozqH>ZJWe4Gj^KO*}1uKid?FQ8|eY_SMQbWAlP`6ex&;}L;nnWZx5o)ro1JOir&hby$0D)=ABZi zZZAyn2Nt2(&B;1%PxIz$S}6w#ge+SJFSbO<&d2pgm-k1_eKJa~5%9X5H2Lw@U`dpf z*PL+q(J)asqn&2Dl-DhXd7)tUjV1f0%6p3DA26r&llpNxU+1fd*D}fGo|E4kv(ZcM zAUwvHIGW4Z_#Ry~ctV#PmB>@l_f~1UIG{|Lry8YXdvCfZ7e)HYx2i~UscgGc%ipA+ z4!E^^eUEO$b;8iTNgzPXoG0eCt!6sNZysax{+WN?t9VW!#8Qj(c9o*pvrFHX)Ut4N z*ty$`$!o=k0vzK}MVEw`BDK-l>nOD!$9i6tZFzVfiKBJ*a`!T79Y^)*q2@OA@U}a9 zqs~Gq(*Em$jJeO0Lb@i@Hw*pBUN9)ET9t<~N7WoZhd%Do(}~a*K~^NbeRELX8^u>@ zm^axpF<#{I=DXNJeliDh-*ng9s(X>gqifUft(T}!so zxn`jr?j$FQ_xlzrD|2rt)$45_bY1n*Nk&@&RYn&>#?#>@PwmE7keH5msWdkH%bMIvB4SUdKc>++0slC@T0{R{iaiDarRJ~nOBJ6RKtep|SJ&Cn7ExT9Jn4tZ)?SH&MK_=_JL z(DhVb#HBO)UAHXvTnRBbmHm5b_&4)ZNep|>i z*O*THi-)-O+I1rfl&e-?(U6SAij$?>L!2VTBYrG5aRufDbSh=Nr?vST8fp4Q#8A973|eRXmpu=+jOYK3qt zEL*tTJ?47P^!2IY+HdPrtJxOhD3buPU2X-?xQgbK0crOVcB2}kpFE>ut9;0BNVblv zwnf|Pi6r3?zEMPzIx^b#7Ic_Q$m{T-tt<1Z4Gr(~-ohGc66F|LN%LBa-qN$Zc0Wa1 zQ35$pXJhHD;#izCACV}WO(7z%WorBfr28_vuguwl&4LLb$I(8KTGxiKl!^-W-@QJ) z3oXb!w1=%R!DigLwww@qM3qh0V4zoYT*r)lF5kNo*s&nYM_E0j8IqTU%M{N2KC6>8 zD;&lR*6E67VP>j|{g{MX54TKtm8`_F*nQPiq9{4N!M^J!b~m;L7GC0F9A+u`Sn12u zcG9|h**!T?muEN^@z9M+eQ&#>Im^B-QG{NRj#sM2T$Vs$m6*aCBXJ~#_I54O<%V>G zo88y&jjzD{ZZ!yB_0Y;V?V*>F5+Rqz>}Uzj-S_c(L8RZ>42Q-=_a{i0P*rz6e>KZy zjoP~L4S^QzS5B|f+_xD2$TScW)FuNJ>%q`DN)#cN^dvpmVV+&D4ot>LN|rqGhwE(i zksCGmUtxxs{t#GM%)-qY6KrJAV=0J z2h5s@O9l#DE`_el!xNG*R^(g7eFpoV63wQj=$&@+mop@fT-adx_S3%Qq93C^E`{r- zm)@~(5re5N=;R?~=$*K05cc00w%b@*@F}izFSU`sL__hq*J!7#bOYgiu*b* z0SXo`SPsneRb?Ju;*<1|H(7Aa^reLvk791uH*KHD%bVJh7?d}pQ>oJPwc%@DkoA|n zPQ>-`+OY>&Fcz5)Bu^L|;bVh&FnUr@16aO6VYjR#ipB3p>2yJVI(DJ$K-J^kIQC|e1#~W>+gjW zGn9=OTE-6wnEVc(iRD0=nT$KCtpfdebMb>;~4ebylgyFQgDc{K}n zxopd>`S_7`EqHjL@16Q5jkVkuMZ-SG+9M*Rhjk<%`wy3_8+V^~?5aaaf3Etqpr+-? zT}*W{ucg(!qSxI*`cwD6j@(=Svsl*k&Y6JM`pCU|K$b*nNpxRx4KkbzbZ_at1|ql*Yyx3NPA zQ~P1AJ|)3?Bb+;#!9k?abeqGMIK};dcj84O;e9cRLFB^L(Vf`5&GxncM1$7g+Q zTqRFB1}gxSSih@@(a-=zZ8k*7gw;t^_Z=#tOMO=B-NqK(2$^YphuWpeaCTTk6zY}5 zFp50+O$6$;QE3$*&mh zJ6YQCwwLRNm+_1Bf_L7)n5wipkFO6Y*5bZ?f-6CZAC<&F)pU?TRGg9J6TuCwf5%Lj5@jkp{S(wy@GeNGqPV3-!WA^rPO7=D&t#fiLv0h8f1(29VXr#dKZ$GQ9p6>o2n5yZ z&TJpx@6*MYelr~f)J(9S^C^{$8A&0h0oF|910sj6t4Xt~U3v?%*h+2L+NWuAT3*PP z?ExK)c7-VKLF^oB@~QqI1=mCrjuKCnTZ9L3-K?20{21}Ph~0aT4~-t04a@EVOC@}` zAv+k4>+;e`qP=NkS^tFWO;`EG?Y&RCSI45V?TU(IILEFVv4ckzN(di2Q1sNf!vr8|&^F#>^W zLGbqLvOmCJ)A_Q{bC9)mFC$(kx5xpal5VWW#nffGfP|Cbve*oIbLm^L7k?Untj4ef1s(>fZJ#*0XDFDX)0xkx}? z74nB;tj7m#6cc|@h%#OCd;(#o`U?@m8eEr^PEE7_%Oxp)k-<#xtMF@Xp1#s6v1?!N?3 z^k7p&l0l&Z-(Sd06KT$|9xk|e{Qqx`!N?ui^3)}44%vSRAj#q3H)QLcrS1Jq04OGJ zdFnA%=5HWKHjc}TvjI|0{GZMLrz53uh+*D;l1lv7MN##LvJ;-!Nk|M%FRqURYI?VMC)_zv#nDI!!Mo z#P1q-NfFQDdeg*5^Dl2uPc9Wr$2NdU{woE*6--TSD&cL2+~?_uSM&KyJcJ^kJA^nigCTe4elTyhOT); zth6v0CdOD?Tz4j-v_2Q&!Ix^7VcrT?J_UEiY@KK(e$gGGMcV>bt)KRD+)PO~=4TCu zBquXw9G9^l_lA-Mjx-O$Pu~?rpm?LW-P`%LXGcmqu#sjtPWEdD&hbMm~mIuI9*lY6YB!2 zgbxSXOqSmKFgJQHH^?frNONn&5aK;i>-EyMVbX77BBglFOB*r?w2R%XCHI%>!-;5T zbV`pulYbM-U%pH_t=POzW8p3t`{*vY|_zwDDK2QM7i<9Zko zoc1Av$Z1TXblHh?QW5}kSC%d}CA|D`8kd{@(<9!>)3jdhQ72VR;j&+*Y;y8U1kcG* z-#C7NDqq5abog8V{(DF9dwlR+d`>iwQrtqx?L1y!2R`B1-Oc z4VY?*>li7zZ*Sxb{iQLVqZM~4^IE3RhLtqRq}08sz-q1~aokO2T`?25)f;0VHArf} zipA=A4mIV?h3C(u@;6m%LgbB(SBaALWohnzDD4)b$Xqa62Hkhf7;9X2yz6(&jiMD7c!g)iD5CxmD-b1qa_|Cr_*Ct_?RTysf_+@!+@vn|TGq+Lw#8Ak(epW**oTpRTU;;1Uwmmsd5#{X z;yy^CD455;pWOf>7vR9{63*)N_Oq*yETSi_?nT{!q;Kz;S#o3aYD|4uj!Le=I%4gI zWzBT`G_Ly^BJ7C_`a}vH0u1*J9mrv&grZ7bd~JdFrGn4e&`{23y%^zO_LFPRaaNLR zcgy>)^R-@2ZvcOEBXsdVTNc)@A z`=3+6W_TCsmv50~r0Q83L(Y;LLFJV_z{7^!Cj^t|%o9S?x5)J;gaRvycH1wmLGe3! z6i(bvamxn#Ba~QB6pC8}kMxlVtvL25=W?MQ;VE))V^eF$!kJ5MF+h(enbiEYouEk= zR6}^_GQ<9uZeMU$eU6{mLAhLL+1`YiwG~MT2v-R@T1{bsZFRPe08eoJ7`m~8NO&2e zyj8CaDNHM$AsvD(H}mfYRIq5t4&-i zt(sQk`&KWh682#BU7Vv*5}fUbHL=yq%hc94k@ch}dmN5*gy>*5{5^a0{*8?YlXnSR zmXc5EPPSidVmHvF)YBWq$r9P!th6UtO8$+Jmvu^nr@5j&R^_{MRW2K=s`G$&T{@mA zK@U5D$LyrYIa{r%lNkFqg`=j!)Tn^B9;@si5$jUn=?S9njJBjYNUgw{>MEpz9p$fo zd6y~f&Ks^ip{~UEFX7rjvvlW!UaG2!VtUKb1Lr)@)IK zeVXf8e)TTGa);JYLXd)|xbh3VC9*cY*&lR0%f?#MR@u5ME!j+gMZ>FyxNkF7w(WhH zr;}}wMjI=>X5Gy4r-~!6FeGH!$i-rtgKG!b&cCkaEURgis4Q!UuM?-h5Z*)Fs1mEk z@e;c(;wtHffe8o5V|&EyJ{AGt+~P{!%H}BZsX=NUrF()E`L>`6p3-$D(l_>c2|SUG z0k!ZEjxNC=_R3*U?Ye0Ki-1BdZrRaNKH7ZR&kYoq|91Ny3s?*Bc)E$NKy)Dr0#3X` z0C7~*mq^$IY+74D{A4RDbcV7i(mD`3;Pob@H|1f2%+*SNv@EJx)!h}M)B0#B(n5s) zP&L}v7?FJ5Af4bkfwEqVEuaZ7X zg~C~mtQY=w^SCTiM!xs@$=ricE8xNvnZ+Ydxqn?_hTiJr-?b}MJY;>e`iz><^*2{T z{-baGeNkFp%qBmxwf`0{|JJ3b1xH!D$uOqf%t?eU`STntW-!aR{a*6wXE%FB-O=&@ zI_>RcvQ~qFEltbww7u|bv7zvp*=_srLVnz0IKeoZpz$8>13s$i-7nklMKMs5zLjjI9PPHZI2*KWG4Fp3z*66}il zy6HpLIiSz!-i-z0qt(3bn1K|kfCbvu#3z3H9_9V9oXN)ts8>ZGo56T!u})WwCNI+Q zK95bRpBv>!d7v&ubzm|y&#|i%gMcY>d!UYNz#}Dh?}jxMeb6rdL>b}QmIpQNg1r-W z%If!|{+$CXvpNtT0W(luIS^MNZapBg-zZ_T%VyqQMakP74=oe-vKmicTX`|Y8b4pX zzJ=f6J1Pa%uX$n*1!5v_@#D8sFHiWD=WCXW90`RmpP5OFaviCvrZqt?Njxg(o3`2S zS17-t+$mUapJjxEI)osYDp9t>bhLzp1BB?fPkqv=fu$SEz*00jbN$G|)rJXEM-+<+ zGj}_t+Df*PSjA7oym7Hhr^KZAmvCV9Oy#j}S3AmfpJ0WQlv%O8Wdz()O)Ag{c-nII~=^XK(|mS?ny)u znlpDbJ5eS$8|vA~dtkZ(FS<1|^Lbk1efVQKw>X)zw@D%kx6G!1De^XX#hH8TE&-Z)>D#wky4WReR{}J1*n=-!Jo01gPK&Kn z@Bt3g0-0{JAF3a^nUlsF-669d2bJfWrJd7Cx7x1aFr3NrUsxiEr)@dT4e{WW^POK} z%^hz?f%eUL%dtFhzo^7;eNR;!6c(+@MF%vkXEm8OXdD!Zf!9HgZYVWC3j0QSyo_GK zp;wgo!UW#q>%>{vX@jxQx$zs*k8toMy&f;*9<*HLfv;k94wO!iVD&EDlz)tQCABT# z41^(Qn>wP!hwrMGr(N#xvR>3LH_%k_ttjfV?eVgkk%qdo&9^CW;&-fAq;0%J%;mm! z-T#Ozu4k71L~nf~{n@rPFLXARCO`(r=jTGz!L70#4GMN~_8aLNh(K34bAMh8%|t6K zU4FhevCLOi`qM~#CiG^f@>y}|*gaN_)6+r&zoE4 z7FcK}Wo~yJGjm{4T(77il>@y+tBG05E$(v)E*edHpJys*GK1 z-Zb6_QYxw_O`ZIQ)4Y*DvMUQkNh#oFZ;m3QHgxv~7oI#ZJN%`qu&4nIBbEq-$%>I3 zb`##?C1elT_WKbdePwR9X_5mUxEd3v31wP|UDd$$72>U5k&kost-%#C9qj8NnMxnx zSZSdJv|?l-)ie6{+S_qLq5Ktdr7{i^OSNnEtCMz&xok$N)--ZlJK{-)yX{&j{z;pk z3)Yi{4Uq$71(=MUu~YLkWhn_5*>d zdbuQYFviisH+QUO%j!M0sKW%dO_J2E?F~XVepuJeBUbfTx=n{j0ScME(?ZjxN1vWC z1PZil)pOS`wHQfmNSy)hT>ubn(b|XpAB6ic?>THvR(Yl^q=YWr=PAr};8$TaG6(C0&3_c&Nm4Q-6I5 zjA8y6K;Rsp1?)NbaImY=#25OWN6BqyLDJ>Y?iK1(p>qaiVn-7szMt+{d0C(QqvY}X zI@=EEnO5#XTm0soua*kuht(a<*=;9Avkgjsyg(9;a&_qLpuB^`><=T5LYXsU_QxR% z-*@9oS#_cG?g^3k@;=&r1~o3DupApEvr$oMD)Z${O#YhQ8EoJjX3`976cOg-CSFaR zGJJyRmip5%T$#>#$#!Dsftd64cUR7!8|TmzStyR7BAK8ryFbH|Rd*_^aOH@>@Zcm( z@N zgmE2xJN;Ri2@!bZByIO~jCxvKYM~bN7wZ{>#CU(I=v@o;S`0Wm`wl*hS`! z8!tsSYp6kCatz7pW&xu3V|{mot4fCU6&=hEPtA`e02#G^&dHpvv|_aTtLfsBNBUfP z7`-|hWL&;(AEek?X$q>L9;#Cqo^t`8^%rxBxaXcg#`HTr8AF&Dj?9~nj2L{-7jS?n z@LksAcp-s_puB(!ox>wsPeDn6Es$Sv!R?`u=B}4#R_|@r(=!efw7;(Bw4=GMT0Y5sL-*q`y)Ha&PCEz1a%>qszY^-fz!oeng&;XF9`c zy$8$OMcii_4gbWl`iy8s+a#kg(2IwrxA~f$pdFebHn;e!ju3zhdRmRc1Q^%1aV^H5 zjf!ElXj(r}&T!#JBKuFZ-PbsSlY`UZw#!_i8^zp6o(MRrv^=kSUW*1X*B2AA2{~DG z#V-F+oi+Rwj&j;mP3aPKozZjsp@mw=t2AJxzQ&D;y1Ba0wpVlCQbt4Bf}1?C8R5(0 zzN*=qWn+mw#A@FH+&8#HKQ&MOh%w)lfxqo6DZsXpu6>x)LSZeX11aEb= zYq1U&0loyY@+m!K<4F8Xq`4ba40`A0BL_jnGJ;kQh#m^LjOIpp{Tq)JjTid~S)tS6 zYQVk)Q!@w;ktSJA{tyA)__13B2%${s=!qE(2P5llX?pdfGck8!O0eso zF39D%;S?a$)vP<|PaC4{DVg{(yDKE787~0UEwO9>RKi*L?K=ntg;MRbG|F20D_~=S zp6x^(v{#c9DWjoUksMP9yauL_>n~WeR)GG7BsHhSQQ%#o;ptyrE>x~y*JD9$!ivPS z%3;)gSEv~*he(;_d}Bs12ww%!_f-v-OIBNTON!E^uyzYGv5>t~zmSC^@T5 z?ejW7A(k~CF&)felcF=kW+{hF%b3S3b7BE!UPGj(>%|9NZ-n6Ay6Yg=Ps9N>R^6v5 zqBrKbf61L1dPg)}0a|3a2ehag9523@$ECnJ*2S#;<2G_6yHo5p5=)-gU5u5%+|rcC z?mU*ui(TdSSMrpSI)Y7nc|?~rdw=(Azc^q@in^=?d6q<@e)ur$b?qH0 zZ~!0Ssg&c98u}FTd zOW{8>>}nad8oi{MGHGeOz_#vmtZ1~}ePQ-!nvRgDwC9p25csM9VGCpGt67yFn*&Pw-OY^$T zj%w1E`0C@ugV@+Lwkd6WD5az4AqBsSNZ5>Aqc6JroHqms<@RQW)4GN=Kj}e1Zyf>LmmWzo71_IPOXP zOe%w@tdNUu|5jWNMNW0%%A;dOP65FO$zj_gcas{g$PaK&(~nJ^k_fw!b@u3k?auyP ONDpRoqgwM$^#22P6B^P0 literal 0 HcmV?d00001 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}

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

{inputControl} + {inputControl} + {actionDisplay} - Auto-mount + Auto-mount
- - - - + + + +
- - - -
+ + + +
); 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 {