Merged 1.0.1_branch into master
1
.gitignore
vendored
@@ -4,3 +4,4 @@ node_modules/
|
||||
build/
|
||||
chrome_data/
|
||||
dist/
|
||||
/.cache
|
||||
|
||||
12
CHANGELOG.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Changelog #
|
||||
## 1.0.1 ##
|
||||
* Added configuration settings for Repertory 1.0.0-alpha.2 and above
|
||||
* Fixed memory leak on component unmount
|
||||
* Added error display
|
||||
* Lighter tray icon on Windows
|
||||
* Tray icon indicates mount status on Windows
|
||||
* Various fixes/layout changes
|
||||
|
||||
## 1.0.0 ##
|
||||
* Initial release
|
||||
* Windows support
|
||||
8
LICENSE.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Repertory UI MIT License #
|
||||
### Copyright <2018> <scott.e.graves@gmail.com> ###
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,6 +1,5 @@
|
||||
# Repertory UI
|
||||

|
||||
|
||||

|
||||
### GUI for [Repertory](https://bitbucket.org/blockstorage/repertory) ###
|
||||
Repertory allows you to mount Hyperspace or Sia blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.
|
||||
# Downloads #
|
||||
|
||||
278
electron.js
@@ -2,7 +2,7 @@
|
||||
|
||||
const {app, BrowserWindow, Tray, nativeImage, Menu} = require('electron');
|
||||
const {ipcMain} = require('electron');
|
||||
|
||||
const Constants = require('./src/constants');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
require('electron-debug')();
|
||||
@@ -14,17 +14,19 @@ const AutoLaunch = require('auto-launch');
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainContextWindow;
|
||||
let mainWindow;
|
||||
let mainWindowTray;
|
||||
let mountedPIDs = [];
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
const height = process.env.ELECTRON_START_URL ? 340 : 320;
|
||||
const height = process.env.ELECTRON_START_URL ? 324 : 304;
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 425,
|
||||
height: height,
|
||||
resizable: false,
|
||||
title: 'Repertory UI',
|
||||
webPreferences: {
|
||||
webSecurity: !process.env.ELECTRON_START_URL
|
||||
}
|
||||
@@ -46,14 +48,18 @@ function createWindow() {
|
||||
mainWindow = null
|
||||
});
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
if ((os.platform() === 'win32') || (os.platform() === 'linux')) {
|
||||
const appPath = (os.platform() === 'win32') ?
|
||||
path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')) :
|
||||
process.env.APPIMAGE;
|
||||
|
||||
const autoLauncher = new AutoLaunch({
|
||||
name: 'Repertory UI',
|
||||
path: path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')),
|
||||
path: appPath,
|
||||
});
|
||||
|
||||
const image = nativeImage.createFromPath(path.join(__dirname, '/build/icon.ico'));
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
const image = nativeImage.createFromPath(path.join(__dirname, '/build/logo.png'));
|
||||
mainContextWindow = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Visible', type: 'checkbox', click(item) {
|
||||
if (item.checked) {
|
||||
@@ -75,17 +81,26 @@ function createWindow() {
|
||||
autoLauncher.disable();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Exit', click(item) {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
contextMenu.items[0].checked = true;
|
||||
autoLauncher.isEnabled()
|
||||
mainContextWindow.items[0].checked = true;
|
||||
autoLauncher
|
||||
.isEnabled()
|
||||
.then((enabled) => {
|
||||
contextMenu.items[1].checked = enabled;
|
||||
mainContextWindow.items[1].checked = enabled;
|
||||
|
||||
mainWindowTray = new Tray(image);
|
||||
mainWindowTray.setToolTip('Repertory UI');
|
||||
mainWindowTray.setContextMenu(contextMenu)
|
||||
mainWindowTray.setContextMenu(mainContextWindow)
|
||||
})
|
||||
.catch(() => {
|
||||
app.quit();
|
||||
@@ -99,6 +114,10 @@ if (!instanceLock) {
|
||||
} else {
|
||||
app.on('second-instance', () => {
|
||||
if (mainWindow) {
|
||||
mainWindow.show();
|
||||
if (mainContextWindow) {
|
||||
mainContextWindow.items[0].checked = true;
|
||||
}
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
@@ -125,7 +144,17 @@ if (!instanceLock) {
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.on('check_installed', (event, data) => {
|
||||
const standardIPCReply = (event, channel, data, error) => {
|
||||
event.sender.send(channel, {
|
||||
data: {
|
||||
...data,
|
||||
Error: error,
|
||||
Success: !error,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ipcMain.on(Constants.IPC_Check_Installed, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
const destination = path.join(dataDirectory, data.Version);
|
||||
helpers
|
||||
@@ -136,27 +165,20 @@ ipcMain.on('check_installed', (event, data) => {
|
||||
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
|
||||
} catch (e) {
|
||||
}
|
||||
event.sender.send('check_installed_reply', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
|
||||
Dependencies: dependencies,
|
||||
Exists: exists,
|
||||
Success: true,
|
||||
Version: data.Version,
|
||||
}
|
||||
});
|
||||
}).catch((e) => {
|
||||
event.sender.send('check_installed_reply', {
|
||||
data: {
|
||||
}).catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
|
||||
Dependencies: [],
|
||||
Error: e,
|
||||
Success: false,
|
||||
Version: data.Version,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('delete_file', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Delete_File, (event, data) => {
|
||||
try {
|
||||
if (fs.existsSync(data.FilePath)) {
|
||||
fs.unlinkSync(data.FilePath);
|
||||
@@ -165,7 +187,7 @@ ipcMain.on('delete_file', (event, data) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('detect_mounts', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Detect_Mounts, (event, data) => {
|
||||
let driveLetters = {
|
||||
Hyperspace: [],
|
||||
Sia: [],
|
||||
@@ -202,6 +224,23 @@ ipcMain.on('detect_mounts', (event, data) => {
|
||||
}
|
||||
};
|
||||
|
||||
const setImage = (hsLocation, siaLocation) => {
|
||||
if (os.platform() === 'win32') {
|
||||
let image;
|
||||
if ((siaLocation.length > 0) && (hsLocation.length > 0)) {
|
||||
image = nativeImage.createFromPath(path.join(__dirname, '/build/logo_both.png'));
|
||||
} else if (hsLocation.length > 0) {
|
||||
image = nativeImage.createFromPath(path.join(__dirname, '/build/logo_hs.png'));
|
||||
} else if (siaLocation.length > 0) {
|
||||
image = nativeImage.createFromPath(path.join(__dirname, '/build/logo_sia.png'));
|
||||
} else {
|
||||
image = nativeImage.createFromPath(path.join(__dirname, '/build/logo.png'));
|
||||
}
|
||||
|
||||
mainWindowTray.setImage(image);
|
||||
}
|
||||
};
|
||||
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
helpers
|
||||
.detectRepertoryMounts(dataDirectory, data.Version)
|
||||
@@ -213,201 +252,222 @@ ipcMain.on('detect_mounts', (event, data) => {
|
||||
siaLocation = siaLocation.toUpperCase();
|
||||
grabDriveLetters(hsLocation, siaLocation);
|
||||
}
|
||||
event.sender.send('detect_mounts_reply', {
|
||||
data: {
|
||||
setImage(hsLocation, siaLocation);
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
|
||||
DriveLetters: driveLetters,
|
||||
Locations: {
|
||||
Hyperspace: hsLocation,
|
||||
Sia: siaLocation,
|
||||
},
|
||||
Success: true,
|
||||
PIDS: {
|
||||
Hyperspace: results.Hyperspace.PID,
|
||||
Sia: results.Sia.PID,
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(error => {
|
||||
grabDriveLetters('', '');
|
||||
|
||||
event.sender.send('detect_mounts_reply', {
|
||||
data: {
|
||||
setImage('', '');
|
||||
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
|
||||
DriveLetters: driveLetters,
|
||||
Error: err,
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('download_file', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
const destination = path.join(dataDirectory, data.Filename);
|
||||
helpers.downloadFile(data.URL, destination, (progress) => {
|
||||
event.sender.send('download_file_progress', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Download_File_Progress, {
|
||||
Destination: destination,
|
||||
Progress: progress,
|
||||
URL: data.URL,
|
||||
}
|
||||
});
|
||||
}, (success, err) => {
|
||||
event.sender.send('download_file_complete', {
|
||||
data: {
|
||||
}, error => {
|
||||
standardIPCReply(event, Constants.IPC_Download_File_Complete, {
|
||||
Destination: destination,
|
||||
Error: err,
|
||||
Success: success,
|
||||
URL: data.URL,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('extract_release', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Extract_Release, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
const destination = path.join(dataDirectory, data.Version);
|
||||
helpers.mkDirByPathSync(destination);
|
||||
|
||||
const stream = fs.createReadStream(data.Source);
|
||||
stream.pipe(unzip.Extract({ path: destination }))
|
||||
.on('error', (e) => {
|
||||
stream
|
||||
.pipe(unzip.Extract({ path: destination }))
|
||||
.on('error', error => {
|
||||
try {
|
||||
helpers.removeDirectoryRecursively(destination);
|
||||
} catch (e) {
|
||||
}
|
||||
stream.close();
|
||||
event.sender.send('extract_release_complete', {
|
||||
data: {
|
||||
Error: e,
|
||||
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
|
||||
Source: data.Source,
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
})
|
||||
.on('finish', () => {
|
||||
stream.close();
|
||||
event.sender.send('extract_release_complete', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
|
||||
Source: data.Source,
|
||||
Success: true,
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('get_platform', (event) => {
|
||||
event.sender.send('get_platform_reply', {
|
||||
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
helpers
|
||||
.getConfig(dataDirectory, data.Version, data.StorageType)
|
||||
.then((data) => {
|
||||
if (data.Code === 0) {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {
|
||||
Config: data.Data,
|
||||
});
|
||||
} else {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, data.Code);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Reply, {}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
helpers
|
||||
.getConfigTemplate(dataDirectory, data.Version, data.StorageType)
|
||||
.then((data) => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {
|
||||
Template: data,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Get_Config_Template_Reply, {}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Get_Platform, (event) => {
|
||||
event.sender.send(Constants.IPC_Get_Platform_Reply, {
|
||||
data: os.platform()
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('get_state', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Get_State, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data);
|
||||
helpers.mkDirByPathSync(dataDirectory);
|
||||
const configFile = path.join(dataDirectory, 'settings.json');
|
||||
if (fs.existsSync(configFile)) {
|
||||
event.sender.send('get_state_reply', {
|
||||
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||
data: JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
||||
});
|
||||
} else {
|
||||
event.sender.send('get_state_reply', {
|
||||
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('grab_releases', (event) => {
|
||||
event.sender.send('grab_releases_reply');
|
||||
ipcMain.on(Constants.IPC_Grab_Releases, (event) => {
|
||||
standardIPCReply(event, Constants.IPC_Grab_Releases_Reply);
|
||||
});
|
||||
|
||||
ipcMain.on('grab_ui_releases', (event) => {
|
||||
event.sender.send('grab_ui_releases_reply');
|
||||
ipcMain.on(Constants.IPC_Grab_UI_Releases, (event) => {
|
||||
standardIPCReply(event, Constants.IPC_Grab_UI_Releases_Reply);
|
||||
});
|
||||
|
||||
ipcMain.on('install_dependency', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
|
||||
helpers
|
||||
.executeAndWait(data.Source)
|
||||
.then(()=> {
|
||||
event.sender.send('install_dependency_reply', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
|
||||
Source: data.Source,
|
||||
Success: true,
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((e)=> {
|
||||
event.sender.send('install_dependency_reply', {
|
||||
data: {
|
||||
Error: e,
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
|
||||
Source: data.Source,
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('install_upgrade', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
|
||||
helpers
|
||||
.executeAsync(data.Source)
|
||||
.then(()=> {
|
||||
mainWindow.close();
|
||||
})
|
||||
.catch((e)=> {
|
||||
event.sender.send('install_upgrade_reply', {
|
||||
data: {
|
||||
Error: e,
|
||||
.catch(error => {
|
||||
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, {
|
||||
Source: data.Source,
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
}, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('mount_drive', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
const errorHandler = (pid) => {
|
||||
const errorHandler = (pid, error) => {
|
||||
mountedPIDs.splice(mountedPIDs.indexOf(pid), 1);
|
||||
event.sender.send('unmount_drive_reply', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, {
|
||||
PID: -1,
|
||||
StorageType: data.StorageType,
|
||||
Success: false,
|
||||
}
|
||||
});
|
||||
}, error || Error(data.StorageType + ' Unmounted'));
|
||||
};
|
||||
helpers.executeMount(dataDirectory, data.Version, data.StorageType, data.Location, (_, pid)=> {
|
||||
errorHandler(pid);
|
||||
helpers.executeMount(dataDirectory, data.Version, data.StorageType, data.Location, (error, pid) => {
|
||||
errorHandler(pid, error);
|
||||
})
|
||||
.then(pid=> {
|
||||
.then(pid => {
|
||||
if (pid !== -1) {
|
||||
mountedPIDs.push(pid);
|
||||
}
|
||||
event.sender.send('mount_drive_reply', {
|
||||
data: {
|
||||
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
|
||||
PID: pid,
|
||||
StorageType: data.StorageType,
|
||||
Success: true,
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((_, pid) => {
|
||||
errorHandler(pid);
|
||||
.catch(error => {
|
||||
errorHandler(-1, error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('save_state', (event, data) => {
|
||||
ipcMain.on(Constants.IPC_Save_State, (event, data) => {
|
||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||
helpers.mkDirByPathSync(dataDirectory);
|
||||
const configFile = path.join(dataDirectory, 'settings.json');
|
||||
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
||||
});
|
||||
|
||||
ipcMain.on('unmount_drive', (event, data) => {
|
||||
helpers.stopProcessByPID(data.PID)
|
||||
ipcMain.on(Constants.IPC_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 {
|
||||
standardIPCReply(event, Constants.IPC_Set_Config_Values_Reply, {});
|
||||
}
|
||||
};
|
||||
setConfigValue(0);
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Shutdown, () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
ipcMain.on(Constants.IPC_Unmount_Drive, (event, data) => {
|
||||
helpers
|
||||
.stopProcessByPID(data.PID)
|
||||
.then((pid)=> {
|
||||
if (mountedPIDs.indexOf(pid) === -1) {
|
||||
event.sender.send('unmount_drive_reply');
|
||||
event.sender.send(Constants.IPC_Unmount_Drive_Reply);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
|
||||
116
helpers.js
@@ -80,18 +80,18 @@ module.exports.downloadFile = (url, destination, progressCallback, completeCallb
|
||||
|
||||
response.data.on('end', () => {
|
||||
stream.end(() => {
|
||||
completeCallback(true);
|
||||
completeCallback();
|
||||
});
|
||||
});
|
||||
|
||||
response.data.on('error', (e) => {
|
||||
stream.end(() => {
|
||||
completeCallback(false, e);
|
||||
completeCallback(e);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((e)=> {
|
||||
completeCallback(false, e);
|
||||
completeCallback(e);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "repertory-ui",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.1",
|
||||
@@ -47,6 +47,7 @@
|
||||
"react-dev-utils": "^5.0.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-loader-spinner": "^2.0.6",
|
||||
"react-tooltip": "^3.8.4",
|
||||
"resolve": "1.6.0",
|
||||
"style-loader": "0.19.0",
|
||||
"sw-precache-webpack-plugin": "0.11.4",
|
||||
@@ -68,7 +69,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.2.0",
|
||||
"electron": "3.0.0",
|
||||
"electron": "^3.0.2",
|
||||
"electron-builder": "^20.28.4",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"webpack-browser-plugin": "^1.0.20"
|
||||
@@ -121,6 +122,7 @@
|
||||
"appId": "repertory-ui",
|
||||
"files": [
|
||||
"./electron.js",
|
||||
"./src/constants.js",
|
||||
"build/**/*",
|
||||
"node_modules/**/*",
|
||||
"./helpers.js"
|
||||
|
||||
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
BIN
public/favicon_old.ico
Normal file
|
After Width: | Height: | Size: 361 KiB |
BIN
public/icon.ico
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
BIN
public/icon_old.ico
Normal file
|
After Width: | Height: | Size: 361 KiB |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/logo_both.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/logo_hs.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/logo_sia.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"Locations": {
|
||||
"win32": {
|
||||
"1.0.1": {
|
||||
"hash": "",
|
||||
"urls": [
|
||||
"https://sia.pixeldrain.com/api/file/Alo1IF1u/download"
|
||||
]
|
||||
},
|
||||
"1.0.0": {
|
||||
"hash": "",
|
||||
"urls": [
|
||||
@@ -11,6 +17,7 @@
|
||||
},
|
||||
"Versions": {
|
||||
"win32": [
|
||||
"1.0.1",
|
||||
"1.0.0"
|
||||
]
|
||||
}
|
||||
|
||||
19
src/App.css
@@ -9,3 +9,22 @@
|
||||
background-image: url('./assets/images/background.jpg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.Container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.Header {
|
||||
height: 28px;
|
||||
margin-bottom: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.Content {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
744
src/App.js
@@ -1,18 +1,22 @@
|
||||
import React, {Component} from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import axios from 'axios';
|
||||
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 Configuration from './containers/Configuration/Configuration';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import DependencyList from './components/DependencyList/DependencyList';
|
||||
import Button from './components/UI/Button/Button';
|
||||
import Modal from './components/UI/Modal/Modal';
|
||||
import DownloadProgress from './components/DownloadProgress/DownloadProgress';
|
||||
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
|
||||
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;
|
||||
@@ -25,180 +29,30 @@ class App extends Component {
|
||||
super(props);
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.on('get_platform_reply', (event, arg) => {
|
||||
this.setState({
|
||||
Platform: arg.data,
|
||||
});
|
||||
ipcRenderer.send('get_state', Constants.DATA_LOCATIONS[arg.data]);
|
||||
});
|
||||
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.on('get_state_reply', (event, arg) => {
|
||||
if (arg.data) {
|
||||
if (arg.data.Hyperspace.AutoMount === undefined) {
|
||||
arg.data.Hyperspace['AutoMount'] = false;
|
||||
}
|
||||
if (arg.data.Sia.AutoMount === undefined) {
|
||||
arg.data.Sia['AutoMount'] = false;
|
||||
}
|
||||
this.setState({
|
||||
Hyperspace: arg.data.Hyperspace,
|
||||
Release: arg.data.Release,
|
||||
Sia: arg.data.Sia,
|
||||
Version: arg.data.Version,
|
||||
});
|
||||
}
|
||||
this.grabReleases();
|
||||
});
|
||||
|
||||
ipcRenderer.on('grab_releases_reply', ()=> {
|
||||
axios.get(Constants.RELEASES_URL)
|
||||
.then(response => {
|
||||
const versionLookup = {
|
||||
Alpha: response.data.Versions.Alpha[this.state.Platform],
|
||||
Beta: response.data.Versions.Beta[this.state.Platform],
|
||||
RC: response.data.Versions.RC[this.state.Platform],
|
||||
Release: response.data.Versions.Release[this.state.Platform],
|
||||
};
|
||||
const locationsLookup = {
|
||||
...response.data.Locations[this.state.Platform],
|
||||
};
|
||||
this.setState({
|
||||
AllowOptions: true,
|
||||
LocationsLookup: locationsLookup,
|
||||
VersionLookup: versionLookup,
|
||||
});
|
||||
this.checkVersionInstalled(this.state.Release, this.state.Version, versionLookup);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('grab_ui_releases_reply', ()=> {
|
||||
axios.get(Constants.UI_RELEASES_URL)
|
||||
.then(response => {
|
||||
const data = response.data;
|
||||
if (data.Versions &&
|
||||
data.Versions[this.state.Platform] &&
|
||||
(data.Versions[this.state.Platform].length > 0) &&
|
||||
(data.Versions[this.state.Platform][0] !== this.props.version)) {
|
||||
this.setState({
|
||||
UpgradeAvailable: true,
|
||||
UpgradeDismissed: false,
|
||||
UpgradeData: data.Locations[this.state.Platform][data.Versions[this.state.Platform][0]],
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('download_file_progress', (event, arg) => {
|
||||
this.setState({
|
||||
DownloadProgress: arg.data.Progress,
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('download_file_complete', (event, arg) => {
|
||||
if (this.state.DownloadingRelease) {
|
||||
if (arg.data.Success) {
|
||||
const selectedVersion = this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||
ipcRenderer.send('extract_release', {
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.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('install_dependency', {
|
||||
Source: arg.data.Destination,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
DownloadActive: false,
|
||||
DownloadProgress: 0.0,
|
||||
DownloadingDependency: arg.data.Success,
|
||||
DownloadName: '',
|
||||
});
|
||||
} else if (this.state.DownloadingUpgrade) {
|
||||
if (arg.data.Success) {
|
||||
ipcRenderer.send('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: '',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on('extract_release_complete', (event, arg) => {
|
||||
ipcRenderer.send('delete_file', {
|
||||
FilePath: arg.data.Source,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
ExtractActive: false,
|
||||
});
|
||||
this.checkVersionInstalled(this.state.Release, this.state.Version);
|
||||
});
|
||||
|
||||
ipcRenderer.on('check_installed_reply', (event, arg) => {
|
||||
this.setState({
|
||||
AllowDownload: true,
|
||||
DownloadingDependency: false,
|
||||
MissingDependencies: arg.data.Dependencies,
|
||||
RepertoryVersion: arg.data.Success && arg.data.Exists ? arg.data.Version : 'none',
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('install_dependency_reply', (event, arg) => {
|
||||
ipcRenderer.send('delete_file', {
|
||||
FilePath: arg.data.Source,
|
||||
});
|
||||
this.checkVersionInstalled(this.state.Release, this.state.Version);
|
||||
});
|
||||
|
||||
ipcRenderer.on('install_upgrade_reply', (event, arg) => {
|
||||
ipcRenderer.sendSync('delete_file', {
|
||||
FilePath: arg.data.Source,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
DownloadActive: false,
|
||||
DownloadProgress: 0.0,
|
||||
DownloadName: '',
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.send('get_platform');
|
||||
ipcRenderer.send(Constants.IPC_Get_State, Constants.DATA_LOCATIONS[this.props.platform]);
|
||||
Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob);
|
||||
}
|
||||
}
|
||||
|
||||
state = {
|
||||
AllowOptions: false,
|
||||
AllowDownload: false,
|
||||
AutoMountChecked: false,
|
||||
AutoMountProcessed: false,
|
||||
ConfigStorageType: null,
|
||||
DisplayError: false,
|
||||
DisplayMainContent: false,
|
||||
Error: null,
|
||||
ErrorAction: null,
|
||||
ErrorCritical: false,
|
||||
DownloadActive: false,
|
||||
DownloadProgress: 0.0,
|
||||
DownloadingDependency: false,
|
||||
@@ -212,6 +66,7 @@ class App extends Component {
|
||||
},
|
||||
LocationsLookup: {},
|
||||
MissingDependencies: [],
|
||||
MountsBusy: false,
|
||||
Platform: 'unknown',
|
||||
Release: 3,
|
||||
ReleaseTypes: [
|
||||
@@ -220,7 +75,7 @@ class App extends Component {
|
||||
'Beta',
|
||||
'Alpha',
|
||||
],
|
||||
RepertoryVersion: 'none',
|
||||
InstalledVersion: 'none',
|
||||
Sia: {
|
||||
AutoMount: false,
|
||||
MountLocation: '',
|
||||
@@ -228,7 +83,8 @@ class App extends Component {
|
||||
UpgradeAvailable: false,
|
||||
UpgradeData: {},
|
||||
UpgradeDismissed: false,
|
||||
Version: 0,
|
||||
Version: -1,
|
||||
VersionAvailable: false,
|
||||
VersionLookup: {
|
||||
Alpha: [
|
||||
'unavailable'
|
||||
@@ -255,25 +111,58 @@ class App extends Component {
|
||||
AllowDownload: false,
|
||||
});
|
||||
|
||||
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('check_installed', {
|
||||
ipcRenderer.send(Constants.IPC_Check_Installed, {
|
||||
Dependencies: dependencies,
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
||||
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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
grabReleases = () => {
|
||||
if (this.state.Platform !== 'unknown') {
|
||||
componentWillUnmount = () => {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send('grab_releases');
|
||||
ipcRenderer.send('grab_ui_releases');
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
grabReleases = () => {
|
||||
if (this.props.platform !== 'unknown') {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send(Constants.IPC_Grab_Releases);
|
||||
ipcRenderer.send(Constants.IPC_Grab_UI_Releases);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -302,6 +191,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('/');
|
||||
@@ -313,8 +214,8 @@ class App extends Component {
|
||||
DownloadName: fileName,
|
||||
});
|
||||
|
||||
ipcRenderer.send('download_file', {
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
||||
ipcRenderer.send(Constants.IPC_Download_File, {
|
||||
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||
Filename: fileName,
|
||||
URL: url,
|
||||
});
|
||||
@@ -341,12 +242,13 @@ class App extends Component {
|
||||
|
||||
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: 0
|
||||
Version: versionIndex
|
||||
});
|
||||
this.saveState(val, 0, this.state.Sia, this.state.Hyperspace);
|
||||
this.checkVersionInstalled(val, 0);
|
||||
this.saveState(val, versionIndex, this.state.Sia, this.state.Hyperspace);
|
||||
this.checkVersionInstalled(val, versionIndex);
|
||||
};
|
||||
|
||||
handleReleaseDownload = () => {
|
||||
@@ -359,8 +261,8 @@ class App extends Component {
|
||||
DownloadName: fileName,
|
||||
});
|
||||
|
||||
ipcRenderer.send('download_file', {
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
||||
ipcRenderer.send(Constants.IPC_Download_File, {
|
||||
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||
Filename: fileName,
|
||||
URL: this.state.LocationsLookup[selectedVersion].urls[0],
|
||||
});
|
||||
@@ -375,9 +277,9 @@ class App extends Component {
|
||||
DownloadName: 'UI Upgrade',
|
||||
});
|
||||
|
||||
ipcRenderer.send('download_file', {
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
||||
Filename: this.state.Platform === 'win32' ? 'upgrade.exe' : '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 {
|
||||
@@ -395,13 +297,228 @@ class App extends Component {
|
||||
};
|
||||
|
||||
notifyAutoMountProcessed = () => {
|
||||
this.setState({AutoMountChecked: true});
|
||||
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.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
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(this.state.Release, this.state.Version);
|
||||
};
|
||||
|
||||
onGetStateReply = (event, arg) => {
|
||||
if (arg.data) {
|
||||
if (arg.data.Hyperspace.AutoMount === undefined) {
|
||||
arg.data.Hyperspace['AutoMount'] = false;
|
||||
}
|
||||
if (arg.data.Sia.AutoMount === undefined) {
|
||||
arg.data.Sia['AutoMount'] = false;
|
||||
}
|
||||
this.setState({
|
||||
Hyperspace: arg.data.Hyperspace,
|
||||
Release: arg.data.Release,
|
||||
Sia: arg.data.Sia,
|
||||
Version: arg.data.Version,
|
||||
});
|
||||
}
|
||||
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(this.state.Release, version, this.state.Sia, this.state.Hyperspace);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
DisplayMainContent: true,
|
||||
LocationsLookup: locationsLookup,
|
||||
Version: version,
|
||||
VersionAvailable: version !== latestVersion,
|
||||
VersionLookup: versionLookup,
|
||||
});
|
||||
|
||||
this.checkVersionInstalled(this.state.Release, version, versionLookup);
|
||||
};
|
||||
|
||||
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) => {
|
||||
ipcRenderer.send(Constants.IPC_Delete_File, {
|
||||
FilePath: arg.data.Source,
|
||||
});
|
||||
this.checkVersionInstalled(this.state.Release, this.state.Version);
|
||||
};
|
||||
|
||||
onInstallUpgradeReply = (event, arg) => {
|
||||
ipcRenderer.sendSync(Constants.IPC_Delete_File, {
|
||||
FilePath: arg.data.Source,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
DownloadActive: false,
|
||||
DownloadProgress: 0.0,
|
||||
DownloadName: '',
|
||||
});
|
||||
};
|
||||
|
||||
saveState = (release, version, sia, hyperspace)=> {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send('save_state', {
|
||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
||||
ipcRenderer.send(Constants.IPC_Save_State, {
|
||||
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||
State: {
|
||||
Hyperspace: hyperspace,
|
||||
Release: release,
|
||||
@@ -412,38 +529,77 @@ class App extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
setErrorState = (error, action, critical) => {
|
||||
this.setState({
|
||||
DisplayError: true,
|
||||
Error: error,
|
||||
ErrorAction: action,
|
||||
ErrorCritical: critical,
|
||||
});
|
||||
};
|
||||
|
||||
updateCheckScheduledJob = () => {
|
||||
if (this.state.Platform !== 'unknown') {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send('grab_ui_releases');
|
||||
}
|
||||
if (this.props.platform !== 'unknown') {
|
||||
this.grabReleases();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const selectedVersion = this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||
const downloadEnabled = this.state.AllowDownload &&
|
||||
!this.state.DownloadActive &&
|
||||
(((selectedVersion !== 'unavailable') && (selectedVersion !== this.state.RepertoryVersion)));
|
||||
const allowMount = this.state.RepertoryVersion !== 'none';
|
||||
const missingDependencies = (this.state.MissingDependencies.length > 0);
|
||||
const selectedVersion = (this.state.Version === -1) ?
|
||||
'unavailable' :
|
||||
this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||
|
||||
let mountDisplay = null;
|
||||
if (allowMount) {
|
||||
mountDisplay = <MountItems platform={this.state.Platform}
|
||||
sia={this.state.Sia}
|
||||
hyperspace={this.state.Hyperspace}
|
||||
changed={this.handleMountLocationChanged}
|
||||
processAutoMount={!this.state.AutoMountChecked}
|
||||
autoMountProcessed={this.notifyAutoMountProcessed}
|
||||
autoMountChanged={this.handleAutoMountChanged}
|
||||
version={this.state.RepertoryVersion}
|
||||
directory={Constants.DATA_LOCATIONS[this.state.Platform]}
|
||||
disabled={!allowMount}/>;
|
||||
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 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 (missingDependencies && !this.state.DownloadActive) {
|
||||
if (showDependencies) {
|
||||
dependencyDisplay = (
|
||||
<Modal>
|
||||
<DependencyList allowDownload={!this.state.DownloadingDependency}
|
||||
@@ -458,105 +614,101 @@ class App extends Component {
|
||||
if (this.state.DownloadActive) {
|
||||
downloadDisplay = (
|
||||
<Modal>
|
||||
<DownloadProgress progress={this.state.DownloadProgress}
|
||||
display={this.state.DownloadName}/>
|
||||
<DownloadProgress display={this.state.DownloadName}
|
||||
progress={this.state.DownloadProgress}/>
|
||||
</Modal>);
|
||||
}
|
||||
|
||||
let releaseDisplay = null;
|
||||
if (this.state.ExtractActive) {
|
||||
releaseDisplay = <h3 style={{textAlign: 'center'}}>{'Activating <' + selectedVersion + '>'}</h3>
|
||||
} else {
|
||||
releaseDisplay = <Button disabled={!downloadEnabled}
|
||||
clicked={this.handleReleaseDownload}>Install</Button>;
|
||||
}
|
||||
|
||||
let upgradeDisplay = null;
|
||||
if (!missingDependencies &&
|
||||
!this.state.DownloadActive &&
|
||||
this.state.UpgradeAvailable &&
|
||||
!this.state.UpgradeDismissed) {
|
||||
if (showUpgrade) {
|
||||
upgradeDisplay = (
|
||||
<Modal>
|
||||
<UpgradeUI upgrade={this.handleUIDownload}
|
||||
cancel={()=>this.setState({UpgradeDismissed: true})}/>
|
||||
<UpgradeUI cancel={()=>this.setState({UpgradeDismissed: true})}
|
||||
upgrade={this.handleUIDownload}/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
let options = null;
|
||||
if (this.state.AllowOptions) {
|
||||
options = (
|
||||
<table width='100%' cellPadding='2'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width='33%'>
|
||||
<h2>Release</h2>
|
||||
</td>
|
||||
<td width='33%'>
|
||||
<h2>Version</h2>
|
||||
</td>
|
||||
<td width='33%'>
|
||||
<h2>Installed</h2>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive}
|
||||
items={this.state.ReleaseTypes}
|
||||
selected={this.state.Release}
|
||||
changed={this.handleReleaseChanged}/>
|
||||
</td>
|
||||
<td>
|
||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive}
|
||||
items={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}
|
||||
selected={this.state.Version}
|
||||
changed={this.handleVersionChanged}/>
|
||||
</td>
|
||||
<td>
|
||||
{this.state.RepertoryVersion}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
{releaseDisplay}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
{mountDisplay}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>);
|
||||
let mainContent = [];
|
||||
if (this.state.DisplayMainContent) {
|
||||
let key = 0;
|
||||
mainContent.push((
|
||||
<div key={'rvd_' + key++}
|
||||
style={{height: '44%'}}>
|
||||
<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) {
|
||||
mainContent.push((
|
||||
<div key={'md_' + key++}
|
||||
style={{height: '56%'}}>
|
||||
<MountItems allowConfig={allowConfig}
|
||||
autoMountChanged={this.handleAutoMountChanged}
|
||||
autoMountProcessed={this.notifyAutoMountProcessed}
|
||||
changed={this.handleMountLocationChanged}
|
||||
configClicked={this.handleConfigClicked}
|
||||
directory={Constants.DATA_LOCATIONS[this.props.platform]}
|
||||
errorHandler={this.setErrorState}
|
||||
hyperspace={this.state.Hyperspace}
|
||||
mountsBusy={this.notifyMountsBusy}
|
||||
platform={this.props.platform}
|
||||
processAutoMount={!this.state.AutoMountProcessed}
|
||||
sia={this.state.Sia}
|
||||
version={this.state.InstalledVersion}/>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
} else {
|
||||
mainContent = <Loading/>
|
||||
}
|
||||
|
||||
return (
|
||||
<div styleName='App'>
|
||||
{errorDisplay}
|
||||
{dependencyDisplay}
|
||||
{upgradeDisplay}
|
||||
{downloadDisplay}
|
||||
<Box dxDark dxStyle={{'height': 'auto', 'padding': '2px'}}>
|
||||
<table cellPadding={0} cellSpacing={0} style={{margin: 0, padding: 0}}>
|
||||
<tbody style={{margin: 0, padding: 0}}>
|
||||
<tr style={{margin: 0, padding: 0}}>
|
||||
<td width='33%' style={{margin: 0, padding: 0}}/>
|
||||
<td width='33%' style={{margin: 0, padding: 0}}>
|
||||
<h1 style={{'textAlign': 'center'}}>{'Repertory UI v' + this.props.version}</h1>
|
||||
</td>
|
||||
<td width='33%' style={{margin: 0, padding: 0}} align='right' valign='middle'>
|
||||
{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})}/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
clicked={()=>this.setState({UpgradeDismissed: false})}
|
||||
col={dimensions => dimensions.columns - 6}
|
||||
colSpan={5}
|
||||
row={1}
|
||||
rowSpan={remain=>remain - 2}/>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Box dxStyle={{'padding': '4px', 'marginTop': '10px'}}>
|
||||
{options}
|
||||
</div>
|
||||
<div styleName='Content'>
|
||||
<Box dxStyle={{padding: '8px'}}>
|
||||
{mainContent}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/assets/images/configure.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/release_available.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 175 KiB |
36
src/components/ConfigurationItem/ConfigurationItem.css
Normal file
@@ -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);
|
||||
}
|
||||
107
src/components/ConfigurationItem/ConfigurationItem.js
Normal file
@@ -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 = <input checked={JSON.parse(props.value)}
|
||||
onChange={e=>handleChanged(e)}
|
||||
type={'checkbox'}/>;
|
||||
break;
|
||||
|
||||
case "double":
|
||||
data = <input min={0.0}
|
||||
onChange={e=>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 (
|
||||
<option styleName='Option' key={i} value={s}>{s}</option>
|
||||
);
|
||||
});
|
||||
|
||||
data = (
|
||||
<select onChange={e=>handleChanged(e)}
|
||||
styleName='Select'
|
||||
value={props.value}>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
break;
|
||||
|
||||
case "string":
|
||||
data = <input onChange={e=>handleChanged(e)}
|
||||
styleName='Input'
|
||||
type={'text'}
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint8":
|
||||
data = <input max={255}
|
||||
min={0}
|
||||
onChange={e=>handleChanged(e)}
|
||||
styleName='Input'
|
||||
type={'number'}
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint16":
|
||||
data = <input max={65535}
|
||||
min={0}
|
||||
onChange={e=>handleChanged(e)}
|
||||
styleName='Input'
|
||||
type={'number'}
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint32":
|
||||
data = <input max={4294967295}
|
||||
min={0}
|
||||
onChange={e=>handleChanged(e)}
|
||||
styleName='Input'
|
||||
type={'number'}
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
case "uint64":
|
||||
data = <input max={18446744073709551615}
|
||||
min={0}
|
||||
onChange={e=>handleChanged(e)}
|
||||
styleName='Input'
|
||||
type={'number'}
|
||||
value={props.value}/>;
|
||||
break;
|
||||
|
||||
default:
|
||||
data = <div>{props.value}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div styleName='ConfigurationItem'>
|
||||
<table cellPadding='2'
|
||||
width='100%'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width='100%'>{props.label}</td>
|
||||
<td>{data}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -8,15 +8,15 @@ export default CSSModules((props) => {
|
||||
const items = props.dependencies.map((k, i)=> {
|
||||
return (
|
||||
<Dependency allowDownload={props.allowDownload}
|
||||
download={k.download}
|
||||
key={i}
|
||||
name={k.display}
|
||||
download={k.download}
|
||||
onDownload={props.onDownload}/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Box dxDark dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
|
||||
<Box dxStyle={{width: '300px', height: 'auto', padding: '5px'}}>
|
||||
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>Missing Dependencies</h1>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import styles from './DownloadProgress.css';
|
||||
export default CSSModules((props) => {
|
||||
|
||||
return (
|
||||
<Box dxDark dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
||||
<Box dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
||||
<div style={{width: '100%', height: 'auto'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.display}</h1>
|
||||
</div>
|
||||
|
||||
11
src/components/ErrorDetails/ErrorDetails.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.Heading {
|
||||
color: var(--text_color_error);
|
||||
text-align: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.Content {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
18
src/components/ErrorDetails/ErrorDetails.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import Box from '../UI/Box/Box';
|
||||
import Button from '../UI/Button/Button';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './ErrorDetails.css';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
return (
|
||||
<Box dxStyle={{padding: '8px'}}>
|
||||
<h1 styleName='Heading'>Application Error</h1>
|
||||
<div styleName='Content'>
|
||||
<p>{props.error.toString()}</p>
|
||||
</div>
|
||||
<Button clicked={props.closed}>Dismiss</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -1,4 +0,0 @@
|
||||
.MountItem {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,49 +4,92 @@ import styles from './MountItem.css';
|
||||
import DropDown from '../UI/DropDown/DropDown';
|
||||
import Button from '../UI/Button/Button';
|
||||
import Loader from 'react-loader-spinner';
|
||||
import Text from '../UI/Text/Text';
|
||||
import Grid from '../UI/Grid/Grid';
|
||||
import configureImage from '../../assets/images/configure.png';
|
||||
import RootElem from '../../hoc/RootElem/RootElem';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
let inputControl = null;
|
||||
let mountWidth = '70%';
|
||||
if (props.platform === 'win32') {
|
||||
inputControl = <DropDown disabled={!props.allowMount || props.mounted}
|
||||
items={props.items}
|
||||
selected={props.items.indexOf(props.location)}
|
||||
changed={props.changed}/>;
|
||||
mountWidth = '18%';
|
||||
} else {
|
||||
inputControl = <input disabled={!props.allowMount || props.mounted}
|
||||
type={'text'}
|
||||
value={props.location}
|
||||
onChange={props.changed}/>;
|
||||
let configButton = null;
|
||||
let secondRow = 6;
|
||||
if (props.allowConfig) {
|
||||
configButton = (
|
||||
<RootElem colSpan={4}
|
||||
rowSpan={6}>
|
||||
<img alt=''
|
||||
height={'16px'}
|
||||
onClick={props.configClicked}
|
||||
src={configureImage}
|
||||
style={{padding: 0, border: 0, margin: 0, cursor: 'pointer'}}
|
||||
width={'16px'}/>
|
||||
</RootElem>
|
||||
);
|
||||
}
|
||||
|
||||
let actionDisplay = null;
|
||||
if (props.allowMount) {
|
||||
actionDisplay = <Button buttonStyles={{width: '100%'}}
|
||||
clicked={()=>props.clicked(props.title, !props.mounted, props.location, props.pid)}>{props.mounted ? 'Unmount' : 'Mount'}</Button>;
|
||||
let inputColumnSpan;
|
||||
let inputControl = null;
|
||||
if (props.platform === 'win32') {
|
||||
inputColumnSpan = 20;
|
||||
inputControl = <DropDown changed={props.changed}
|
||||
colSpan={inputColumnSpan}
|
||||
disabled={!props.allowMount || props.mounted}
|
||||
items={props.items}
|
||||
row={secondRow}
|
||||
rowSpan={7}
|
||||
selected={props.items.indexOf(props.location)}/>;
|
||||
|
||||
} else {
|
||||
actionDisplay = <Loader color={'var(--heading_text_color)'}
|
||||
height='24px'
|
||||
width='24px'
|
||||
type='Circles'/>;
|
||||
inputColumnSpan = 60;
|
||||
inputControl = (
|
||||
<RootElem colSpan={inputColumnSpan}
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
<input disabled={!props.allowMount || props.mounted}
|
||||
onChange={props.changed}
|
||||
type={'text'}
|
||||
value={props.location}/>
|
||||
</RootElem>);
|
||||
}
|
||||
|
||||
const buttonDisplay = props.allowMount ?
|
||||
(props.mounted ? 'Unmount' : 'Mount') :
|
||||
<Loader color={'var(--heading_text_color)'}
|
||||
height='19px'
|
||||
type='Circles'
|
||||
width='19px'/>;
|
||||
|
||||
const actionsDisplay = (
|
||||
<Button clicked={()=>props.clicked(props.title, !props.mounted, props.location, props.pid)}
|
||||
col={inputColumnSpan + 2}
|
||||
colSpan={20}
|
||||
disabled={!props.allowMount}
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
{buttonDisplay}
|
||||
</Button>);
|
||||
|
||||
const autoMountControl = (
|
||||
<RootElem col={inputColumnSpan + 23}
|
||||
colSpan={26}
|
||||
row={secondRow}
|
||||
rowSpan={7}>
|
||||
<input checked={props.autoMount}
|
||||
onChange={props.autoMountChanged}
|
||||
type='checkbox'/>Auto-mount
|
||||
</RootElem>
|
||||
);
|
||||
|
||||
return (
|
||||
<div styleName='MountItem'>
|
||||
<h2>{props.title}</h2>
|
||||
<table width='100%' cellPadding='2'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={mountWidth} height='30px'>{inputControl}</td>
|
||||
<td width='25%' align='center' valign='middle'>
|
||||
{actionDisplay}
|
||||
</td>
|
||||
<td>
|
||||
<input type='checkbox' checked={props.autoMount} onChange={props.autoMountChanged}/>Auto-mount
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Grid>
|
||||
{configButton}
|
||||
<Text
|
||||
col={configButton ? 6 : 0}
|
||||
rowSpan={5}
|
||||
text={props.title}
|
||||
type={'Heading1'}/>
|
||||
{inputControl}
|
||||
{actionsDisplay}
|
||||
{autoMountControl}
|
||||
</Grid>
|
||||
);
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import styles from './ReleaseVersionDisplay.css';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import DropDown from '../UI/DropDown/DropDown';
|
||||
import Grid from '../UI/Grid/Grid';
|
||||
import Text from '../UI/Text/Text';
|
||||
import Button from '../UI/Button/Button';
|
||||
import UpgradeIcon from '../UpgradeIcon/UpgradeIcon';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
let optionsDisplay = null;
|
||||
if (props.releaseExtracting) {
|
||||
optionsDisplay = <Text align='center'
|
||||
row={13}
|
||||
rowSpan={7}
|
||||
colSpan={'remain'}
|
||||
text={'Activating <' + props.installedVersion + '>'}/>
|
||||
} else {
|
||||
optionsDisplay = <Button clicked={props.downloadClicked}
|
||||
colSpan={20}
|
||||
disabled={props.downloadDisabled}
|
||||
row={13}
|
||||
rowSpan={7}>Install</Button>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Text colSpan={columns=>columns / 3}
|
||||
rowSpan={4}
|
||||
text={'Release'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<DropDown changed={props.releaseChanged}
|
||||
colSpan={remain=>remain / 3 - 1}
|
||||
disabled={props.disabled}
|
||||
items={props.releaseTypes}
|
||||
row={5}
|
||||
rowSpan={7}
|
||||
selected={props.release}/>
|
||||
<Text col={dimensions => dimensions.columns / 3}
|
||||
colSpan={remain=>remain / 2}
|
||||
rowSpan={4}
|
||||
text={'Version'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<UpgradeIcon available={props.versionAvailable}
|
||||
col={dimensions => ((dimensions.columns / 3) * 2) - 6}
|
||||
colSpan={4}
|
||||
release
|
||||
rowSpan={4}/>
|
||||
<DropDown changed={props.versionChanged}
|
||||
col={dimensions => dimensions.columns / 3}
|
||||
colSpan={remain=>remain / 2 - 1}
|
||||
disabled={props.disabled}
|
||||
items={props.versions}
|
||||
row={5}
|
||||
rowSpan={7}
|
||||
selected={props.version}/>
|
||||
<Text col={dimensions => (dimensions.columns / 3) * 2}
|
||||
colSpan={'remain'}
|
||||
rowSpan={4}
|
||||
text={'Installed'}
|
||||
textAlign={'left'}
|
||||
type={'Heading2'}/>
|
||||
<Text col={dimensions => (dimensions.columns / 3) * 2}
|
||||
colSpan={'remain'}
|
||||
row={5}
|
||||
rowSpan={7}
|
||||
text={props.installedVersion}
|
||||
textAlign={'left'}/>
|
||||
{optionsDisplay}
|
||||
</Grid>
|
||||
);
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -3,7 +3,8 @@ import CSSModules from 'react-css-modules';
|
||||
import styles from './Box.css';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
const styleList = ['Box'];
|
||||
const styleList = [];
|
||||
styleList.push('Box');
|
||||
if (props.dxDark) {
|
||||
styleList.push('Darker');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.Button {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
outline: 0;
|
||||
color: var(--text_color);
|
||||
@@ -10,7 +9,10 @@
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
text-outline: none;
|
||||
width: 70px;
|
||||
vertical-align: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.Button:hover:enabled {
|
||||
@@ -20,7 +22,7 @@
|
||||
}
|
||||
|
||||
.Button:hover:disabled {
|
||||
cursor: default;
|
||||
cursor: no-drop;
|
||||
}
|
||||
|
||||
.Button:active,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.DropDown {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -22,3 +24,16 @@
|
||||
border-color: rgba(10, 10, 20, 0.9);
|
||||
color: var(--text_color);
|
||||
}
|
||||
|
||||
.Select:hover:enabled {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Select:hover:disabled {
|
||||
cursor: no-drop;
|
||||
}
|
||||
|
||||
.Select:active,
|
||||
.Select.active {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -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) => {
|
||||
|
||||
18
src/components/UI/Grid/Grid.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.Grid {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.GridOwner {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
126
src/components/UI/Grid/Grid.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, {Component} from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './Grid.css';
|
||||
import GridComponent from './GridComponent/GridComponent';
|
||||
|
||||
export default CSSModules(class extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
window.addEventListener("resize", this.updateSizeAsync);
|
||||
}
|
||||
|
||||
state = {
|
||||
calculated: false,
|
||||
dimensions: {
|
||||
columns: 0,
|
||||
rows: 0
|
||||
}
|
||||
};
|
||||
|
||||
calculateDimensions = (size) => {
|
||||
return {
|
||||
columns: Math.floor(size.width / 4),
|
||||
rows: Math.floor(size.height / 4)
|
||||
};
|
||||
};
|
||||
|
||||
getSize = () => {
|
||||
const elem = this.refs.GridOwner;
|
||||
return {
|
||||
height: elem ? elem.clientHeight : 0,
|
||||
width: elem ? elem.clientWidth : 0
|
||||
};
|
||||
};
|
||||
|
||||
updateSize = () => {
|
||||
const state = {
|
||||
...this.state
|
||||
};
|
||||
const size = this.getSize();
|
||||
const dimensions = this.calculateDimensions(size);
|
||||
if (state.dimensions !== dimensions) {
|
||||
this.setState({
|
||||
calculated: true,
|
||||
dimensions: dimensions
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
updateSizeAsync = () => {
|
||||
return new Promise((done) => {
|
||||
this.updateSize();
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.updateSizeAsync();
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
window.removeEventListener("resize", this.updateSizeAsync);
|
||||
};
|
||||
|
||||
render() {
|
||||
let children = null;
|
||||
const dimensions = this.state.dimensions;
|
||||
if (this.state.calculated) {
|
||||
children = React.Children.map(this.props.children, (child, i) => {
|
||||
if (child) {
|
||||
let row = child.props.row || 0;
|
||||
if (typeof(row) === 'function') {
|
||||
row = row(dimensions);
|
||||
}
|
||||
|
||||
let col = child.props.col || 0;
|
||||
if (typeof(col) === 'function') {
|
||||
col = col(dimensions);
|
||||
}
|
||||
|
||||
let rowSpan = child.props.rowSpan;
|
||||
if (typeof(rowSpan) === 'function') {
|
||||
rowSpan = rowSpan(dimensions.rows - row, dimensions.rows);
|
||||
}
|
||||
|
||||
let colSpan = child.props.colSpan;
|
||||
if (typeof(colSpan) === 'function') {
|
||||
colSpan = colSpan(dimensions.columns - col, dimensions.columns);
|
||||
}
|
||||
|
||||
rowSpan = rowSpan ? (rowSpan === 'remain' ? (dimensions.rows - row) : rowSpan) : null;
|
||||
colSpan = colSpan ? (colSpan === 'remain' ? dimensions.columns - col : colSpan) : null;
|
||||
|
||||
return <GridComponent
|
||||
row={row}
|
||||
col={col}
|
||||
rowSpan={rowSpan}
|
||||
colSpan={colSpan}
|
||||
key={'gc_' + i}>{child}</GridComponent>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(i => i !== null);
|
||||
}
|
||||
|
||||
const style = {
|
||||
style: {
|
||||
gridTemplateColumns: '4px '.repeat(dimensions.columns).trim(),
|
||||
gridTemplateRows: '4px '.repeat(dimensions.rows).trim(),
|
||||
gridAutoColumns: '4px',
|
||||
gridAutoRows: '4px'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref='GridOwner'
|
||||
styleName='GridOwner'>
|
||||
<div styleName='Grid' {...style}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
}, styles, {allowMultiple: true});
|
||||
7
src/components/UI/Grid/GridComponent/GridComponent.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.GridComponent {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
21
src/components/UI/Grid/GridComponent/GridComponent.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './GridComponent.css';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
const style = {
|
||||
style: {
|
||||
gridRowStart: Math.floor(props.row + 1),
|
||||
gridRowEnd: 'span ' + Math.floor(props.rowSpan || 1),
|
||||
gridColumnStart: Math.floor(props.col + 1),
|
||||
gridColumnEnd: 'span ' + Math.floor(props.colSpan || 1)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div styleName='GridComponent' {...style}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
||||
}, styles, {allowMultiple: true});
|
||||
18
src/components/UI/Loading/Loading.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.Loading {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.Content {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
17
src/components/UI/Loading/Loading.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './Loading.css'
|
||||
import Loader from 'react-loader-spinner';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
return (
|
||||
<div
|
||||
styleName='Loading'>
|
||||
<div styleName='Content'>
|
||||
<Loader color={'var(--heading_text_color)'}
|
||||
height='28px'
|
||||
width='28px'
|
||||
type='ThreeDots'/>
|
||||
</div>
|
||||
</div>);
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -17,3 +17,11 @@
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 2001;
|
||||
}
|
||||
|
||||
.Modal.Critical {
|
||||
z-index: 2100;
|
||||
}
|
||||
|
||||
.Content.Critical {
|
||||
z-index: 2101;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,20 @@ import CSSModules from 'react-css-modules';
|
||||
import styles from './Modal.css'
|
||||
|
||||
export default CSSModules((props) => {
|
||||
let modalStyles = [];
|
||||
let contentStyles = [];
|
||||
modalStyles.push('Modal');
|
||||
contentStyles.push('Content');
|
||||
if (props.critical) {
|
||||
modalStyles.push('Critical');
|
||||
contentStyles.push('Critical');
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
styleName='Modal'
|
||||
styleName={modalStyles.join(' ')}
|
||||
onClick={props.clicked}>
|
||||
<div styleName='Content'>
|
||||
<div styleName={contentStyles.join(' ')}>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>);
|
||||
|
||||
39
src/components/UI/Text/Text.css
Normal file
@@ -0,0 +1,39 @@
|
||||
.TextOwner {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.Text {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
color: var(--text_color);
|
||||
}
|
||||
|
||||
.Heading1 {
|
||||
font-weight: bold;
|
||||
color: var(--heading_text_color);
|
||||
}
|
||||
|
||||
.Heading2 {
|
||||
font-weight: bold;
|
||||
color: var(--heading_other_text_color);
|
||||
}
|
||||
|
||||
.Heading3 {
|
||||
font-weight: bold;
|
||||
color: var(--heading_other_text_color);
|
||||
}
|
||||
|
||||
.AltTextColor {
|
||||
color: var(--heading_other_text_color);
|
||||
}
|
||||
28
src/components/UI/Text/Text.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './Text.css';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
const styleList = [];
|
||||
styleList.push('Text');
|
||||
if (props.type) {
|
||||
styleList.push(props.type);
|
||||
}
|
||||
|
||||
let style = {...props.style};
|
||||
if (props.textAlign) {
|
||||
style['textAlign'] = props.textAlign.toLowerCase();
|
||||
}
|
||||
|
||||
const text = (
|
||||
<div
|
||||
styleName={styleList.join(' ')}
|
||||
style={style}>{props.text}
|
||||
</div>);
|
||||
|
||||
return props.noOwner ? text : (
|
||||
<div styleName={'TextOwner'}>
|
||||
{text}
|
||||
</div>);
|
||||
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -1,14 +1,21 @@
|
||||
.UpgradeIcon {
|
||||
display: block;
|
||||
margin: 0;
|
||||
.Owner {
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.UpgradeIcon {
|
||||
cursor: default;
|
||||
}
|
||||
.UpgradeIcon {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
object-fit: contain;
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.65;
|
||||
}
|
||||
@@ -1,10 +1,29 @@
|
||||
import React from 'react';
|
||||
import CSSModules from 'react-css-modules';
|
||||
import styles from './UpgradeIcon.css';
|
||||
import availableImage from '../../assets/images/upgrade_available.png';
|
||||
import availableImage from '../../assets/images/release_available.png';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
return props.available ?
|
||||
<img alt='' styleName='UpgradeIcon' src={availableImage} onClick={props.clicked}/> :
|
||||
<div styleName='UpgradeIcon'/> ;
|
||||
let placement = 'left';
|
||||
let toolTipText = 'UI Upgrade Available';
|
||||
if (props.release) {
|
||||
placement='bottom';
|
||||
toolTipText = 'New Release Available';
|
||||
}
|
||||
|
||||
return props
|
||||
.available ?
|
||||
(
|
||||
<div styleName='Owner'>
|
||||
<p data-tip='' data-for={placement}>
|
||||
<img alt=''
|
||||
onClick={props.clicked}
|
||||
src={availableImage}
|
||||
styleName='UpgradeIcon'/>
|
||||
</p>
|
||||
<ReactTooltip id={placement} place={placement}>{toolTipText}</ReactTooltip>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
}, styles, {allowMultiple: true});
|
||||
@@ -6,7 +6,7 @@ import styles from './UpgradeUI.css';
|
||||
|
||||
export default CSSModules((props) => {
|
||||
return (
|
||||
<Box dxDark dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
||||
<Box dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
||||
<div style={{width: '100%', height: 'auto'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>UI Upgrade Available</h1>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,62 @@
|
||||
export const RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/master/releases.json';
|
||||
export const DATA_LOCATIONS = {
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/master/releases.json';
|
||||
exports.DATA_LOCATIONS = {
|
||||
linux: '~/.local/repertory/ui',
|
||||
darwin: '~/Library/Application Support/repertory/ui',
|
||||
win32: '%LOCALAPPDATA%\\repertory\\ui',
|
||||
win32: '%LOCALAPPDATA%\\repertory\\ui'
|
||||
};
|
||||
export const UI_RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory-ui/raw/master/releases.json';
|
||||
exports.UI_RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory-ui/raw/master/releases.json';
|
||||
|
||||
exports.IPC_Check_Installed = 'check_installed';
|
||||
exports.IPC_Check_Installed_Reply = 'check_installed_reply';
|
||||
|
||||
exports.IPC_Delete_File = 'delete_file';
|
||||
|
||||
exports.IPC_Detect_Mounts = 'detect_mounts';
|
||||
exports.IPC_Detect_Mounts_Reply = 'detect_mounts_reply';
|
||||
|
||||
exports.IPC_Download_File = 'download_file';
|
||||
exports.IPC_Download_File_Complete = 'download_file_complete';
|
||||
exports.IPC_Download_File_Progress = 'download_file_progress';
|
||||
|
||||
exports.IPC_Extract_Release = 'extract_release';
|
||||
exports.IPC_Extract_Release_Complete = 'extract_release_complete';
|
||||
|
||||
exports.IPC_Get_Config = 'get_config';
|
||||
exports.IPC_Get_Config_Reply = 'get_config_reply';
|
||||
|
||||
exports.IPC_Get_Config_Template = 'get_config_template';
|
||||
exports.IPC_Get_Config_Template_Reply = 'get_config_template_reply';
|
||||
|
||||
exports.IPC_Get_Platform = 'get_platform';
|
||||
exports.IPC_Get_Platform_Reply = 'get_platform_reply';
|
||||
|
||||
exports.IPC_Get_State = 'get_state';
|
||||
exports.IPC_Get_State_Reply = 'get_state_reply';
|
||||
|
||||
exports.IPC_Grab_Releases = 'grab_releases';
|
||||
exports.IPC_Grab_Releases_Reply = 'grab_releases_reply';
|
||||
|
||||
exports.IPC_Grab_UI_Releases = 'grab_ui_releases';
|
||||
exports.IPC_Grab_UI_Releases_Reply = 'grab_ui_releases_reply';
|
||||
|
||||
exports.IPC_Install_Dependency = 'install_dependency';
|
||||
exports.IPC_Install_Dependency_Reply = 'install_dependency_reply';
|
||||
|
||||
exports.IPC_Install_Upgrade = 'install_upgrade';
|
||||
exports.IPC_Install_Upgrade_Reply = 'install_upgrade_reply';
|
||||
|
||||
exports.IPC_Mount_Drive = 'mount_drive';
|
||||
exports.IPC_Mount_Drive_Reply = 'mount_drive_reply';
|
||||
|
||||
exports.IPC_Save_State = 'save_state';
|
||||
|
||||
exports.IPC_Set_Config_Values = 'set_config_values';
|
||||
exports.IPC_Set_Config_Values_Reply = 'set_config_values_reply';
|
||||
|
||||
exports.IPC_Shutdown = 'shutdown';
|
||||
|
||||
exports.IPC_Unmount_Drive = 'unmount_drive';
|
||||
exports.IPC_Unmount_Drive_Reply = 'unmount_drive_reply';
|
||||
6
src/containers/Configuration/Configuration.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.Configuration {
|
||||
width: 90vw;
|
||||
height: 90vh;
|
||||
padding: 4px;
|
||||
margin: 0;
|
||||
}
|
||||
295
src/containers/Configuration/Configuration.js
Normal file
@@ -0,0 +1,295 @@
|
||||
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';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
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(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply);
|
||||
ipcRenderer.on(Constants.IPC_Get_Config_Reply, this.onGetConfigReply);
|
||||
ipcRenderer.on(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply);
|
||||
|
||||
ipcRenderer.send(Constants.IPC_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(Constants.IPC_Get_Config_Reply, this.onGetConfigReply);
|
||||
ipcRenderer.removeListener(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply);
|
||||
ipcRenderer.removeListener(Constants.IPC_Set_Config_Values_Reply, 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,
|
||||
});
|
||||
} else {
|
||||
this.props.errorHandler(arg.data.Error);
|
||||
}
|
||||
};
|
||||
|
||||
onGetConfigTemplateReply = (event, arg) => {
|
||||
if (arg.data.Success) {
|
||||
this.setState({
|
||||
Template: arg.data.Template,
|
||||
});
|
||||
ipcRenderer.send(Constants.IPC_Get_Config, {
|
||||
Directory: this.props.directory,
|
||||
StorageType: this.props.storageType,
|
||||
Version: this.props.version,
|
||||
});
|
||||
} else {
|
||||
this.props.errorHandler(arg.data.Error, () => {
|
||||
this.props.closed();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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(Constants.IPC_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 = (
|
||||
<Modal>
|
||||
<Box dxStyle={{width: '40vw', padding: '4px'}}>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>Save Changes?</h1>
|
||||
<table width='100%'><tbody>
|
||||
<tr>
|
||||
<td align='center' width='50%'><Button clicked={this.saveAndClose} disabled={this.state.Saving}>Yes</Button></td>
|
||||
<td align='center' width='50%'><Button clicked={this.props.closed} disabled={this.state.Saving}>No</Button></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const configurationItems = this.state.ItemList
|
||||
.map((k, i) => {
|
||||
return (
|
||||
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
|
||||
<ConfigurationItem advanced={k.advanced}
|
||||
changed={e=>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((
|
||||
<div key={key}>
|
||||
<h1>{key}</h1>
|
||||
<div>
|
||||
{
|
||||
this.state.ObjectLookup[key].map((k, i) => {
|
||||
return (
|
||||
((this.state.ShowAdvanced && k.advanced) || !k.advanced) ?
|
||||
<ConfigurationItem advanced={k.advanced}
|
||||
changed={e=>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)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<div styleName='Configuration'>
|
||||
{confirmSave}
|
||||
<Box dxDark dxStyle={{padding: '8px'}}>
|
||||
<div style={{float: 'right', margin: 0, padding: 0, marginTop: '-4px', boxSizing: 'border-box', display: 'block'}}>
|
||||
<b style={{cursor: 'pointer'}}
|
||||
onClick={this.checkSaveRequired}>X</b>
|
||||
</div>
|
||||
<h1 style={{width: '100%', textAlign: 'center'}}>{this.props.storageType + ' Configuration'}</h1>
|
||||
<div style={{overflowY: 'auto', height: '90%'}}>
|
||||
{objectItems}
|
||||
{(configurationItems.length > 0) ? <h1>Settings</h1> : null}
|
||||
{configurationItems}
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CSSModules(Configuration, styles, {allowMultiple: true});
|
||||
@@ -1,4 +1,6 @@
|
||||
.MountItems {
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import CSSModules from 'react-css-modules';
|
||||
import styles from './MountItems.css';
|
||||
import MountItem from '../../components/MountItem/MountItem';
|
||||
|
||||
const Constants = require('../../constants');
|
||||
|
||||
let ipcRenderer = null;
|
||||
if (!process.versions.hasOwnProperty('electron')) {
|
||||
ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
|
||||
@@ -13,64 +15,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,
|
||||
});
|
||||
|
||||
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(Constants.IPC_Detect_Mounts_Reply, this.onDetectMountsReply);
|
||||
ipcRenderer.on(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
|
||||
ipcRenderer.on(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
|
||||
|
||||
this.detectMounts();
|
||||
}
|
||||
@@ -91,8 +38,17 @@ class MountItems extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.removeListener(Constants.IPC_Detect_Mounts_Reply, this.onDetectMountsReply);
|
||||
ipcRenderer.removeListener(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
|
||||
ipcRenderer.removeListener(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
|
||||
}
|
||||
};
|
||||
|
||||
detectMounts = ()=> {
|
||||
ipcRenderer.send('detect_mounts', {
|
||||
this.props.mountsBusy(true);
|
||||
ipcRenderer.send(Constants.IPC_Detect_Mounts, {
|
||||
Directory: this.props.directory,
|
||||
Version: this.props.version,
|
||||
});
|
||||
@@ -117,15 +73,17 @@ class MountItems extends Component {
|
||||
[storageType]: state,
|
||||
});
|
||||
|
||||
this.props.mountsBusy(true);
|
||||
|
||||
if (mount) {
|
||||
ipcRenderer.send('mount_drive', {
|
||||
ipcRenderer.send(Constants.IPC_Mount_Drive, {
|
||||
Directory: this.props.directory,
|
||||
Location: location,
|
||||
StorageType: storageType,
|
||||
Version: this.props.version,
|
||||
});
|
||||
} else {
|
||||
ipcRenderer.send('unmount_drive', {
|
||||
ipcRenderer.send(Constants.IPC_Unmount_Drive, {
|
||||
Directory: this.props.directory,
|
||||
Location: location,
|
||||
PID: pid,
|
||||
@@ -136,6 +94,69 @@ 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();
|
||||
} else {
|
||||
this.props.errorHandler(arg.data.Error);
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
@@ -155,28 +176,33 @@ class MountItems extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div styleName='MountItems'>
|
||||
<MountItem allowMount={this.state.Hyperspace.AllowMount}
|
||||
<MountItem allowConfig={this.props.allowConfig}
|
||||
allowMount={this.state.Hyperspace.AllowMount}
|
||||
autoMount={this.props.hyperspace.AutoMount}
|
||||
autoMountChanged={(e)=>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}/>
|
||||
<MountItem allowMount={this.state.Sia.AllowMount}
|
||||
configClicked={()=>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'}/>
|
||||
<div style={{paddingTop: '8px'}}/>
|
||||
<MountItem allowConfig={this.props.allowConfig}
|
||||
allowMount={this.state.Sia.AllowMount}
|
||||
autoMount={this.props.sia.AutoMount}
|
||||
autoMountChanged={(e)=>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'}/>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
9
src/hoc/RootElem/RootElem.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default (props) => {
|
||||
return (
|
||||
<div style={{margin: 0, padding: 0}} {...props}>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
@@ -2,22 +2,23 @@
|
||||
--border_radius: 4px;
|
||||
|
||||
--control_background: rgba(150, 150, 190, .15);
|
||||
--control_background_hover: rgba(150, 150, 190, .3);
|
||||
--control_background_hover: rgba(150, 150, 190, .35);
|
||||
--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_transparent_background: rgba(40, 40, 55, 0.45);
|
||||
--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_error: rgba(203, 120, 120, 0.8);
|
||||
--heading_text_color: rgba(161, 190, 219, 0.7);
|
||||
--heading_other_text_color: var(--heading_text_color);
|
||||
--text_color_transition: color 0.3s;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-size: 15px;
|
||||
font-size: 5vh;
|
||||
}
|
||||
|
||||
*::-moz-focus-inner {
|
||||
@@ -61,3 +62,27 @@ h1 {
|
||||
h2, h3 {
|
||||
color: var(--heading_other_text_color);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
color: var(--text_color);
|
||||
}
|
||||
|
||||
.scrollable-content {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.scrollable-content, ::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.scrollable-content, ::-webkit-scrollbar * {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scrollable-content, ::-webkit-scrollbar-thumb {
|
||||
background: rgba(90, 90, 90, 0.6) !important;
|
||||
}
|
||||
|
||||
18
src/index.js
@@ -1,10 +1,20 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
|
||||
import App from './App';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
import packageJson from '../package.json';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
|
||||
const Constants = require('./constants');
|
||||
|
||||
if (!process.versions.hasOwnProperty('electron')) {
|
||||
const ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.on(Constants.IPC_Get_Platform_Reply, (event, arg) => {
|
||||
ReactDOM.render(<App platform={arg.data} version={packageJson.version}/>, document.getElementById('root'));
|
||||
registerServiceWorker();
|
||||
});
|
||||
ipcRenderer.send(Constants.IPC_Get_Platform);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App version={packageJson.version}/>, document.getElementById('root'));
|
||||
registerServiceWorker();
|
||||
|
||||