484 lines
14 KiB
JavaScript
484 lines
14 KiB
JavaScript
import React from 'react';
|
|
import './MountItems.css';
|
|
import AddMount from '../AddMount/AddMount';
|
|
import Box from '../../components/UI/Box/Box';
|
|
import Button from '../../components/UI/Button/Button';
|
|
import IPCContainer from '../IPCContainer/IPCContainer';
|
|
import Modal from '../../components/UI/Modal/Modal';
|
|
import MountItem from './MountItem/MountItem';
|
|
import { connect } from 'react-redux';
|
|
import { notifyError } from '../../redux/actions/error_actions';
|
|
import {
|
|
resetMountsState,
|
|
setAllowMount,
|
|
setAutoMountProcessed,
|
|
setBusy,
|
|
setMounted,
|
|
setMountState,
|
|
setProviderState,
|
|
} from '../../redux/actions/mount_actions';
|
|
|
|
const Constants = require('../../constants');
|
|
|
|
class MountItems extends IPCContainer {
|
|
retryIntervals = {};
|
|
activeDetections = [];
|
|
|
|
state = {
|
|
DisplayRetry: false,
|
|
RetryItems: {},
|
|
};
|
|
|
|
addMountsBusy = (provider) => {
|
|
this.props.setMountsBusy(true);
|
|
this.activeDetections.push(provider);
|
|
};
|
|
|
|
cancelRetryMount = (provider, stateCallback) => {
|
|
clearInterval(this.retryIntervals[provider]);
|
|
delete this.retryIntervals[provider];
|
|
|
|
if (stateCallback) {
|
|
let retryItems = {
|
|
...this.state.RetryItems,
|
|
};
|
|
delete retryItems[provider];
|
|
this.setState(
|
|
{
|
|
DisplayRetry: Object.keys(retryItems).length > 0,
|
|
RetryItems: retryItems,
|
|
},
|
|
() => {
|
|
if (this.state.DisplayRetry) {
|
|
this.sendRequest(Constants.IPC_Show_Window);
|
|
}
|
|
stateCallback();
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
componentDidMount() {
|
|
this.setRequestHandler(Constants.IPC_Detect_Mount_Reply, this.onDetectMountReply);
|
|
this.setRequestHandler(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
|
|
this.setRequestHandler(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
|
|
this.props.resetMountsState();
|
|
this.detectMounts();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
Object.keys(this.state.RetryItems).forEach((provider) => {
|
|
this.cancelRetryMount(provider);
|
|
});
|
|
|
|
this.props.resetMountsState();
|
|
this.activeDetections = [];
|
|
super.componentWillUnmount();
|
|
}
|
|
|
|
detectMount = (provider) => {
|
|
this.addMountsBusy(provider);
|
|
|
|
this.sendRequest(Constants.IPC_Detect_Mount, {
|
|
RemoteMounts: this.props.RemoteMounts,
|
|
S3Mounts: this.props.S3Mounts,
|
|
Provider: provider,
|
|
Version: this.props.InstalledVersion,
|
|
});
|
|
};
|
|
|
|
detectMounts = () => {
|
|
if (!this.state.DisplayRetry) {
|
|
this.getProviderList().forEach((provider) => {
|
|
this.detectMount(provider);
|
|
});
|
|
}
|
|
};
|
|
|
|
displayRetryMount = (provider, remote, s3, mountLocation, msg) => {
|
|
if (!this.state.RetryItems[provider]) {
|
|
let retryItems = {
|
|
...this.state.RetryItems,
|
|
};
|
|
retryItems[provider] = {
|
|
RetrySeconds: 10,
|
|
RetryMessage: msg ? msg.toString() : null,
|
|
};
|
|
const mountState = {
|
|
AllowMount: false,
|
|
Mounted: false,
|
|
};
|
|
this.props.setMountState(provider, mountState);
|
|
|
|
this.setState(
|
|
{
|
|
DisplayRetry: true,
|
|
RetryItems: retryItems,
|
|
},
|
|
() => {
|
|
this.sendRequest(Constants.IPC_Show_Window);
|
|
this.retryIntervals[provider] = setInterval(() => {
|
|
const retryItems = {
|
|
...this.state.RetryItems,
|
|
};
|
|
const retrySeconds = retryItems[provider].RetrySeconds - 1;
|
|
if (retrySeconds === 0) {
|
|
this.cancelRetryMount(provider, () => {
|
|
this.handleMountUnMount(provider, remote, s3, true, mountLocation);
|
|
});
|
|
} else {
|
|
retryItems[provider].RetrySeconds = retrySeconds;
|
|
this.setState({
|
|
RetryItems: retryItems,
|
|
});
|
|
}
|
|
}, 1000);
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
handleBrowseLocation = (provider, location) => {
|
|
location = this.sendSyncRequest(Constants.IPC_Browse_Directory, {
|
|
Title: provider + ' Mount Location',
|
|
Location: location,
|
|
});
|
|
if (location && location.length > 0) {
|
|
this.handleMountLocationChanged(provider, location);
|
|
}
|
|
};
|
|
|
|
handleMountLocationChanged = (provider, mountLocation) => {
|
|
const state = {
|
|
...this.props.ProviderState[provider],
|
|
MountLocation: mountLocation,
|
|
};
|
|
this.props.setProviderState(provider, state);
|
|
};
|
|
|
|
handleMountUnMount = (provider, remote, s3, mount, location) => {
|
|
if (!location || location.trim().length === 0) {
|
|
this.props.notifyError('Mount location is not set');
|
|
} else {
|
|
let allowAction = true;
|
|
if (mount) {
|
|
let result =
|
|
remote || s3 || provider === 'Skynet'
|
|
? { Valid: true, Success: true }
|
|
: this.sendSyncRequest(Constants.IPC_Check_Daemon_Version, {
|
|
Provider: provider,
|
|
Remote: remote,
|
|
S3: s3,
|
|
Version: this.props.InstalledVersion,
|
|
}).data;
|
|
const displayRetry = (msg) => {
|
|
this.displayRetryMount(provider, remote, s3, location, msg);
|
|
};
|
|
if (result.Success) {
|
|
if (result.Valid) {
|
|
if (this.props.Platform !== 'win32') {
|
|
result = this.sendSyncRequest(Constants.IPC_Check_Mount_Location, {
|
|
Location: location,
|
|
});
|
|
if (!result.Success) {
|
|
allowAction = false;
|
|
this.props.notifyError(result.Error.toString());
|
|
}
|
|
}
|
|
} else {
|
|
allowAction = false;
|
|
if (
|
|
result.Code === new Uint32Array([-1])[0] ||
|
|
result.Code === new Uint8Array([-1])[0]
|
|
) {
|
|
displayRetry('Failed to connect to ' + provider + ' daemon');
|
|
} else if (
|
|
result.Code === new Uint32Array([-3])[0] ||
|
|
result.Code === new Uint8Array([-3])[0]
|
|
) {
|
|
displayRetry(
|
|
'Incompatible ' + provider + ' daemon. Please upgrade ' + provider + '.'
|
|
);
|
|
} else {
|
|
displayRetry('Version check failed: ' + result.Error);
|
|
}
|
|
}
|
|
} else {
|
|
allowAction = false;
|
|
if (this.props.Platform === 'win32') {
|
|
this.props.notifyError(
|
|
'Failed to launch repertory. Please install Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019.'
|
|
);
|
|
} else {
|
|
displayRetry('Version check failed: ' + result.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allowAction) {
|
|
this.addMountsBusy(provider);
|
|
this.props.setAllowMount(provider, false);
|
|
|
|
this.sendRequest(mount ? Constants.IPC_Mount_Drive : Constants.IPC_Unmount_Drive, {
|
|
Location: location,
|
|
Provider: provider,
|
|
Remote: remote,
|
|
S3: s3,
|
|
Version: this.props.InstalledVersion,
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
getProviderList = (providersOnly) => {
|
|
const providerList = Constants.PROVIDER_LIST.filter((i) => {
|
|
return (
|
|
(i === 'Skynet' && this.props.skynetSupported) ||
|
|
(i === 'ScPrime' && this.props.scPrimeSupported) ||
|
|
(i === 'Sia' && this.props.siaSupported)
|
|
);
|
|
});
|
|
|
|
let remoteList = [];
|
|
if (this.props.remoteSupported && !providersOnly) {
|
|
remoteList = [...this.props.RemoteMounts];
|
|
}
|
|
|
|
let s3List = [];
|
|
if (this.props.s3Supported && !providersOnly) {
|
|
s3List = [...this.props.S3Mounts];
|
|
}
|
|
|
|
return [...providerList, ...remoteList, ...s3List];
|
|
};
|
|
|
|
hasActiveMount = () => {
|
|
return !!Object.keys(this.props.MountState).find((provider) => {
|
|
return this.props.MountState[provider].Mounted;
|
|
});
|
|
};
|
|
|
|
onDetectMountReply = (_, arg) => {
|
|
const provider = arg.data.Provider;
|
|
if (!this.state.RetryItems[provider]) {
|
|
if (
|
|
arg.data.Success &&
|
|
(!arg.data.Active || (arg.data.Location && arg.data.Location.length > 0))
|
|
) {
|
|
const mountState = {
|
|
AllowMount: true,
|
|
DriveLetters: arg.data.DriveLetters,
|
|
Mounted: arg.data.Active,
|
|
};
|
|
this.props.setMountState(provider, mountState);
|
|
|
|
this.updateMountLocation(
|
|
provider,
|
|
arg.data.Location,
|
|
mountState.Mounted,
|
|
arg.data.DriveLetters
|
|
);
|
|
this.props.setAutoMountProcessed(provider, true);
|
|
this.removeMountsBusy(provider);
|
|
} else {
|
|
this.detectMount(provider);
|
|
this.removeMountsBusy(provider);
|
|
}
|
|
}
|
|
};
|
|
|
|
onMountDriveReply = (_, arg) => {
|
|
this.props.setMounted(arg.data.Provider, arg.data.Success);
|
|
this.detectMount(arg.data.Provider);
|
|
this.removeMountsBusy(arg.data.Provider);
|
|
};
|
|
|
|
onUnmountDriveReply = (_, arg) => {
|
|
if (
|
|
arg &&
|
|
arg.data &&
|
|
!arg.data.Expected &&
|
|
arg.data.Location &&
|
|
this.props.ProviderState[arg.data.Provider].AutoRestart
|
|
) {
|
|
this.displayRetryMount(arg.data.Provider, arg.data.Remote, arg.data.Location);
|
|
} else {
|
|
this.detectMount(arg.data.Provider);
|
|
}
|
|
this.removeMountsBusy(arg.data.Provider);
|
|
};
|
|
|
|
removeMountsBusy = (provider) => {
|
|
const idx = this.activeDetections.indexOf(provider);
|
|
if (idx > -1) {
|
|
this.activeDetections.splice(idx, 1);
|
|
}
|
|
this.props.setMountsBusy(this.activeDetections.length > 0 || this.hasActiveMount());
|
|
};
|
|
|
|
updateMountLocation = (provider, location, mounted, driveLetters) => {
|
|
const providerState = this.props.ProviderState[provider];
|
|
if (location.length === 0) {
|
|
location =
|
|
this.props.Platform === 'win32'
|
|
? !providerState.MountLocation || providerState.MountLocation.trim().length === 0
|
|
? driveLetters[0]
|
|
: providerState.MountLocation
|
|
: providerState.MountLocation;
|
|
}
|
|
|
|
if (location !== providerState.MountLocation) {
|
|
this.handleMountLocationChanged(provider, location);
|
|
}
|
|
|
|
if (
|
|
!this.props.AutoMountProcessed[provider] &&
|
|
this.props.ProviderState[provider].AutoMount &&
|
|
!mounted &&
|
|
location.length > 0
|
|
) {
|
|
this.handleMountUnMount(
|
|
provider,
|
|
this.props.RemoteMounts.includes(provider),
|
|
this.props.S3Mounts.includes(provider),
|
|
true,
|
|
location
|
|
);
|
|
}
|
|
};
|
|
|
|
render() {
|
|
let retryDisplay;
|
|
if (this.state.DisplayRetry) {
|
|
const retryList = [];
|
|
let retryCount = 0;
|
|
Object.keys(this.state.RetryItems).forEach((provider) => {
|
|
if (this.state.RetryItems[provider].RetryMessage) {
|
|
retryList.push(
|
|
<p key={'rl_' + retryList.length}>{this.state.RetryItems[provider].RetryMessage}</p>
|
|
);
|
|
}
|
|
retryList.push(
|
|
<Button
|
|
clicked={() => this.cancelRetryMount(provider, () => this.detectMounts())}
|
|
key={'rl_' + retryList.length}>
|
|
Cancel {provider} Remount ({this.state.RetryItems[provider].RetrySeconds}s)
|
|
</Button>
|
|
);
|
|
if (++retryCount < Object.keys(this.state.RetryItems).length) {
|
|
retryList.push(
|
|
<div style={{ paddingTop: 'var(--default_spacing)' }} key={'rl_' + retryList.length} />
|
|
);
|
|
}
|
|
});
|
|
|
|
retryDisplay = (
|
|
<Modal>
|
|
<Box dxDark dxStyle={{ padding: 'var(--default_spacing)', minWidth: '70vw' }}>
|
|
<h1
|
|
style={{
|
|
textAlign: 'center',
|
|
paddingBottom: 'var(--default_spacing)',
|
|
color: 'var(--text_color_error)',
|
|
}}>
|
|
Mount Failed
|
|
</h1>
|
|
{retryList}
|
|
</Box>
|
|
</Modal>
|
|
);
|
|
}
|
|
|
|
const footerItems = [];
|
|
if (this.props.remoteSupported || this.props.s3Supported) {
|
|
footerItems.push(
|
|
<AddMount
|
|
remoteSupported={this.props.remoteSupported}
|
|
s3Supported={this.props.s3Supported}
|
|
key={'hi_' + footerItems.length}
|
|
/>
|
|
);
|
|
} else {
|
|
footerItems.push(<div key={'hi_' + footerItems.length} style={{ height: '27px' }} />);
|
|
}
|
|
|
|
const mountItems = [];
|
|
const addMountItem = (provider, remote, s3) => {
|
|
if (mountItems.length > 0) {
|
|
mountItems.push(
|
|
<div
|
|
key={'it_' + mountItems.length}
|
|
style={{ paddingTop: 'calc(var(--default_spacing) * 2.5)' }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
mountItems.push(
|
|
<MountItem
|
|
allowRemove={remote || s3}
|
|
browseClicked={this.handleBrowseLocation}
|
|
changed={(e) => this.handleMountLocationChanged(provider, e.target.value)}
|
|
clicked={this.handleMountUnMount}
|
|
key={'it_' + mountItems.length}
|
|
provider={provider}
|
|
remote={remote}
|
|
s3={s3}
|
|
/>
|
|
);
|
|
};
|
|
|
|
this.getProviderList(true).forEach((provider) => addMountItem(provider));
|
|
|
|
if (this.props.remoteSupported) {
|
|
this.props.RemoteMounts.forEach((provider) => addMountItem(provider, true));
|
|
}
|
|
|
|
if (this.props.s3Supported) {
|
|
this.props.S3Mounts.forEach((provider) => addMountItem(provider, false, true));
|
|
}
|
|
|
|
return (
|
|
<div style={{ margin: 0, padding: 0 }}>
|
|
{retryDisplay}
|
|
<div
|
|
className={
|
|
this.props.remoteSupported || this.props.s3Supported ? 'MountItemsRemote' : 'MountItems'
|
|
}>
|
|
{mountItems}
|
|
</div>
|
|
<div style={{ paddingTop: 'var(--default_spacing)' }} />
|
|
{footerItems}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state) => {
|
|
return {
|
|
AutoMountProcessed: state.mounts.AutoMountProcessed,
|
|
InstalledVersion: state.relver.InstalledVersion,
|
|
MountState: state.mounts.MountState,
|
|
MountsBusy: state.mounts.MountsBusy,
|
|
Platform: state.common.Platform,
|
|
ProviderState: state.mounts.ProviderState,
|
|
RemoteMounts: state.mounts.RemoteMounts,
|
|
S3Mounts: state.mounts.S3Mounts,
|
|
};
|
|
};
|
|
|
|
const mapDispatchToProps = (dispatch) => {
|
|
return {
|
|
notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)),
|
|
resetMountsState: () => dispatch(resetMountsState()),
|
|
setAllowMount: (provider, allow) => dispatch(setAllowMount(provider, allow)),
|
|
setAutoMountProcessed: (provider, processed) =>
|
|
dispatch(setAutoMountProcessed(provider, processed)),
|
|
setMounted: (provider, mounted) => dispatch(setMounted(provider, mounted)),
|
|
setMountsBusy: (busy) => dispatch(setBusy(busy)),
|
|
setMountState: (provider, state) => dispatch(setMountState(provider, state)),
|
|
setProviderState: (provider, state) => dispatch(setProviderState(provider, state)),
|
|
};
|
|
};
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(MountItems);
|