This repository has been archived on 2025-09-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
repertory-ui/src/App.js
Scott E. Graves e95477f421 Refactoring
2018-12-08 23:36:43 -06:00

752 lines
23 KiB
JavaScript

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 = (
<Modal critical>
<ErrorDetails closed={this.closeErrorDisplay}
critical={this.state.ErrorCritical}
error={this.state.Error}/>
</Modal>
);
}
let configDisplay = null;
if (showConfig) {
configDisplay = (
<Modal>
<Configuration closed={this.handleConfigClosed}
directory={Constants.DATA_LOCATIONS[this.props.platform]}
errorHandler={this.setErrorState}
storageType={this.state.ConfigStorageType}
version={selectedVersion} />
</Modal>
);
}
let dependencyDisplay = null;
if (showDependencies) {
dependencyDisplay = (
<Modal>
<DependencyList allowDownload={!this.state.DownloadingDependency}
dependencies={this.state.MissingDependencies}
onDownload={this.handleDependencyDownload}/>
}
</Modal>
)
}
let downloadDisplay = null;
if (this.state.DownloadActive) {
downloadDisplay = (
<Modal>
<DownloadProgress display={this.state.DownloadName}
progress={this.state.DownloadProgress}/>
</Modal>);
}
let upgradeDisplay = null;
if (showUpgrade) {
upgradeDisplay = (
<Modal>
<UpgradeUI cancel={()=>this.setState({UpgradeDismissed: true})}
upgrade={this.handleUIDownload}/>
</Modal>
);
}
let mainContent = [];
if (this.state.DisplayMainContent) {
let key = 0;
mainContent.push((
<div key={'rvd_' + key++}
style={{height: '34%'}}>
<ReleaseVersionDisplay disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
downloadClicked={this.handleReleaseDownload}
downloadDisabled={!downloadEnabled}
installedVersion={this.state.InstalledVersion}
release={this.state.Release}
releaseChanged={this.handleReleaseChanged}
releaseExtracting={this.state.ExtractActive}
releaseTypes={this.state.ReleaseTypes}
version={this.state.Version}
versionAvailable={this.state.VersionAvailable}
versionChanged={this.handleVersionChanged}
versions={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}/>
</div>
));
if (allowMount) {
let providerProps = {};
for (const provider of Constants.PROVIDER_LIST) {
const providerLower = provider.toLowerCase();
providerProps[providerLower] = this.state[provider];
}
mainContent.push((
<div key={'md_' + key++}>
<MountItems {...providerProps}
allowConfig={allowConfig}
allowSiaPrime={allowSiaPrime}
autoMountChanged={this.handleAutoMountChanged}
autoMountProcessed={this.notifyAutoMountProcessed}
autoRestartChanged={this.handleAutoRestartChanged}
changed={this.handleMountLocationChanged}
configClicked={this.handleConfigClicked}
directory={Constants.DATA_LOCATIONS[this.props.platform]}
errorHandler={this.setErrorState}
mountsBusy={this.notifyMountsBusy}
platform={this.props.platform}
processAutoMount={!this.state.AutoMountProcessed}
version={this.state.InstalledVersion}/>
</div>
));
}
} else {
mainContent = <Loading/>
}
return (
<div styleName='App'>
{errorDisplay}
{dependencyDisplay}
{upgradeDisplay}
{downloadDisplay}
{configDisplay}
<div styleName='Container'>
<div styleName='Header'>
<Box>
<Grid>
<Text col={0}
colSpan={'remain'}
row={0}
rowSpan={'remain'}
text={'Repertory UI v' + this.props.version}
textAlign={'center'}
type={'Heading1'}/>
<UpgradeIcon
available={this.state.UpgradeAvailable}
clicked={()=>this.setState({UpgradeDismissed: false})}
col={dimensions => dimensions.columns - 6}
colSpan={5}
row={1}
rowSpan={remain=>remain - 1}/>
</Grid>
</Box>
</div>
<div styleName='Content'>
<Box dxStyle={{padding: '8px 8px 0px 8px'}}>
{mainContent}
</Box>
</div>
</div>
</div>
);
}
}
export default CSSModules(App, styles, {allowMultiple: true});