import React, {Component} from 'react'; import axios from 'axios'; import styles from './App.css'; import Box from './components/UI/Box/Box'; 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 ErrorDetails from './components/ErrorDetails/ErrorDetails'; import Grid from './components/UI/Grid/Grid'; import Loading from './components/UI/Loading/Loading'; import Modal from './components/UI/Modal/Modal'; import MountItems from './containers/MountItems/MountItems'; import ReleaseVersionDisplay from './components/ReleaseVersionDisplay/ReleaseVersionDisplay'; import Text from './components/UI/Text/Text'; import UpgradeIcon from './components/UpgradeIcon/UpgradeIcon'; import UpgradeUI from './components/UpgradeUI/UpgradeUI'; const Constants = require('./constants'); const Scheduler = require('node-schedule'); let ipcRenderer = null; if (!process.versions.hasOwnProperty('electron')) { ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null); } class App extends Component { constructor(props) { super(props); for (const provider of Constants.PROVIDER_LIST) { this.state[provider] = { AutoMount: false, AutoRestart: false, MountLocation: '', } } if (ipcRenderer) { ipcRenderer.on(Constants.IPC_Check_Installed_Reply, this.onCheckInstalledReply); ipcRenderer.on(Constants.IPC_Download_File_Complete, this.onDownloadFileComplete); ipcRenderer.on(Constants.IPC_Download_File_Progress, this.onDownloadFileProgress); ipcRenderer.on(Constants.IPC_Extract_Release_Complete, this.onExtractReleaseComplete); ipcRenderer.on(Constants.IPC_Get_State_Reply, this.onGetStateReply); ipcRenderer.on(Constants.IPC_Grab_Releases_Reply, this.onGrabReleasesReply); ipcRenderer.on(Constants.IPC_Grab_UI_Releases_Reply, this.onGrabUiReleasesReply); ipcRenderer.on(Constants.IPC_Install_Dependency_Reply, this.onInstallDependencyReply); ipcRenderer.on(Constants.IPC_Install_Upgrade_Reply, this.onInstallUpgradeReply); ipcRenderer.send(Constants.IPC_Get_State, Constants.DATA_LOCATIONS[this.props.platform]); Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob); } } state = { AllowDownload: false, AutoMountProcessed: false, ConfigStorageType: null, DisplayError: false, DisplayMainContent: false, Error: null, ErrorAction: null, ErrorCritical: false, DownloadActive: false, DownloadProgress: 0.0, DownloadingDependency: false, DownloadName: '', DownloadingRelease: false, DownloadingUpgrade: false, ExtractActive: false, LocationsLookup: {}, MissingDependencies: [], MountsBusy: false, Release: 3, ReleaseTypes: [ 'Release', 'RC', 'Beta', 'Alpha', ], InstalledVersion: 'none', UpgradeAvailable: false, UpgradeData: {}, UpgradeDismissed: false, Version: -1, VersionAvailable: false, VersionLookup: { Alpha: [ 'unavailable' ], Beta: [ 'unavailable' ], RC: [ 'unavailable' ], Release: [ 'unavailable' ], } }; checkVersionInstalled = () => { this.setState({ AllowDownload: false, }, ()=> { const selectedVersion = this.getSelectedVersion(); if (selectedVersion !== 'unavailable') { if (ipcRenderer) { let dependencies = []; if (this.state.LocationsLookup[selectedVersion] && this.state.LocationsLookup[selectedVersion].dependencies) { dependencies = this.state.LocationsLookup[selectedVersion].dependencies; } ipcRenderer.send(Constants.IPC_Check_Installed, { Dependencies: dependencies, Directory: Constants.DATA_LOCATIONS[this.props.platform], Version: selectedVersion, }); } } }); }; closeErrorDisplay = () => { if (this.state.ErrorAction) { this.state.ErrorAction(); } if (this.state.ErrorCritical) { if (ipcRenderer) { ipcRenderer.send(Constants.IPC_Shutdown); } } else { this.setState({ DisplayError: false, Error: null, }); } }; componentWillUnmount = () => { if (ipcRenderer) { ipcRenderer.removeListener(Constants.IPC_Check_Installed_Reply, this.onCheckInstalledReply); ipcRenderer.removeListener(Constants.IPC_Download_File_Complete, this.onDownloadFileComplete); ipcRenderer.removeListener(Constants.IPC_Download_File_Progress, this.onDownloadFileProgress); ipcRenderer.removeListener(Constants.IPC_Extract_Release_Complete, this.onExtractReleaseComplete); ipcRenderer.removeListener(Constants.IPC_Get_State_Reply, this.onGetStateReply); ipcRenderer.removeListener(Constants.IPC_Grab_Releases_Reply, this.onGrabReleasesReply); ipcRenderer.removeListener(Constants.IPC_Grab_UI_Releases_Reply, this.onGrabUiReleasesReply); ipcRenderer.removeListener(Constants.IPC_Install_Dependency_Reply, this.onInstallDependencyReply); ipcRenderer.removeListener(Constants.IPC_Install_Upgrade_Reply, this.onInstallUpgradeReply); } }; getSelectedVersion = () => { return this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version]; }; grabReleases = () => { if (this.props.platform !== 'unknown') { if (ipcRenderer) { ipcRenderer.send(Constants.IPC_Grab_Releases); ipcRenderer.send(Constants.IPC_Grab_UI_Releases); } } }; handleAutoMountChanged = (storageType, e) => { const state = { ...this.state[storageType], AutoMount: e.target.checked, }; this.setState({ [storageType]: state, }, ()=> { this.saveState(); }); }; handleAutoRestartChanged = (storageType, e) => { const state = { ...this.state[storageType], AutoRestart: e.target.checked, }; this.setState({ [storageType]: state, }, ()=> { this.saveState(); }); }; handleConfigClicked = (storageType) => { this.setState({ ConfigStorageType: storageType, }) }; handleConfigClosed = () => { this.setState({ ConfigStorageType: null, }); }; handleDependencyDownload = (url) => { if (ipcRenderer) { const items = url.split('/'); const fileName = items[items.length - 1]; this.setState({ DownloadActive: true, DownloadingDependency: true, DownloadName: fileName, }, ()=> { ipcRenderer.send(Constants.IPC_Download_File, { Directory: Constants.DATA_LOCATIONS[this.props.platform], Filename: this.state.DownloadName, URL: url, }); }); } }; handleMountLocationChanged = (storageType, location) => { const state = { ...this.state[storageType], MountLocation: location, }; this.setState({ [storageType]: state, }, ()=> { this.saveState(); }); }; handleReleaseChanged = (e) => { const val = parseInt(e.target.value, 10); const versionIndex = this.state.VersionLookup[this.state.ReleaseTypes[val]].length - 1; this.setState({ Release: val, Version: versionIndex }, ()=> { this.saveState(); this.checkVersionInstalled( ); }); }; handleReleaseDownload = () => { const selectedVersion = this.getSelectedVersion(); const fileName = selectedVersion + '.zip'; if (ipcRenderer) { this.setState({ DownloadActive: true, DownloadingRelease: true, DownloadName: fileName, }, () => { ipcRenderer.send(Constants.IPC_Download_File, { Directory: Constants.DATA_LOCATIONS[this.props.platform], Filename: this.state.DownloadName, URL: this.state.LocationsLookup[selectedVersion].urls[0], }); }); } }; handleUIDownload = () => { if (ipcRenderer) { this.setState({ DownloadActive: true, DownloadingUpgrade: true, DownloadName: 'UI Upgrade', }, ()=> { ipcRenderer.send(Constants.IPC_Download_File, { Directory: Constants.DATA_LOCATIONS[this.props.platform], Filename: this.props.platform === 'win32' ? 'upgrade.exe' : 'upgrade', URL: this.state.UpgradeData.urls[0], }); }); } else { this.setState({UpgradeDismissed: true}); } }; handleVersionChanged = (e) => { this.setState({ Version: parseInt(e.target.value, 10), }, ()=> { this.saveState(); this.checkVersionInstalled( ); }); }; notifyAutoMountProcessed = () => { this.setState({AutoMountProcessed: true}); }; notifyMountsBusy = (busy) => { this.setState({MountsBusy: busy}) }; onCheckInstalledReply = (event, arg) => { const action = () => { const installedVersion = arg.data.Success && arg.data.Exists ? arg.data.Version : 'none'; let versionAvailable = false; if (installedVersion !== 'none') { const latestVersion = this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]].length - 1; let version = this.state.Version; if (version === -1) { version = latestVersion; } versionAvailable = version !== latestVersion; } this.setState({ AllowDownload: true, DownloadingDependency: false, MissingDependencies: arg.data.Dependencies, InstalledVersion: installedVersion, VersionAvailable: versionAvailable, }); }; if (arg.data.Success) { action(); } else { this.setErrorState(arg.data.Error, action); } }; onDownloadFileComplete = (event, arg) => { if (this.state.DownloadingRelease) { if (arg.data.Success) { const selectedVersion = this.getSelectedVersion(); ipcRenderer.send(Constants.IPC_Extract_Release, { Directory: Constants.DATA_LOCATIONS[this.props.platform], Source: arg.data.Destination, Version: selectedVersion, }); } this.setState({ DownloadActive: false, DownloadProgress: 0.0, DownloadingRelease: false, ExtractActive: arg.data.Success, DownloadName: '', }); } else if (this.state.DownloadingDependency) { if (arg.data.Success) { ipcRenderer.send(Constants.IPC_Install_Dependency, { Source: arg.data.Destination, URL: arg.data.URL, }); } this.setState({ DownloadActive: false, DownloadProgress: 0.0, DownloadingDependency: arg.data.Success, DownloadName: '', }); } else if (this.state.DownloadingUpgrade) { if (arg.data.Success) { ipcRenderer.send(Constants.IPC_Install_Upgrade, { Source: arg.data.Destination, }); } else { this.setState({ DownloadActive: false, DownloadProgress: 0.0, DownloadingUpgrade: false, DownloadName: '', }); } } else { this.setState({ DownloadActive: false, DownloadProgress: 0.0, DownloadName: '', }); } }; onDownloadFileProgress = (event, arg) => { this.setState({ DownloadProgress: arg.data.Progress, }); }; onExtractReleaseComplete = (event, arg) => { ipcRenderer.send(Constants.IPC_Delete_File, { FilePath: arg.data.Source, }); this.setState({ ExtractActive: false, }, ()=> { this.checkVersionInstalled( ); }); }; onGetStateReply = (event, arg) => { if (arg.data) { let state = { Release: arg.data.Release, Version: arg.data.Version, }; for (const provider of Constants.PROVIDER_LIST) { let data = arg.data[provider] || this.state[provider]; if (data.AutoMount === undefined) { data['AutoMount'] = false; } if (data.AutoRestart === undefined) { data['AutoRestart'] = false; } state[provider] = data; } this.setState(state, ()=> { this.grabReleases(); }); } else { this.grabReleases(); } }; onGrabReleasesReply = ()=> { const doUpdate = (locationsLookup, versionLookup) => { const latestVersion = versionLookup[this.state.ReleaseTypes[this.state.Release]].length - 1; let version = this.state.Version; if ((version === -1) || !versionLookup[this.state.ReleaseTypes[this.state.Release]][version]) { version = latestVersion; this.saveState(version); } this.setState({ DisplayMainContent: true, LocationsLookup: locationsLookup, Version: version, VersionAvailable: version !== latestVersion, VersionLookup: versionLookup, }, () => { this.checkVersionInstalled( ); }); }; axios.get(Constants.RELEASES_URL) .then(response => { const versionLookup = { Alpha: response.data.Versions.Alpha[this.props.platform], Beta: response.data.Versions.Beta[this.props.platform], RC: response.data.Versions.RC[this.props.platform], Release: response.data.Versions.Release[this.props.platform], }; const locationsLookup = { ...response.data.Locations[this.props.platform], }; window.localStorage.setItem('releases', JSON.stringify({ LocationsLookup: locationsLookup, VersionLookup: versionLookup })); doUpdate(locationsLookup, versionLookup); }).catch(error => { const releases = window.localStorage.getItem('releases'); if (releases && (releases.length > 0)) { const obj = JSON.parse(releases); const locationsLookup = obj.LocationsLookup; const versionLookup = obj.VersionLookup; doUpdate(locationsLookup, versionLookup); } else { this.setErrorState(error, null, true); } }); }; onGrabUiReleasesReply = ()=> { axios.get(Constants.UI_RELEASES_URL) .then(response => { const data = response.data; if (data.Versions && data.Versions[this.props.platform] && (data.Versions[this.props.platform].length > 0) && (data.Versions[this.props.platform][0] !== this.props.version)) { this.setState({ UpgradeAvailable: true, UpgradeDismissed: false, UpgradeData: data.Locations[this.props.platform][data.Versions[this.props.platform][0]], }); } }).catch(() => { this.setState({ UpgradeAvailable: false, UpgradeData: {}, }); }); }; onInstallDependencyReply = (event, arg) => { if (arg.data.Success && arg.data.Source.toLowerCase().endsWith('.dmg')) { this.waitForDependencyInstall(arg.data.URL); } else { ipcRenderer.send(Constants.IPC_Delete_File, { FilePath: arg.data.Source, }); this.checkVersionInstalled(); } }; onInstallUpgradeReply = (event, arg) => { ipcRenderer.sendSync(Constants.IPC_Delete_File, { FilePath: arg.data.Source, }); this.setState({ DownloadActive: false, DownloadProgress: 0.0, DownloadName: '', }); }; saveState = version => { if (ipcRenderer) { let state = { Release: this.state.Release, Version: version || this.state.Version, }; for (const provider of Constants.PROVIDER_LIST) { state[provider] = this.state[provider]; } ipcRenderer.send(Constants.IPC_Save_State, { Directory: Constants.DATA_LOCATIONS[this.props.platform], State: state }); } }; setErrorState = (error, action, critical) => { this.setState({ DisplayError: true, Error: error, ErrorAction: action, ErrorCritical: critical, }); }; updateCheckScheduledJob = () => { if (this.props.platform !== 'unknown') { this.grabReleases(); } }; waitForDependencyInstall = (url) => { const dep = this.state.MissingDependencies.find(d => { return d.download === url; }); const i = setInterval(()=> { const ret = ipcRenderer.sendSync(Constants.IPC_Check_Dependency_Installed, { File: dep.file, }); if (ret.data.Exists || !ret.data.Success) { clearInterval(i); this.checkVersionInstalled(); } }, 3000); }; render() { const selectedVersion = (this.state.Version === -1) ? 'unavailable' : this.getSelectedVersion(); const downloadEnabled = this.state.AllowDownload && !this.state.MountsBusy && !this.state.DownloadActive && (selectedVersion !== 'unavailable') && (selectedVersion !== this.state.InstalledVersion); const allowMount = this.state.InstalledVersion !== 'none'; const missingDependencies = (this.state.MissingDependencies.length > 0); const allowConfig = this.state.LocationsLookup[selectedVersion] && this.state.LocationsLookup[selectedVersion].config_support; const allowSiaPrime = this.state.LocationsLookup[selectedVersion] && this.state.LocationsLookup[selectedVersion].siaprime_support; const showDependencies = missingDependencies && !this.state.DownloadActive; const showConfig = !missingDependencies && this.state.ConfigStorageType && allowConfig; const showUpgrade = this.state.UpgradeAvailable && !this.state.DisplayError && !showConfig && !missingDependencies && !this.state.DownloadActive && !this.state.UpgradeDismissed; let errorDisplay = null; if (this.state.DisplayError) { errorDisplay = ( ); } let configDisplay = null; if (showConfig) { configDisplay = ( ); } let dependencyDisplay = null; if (showDependencies) { dependencyDisplay = ( } ) } let downloadDisplay = null; if (this.state.DownloadActive) { downloadDisplay = ( ); } let upgradeDisplay = null; if (showUpgrade) { upgradeDisplay = ( this.setState({UpgradeDismissed: true})} upgrade={this.handleUIDownload}/> ); } let mainContent = []; if (this.state.DisplayMainContent) { let key = 0; mainContent.push((
)); if (allowMount) { let providerProps = {}; for (const provider of Constants.PROVIDER_LIST) { const providerLower = provider.toLowerCase(); providerProps[providerLower] = this.state[provider]; } mainContent.push((
)); } } else { mainContent = } return (
{errorDisplay} {dependencyDisplay} {upgradeDisplay} {downloadDisplay} {configDisplay}
this.setState({UpgradeDismissed: false})} col={dimensions => dimensions.columns - 6} colSpan={5} row={1} rowSpan={remain=>remain - 1}/>
{mainContent}
); } } export default CSSModules(App, styles, {allowMultiple: true});