Merged 1.0.1_branch into master
1
.gitignore
vendored
@@ -4,3 +4,4 @@ node_modules/
|
|||||||
build/
|
build/
|
||||||
chrome_data/
|
chrome_data/
|
||||||
dist/
|
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
|
# Repertory UI
|
||||||

|

|
||||||
|
|
||||||
### GUI for [Repertory](https://bitbucket.org/blockstorage/repertory) ###
|
### 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.
|
Repertory allows you to mount Hyperspace or Sia blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.
|
||||||
# Downloads #
|
# Downloads #
|
||||||
|
|||||||
360
electron.js
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const {app, BrowserWindow, Tray, nativeImage, Menu} = require('electron');
|
const {app, BrowserWindow, Tray, nativeImage, Menu} = require('electron');
|
||||||
const {ipcMain} = require('electron');
|
const {ipcMain} = require('electron');
|
||||||
|
const Constants = require('./src/constants');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
require('electron-debug')();
|
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
|
// 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.
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
|
let mainContextWindow;
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
let mainWindowTray;
|
let mainWindowTray;
|
||||||
let mountedPIDs = [];
|
let mountedPIDs = [];
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
// Create the browser window.
|
// 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({
|
mainWindow = new BrowserWindow({
|
||||||
width: 425,
|
width: 425,
|
||||||
height: height,
|
height: height,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
|
title: 'Repertory UI',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
webSecurity: !process.env.ELECTRON_START_URL
|
webSecurity: !process.env.ELECTRON_START_URL
|
||||||
}
|
}
|
||||||
@@ -46,14 +48,18 @@ function createWindow() {
|
|||||||
mainWindow = null
|
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({
|
const autoLauncher = new AutoLaunch({
|
||||||
name: 'Repertory UI',
|
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 image = nativeImage.createFromPath(path.join(__dirname, '/build/logo.png'));
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
mainContextWindow = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: 'Visible', type: 'checkbox', click(item) {
|
label: 'Visible', type: 'checkbox', click(item) {
|
||||||
if (item.checked) {
|
if (item.checked) {
|
||||||
@@ -75,21 +81,30 @@ function createWindow() {
|
|||||||
autoLauncher.disable();
|
autoLauncher.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Exit', click(item) {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
contextMenu.items[0].checked = true;
|
mainContextWindow.items[0].checked = true;
|
||||||
autoLauncher.isEnabled()
|
autoLauncher
|
||||||
.then((enabled) => {
|
.isEnabled()
|
||||||
contextMenu.items[1].checked = enabled;
|
.then((enabled) => {
|
||||||
|
mainContextWindow.items[1].checked = enabled;
|
||||||
|
|
||||||
mainWindowTray = new Tray(image);
|
mainWindowTray = new Tray(image);
|
||||||
mainWindowTray.setToolTip('Repertory UI');
|
mainWindowTray.setToolTip('Repertory UI');
|
||||||
mainWindowTray.setContextMenu(contextMenu)
|
mainWindowTray.setContextMenu(mainContextWindow)
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +114,10 @@ if (!instanceLock) {
|
|||||||
} else {
|
} else {
|
||||||
app.on('second-instance', () => {
|
app.on('second-instance', () => {
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
|
mainWindow.show();
|
||||||
|
if (mainContextWindow) {
|
||||||
|
mainContextWindow.items[0].checked = true;
|
||||||
|
}
|
||||||
if (mainWindow.isMinimized()) {
|
if (mainWindow.isMinimized()) {
|
||||||
mainWindow.restore();
|
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 dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
const destination = path.join(dataDirectory, data.Version);
|
const destination = path.join(dataDirectory, data.Version);
|
||||||
helpers
|
helpers
|
||||||
@@ -136,27 +165,20 @@ ipcMain.on('check_installed', (event, data) => {
|
|||||||
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
|
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
event.sender.send('check_installed_reply', {
|
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
|
||||||
data: {
|
Dependencies: dependencies,
|
||||||
Dependencies: dependencies,
|
Exists: exists,
|
||||||
Exists: exists,
|
Version: data.Version,
|
||||||
Success: true,
|
|
||||||
Version: data.Version,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
event.sender.send('check_installed_reply', {
|
|
||||||
data: {
|
|
||||||
Dependencies: [],
|
|
||||||
Error: e,
|
|
||||||
Success: false,
|
|
||||||
Version: data.Version,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
standardIPCReply(event, Constants.IPC_Check_Installed_Reply, {
|
||||||
|
Dependencies: [],
|
||||||
|
Version: data.Version,
|
||||||
|
}, error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('delete_file', (event, data) => {
|
ipcMain.on(Constants.IPC_Delete_File, (event, data) => {
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(data.FilePath)) {
|
if (fs.existsSync(data.FilePath)) {
|
||||||
fs.unlinkSync(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 = {
|
let driveLetters = {
|
||||||
Hyperspace: [],
|
Hyperspace: [],
|
||||||
Sia: [],
|
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);
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
helpers
|
helpers
|
||||||
.detectRepertoryMounts(dataDirectory, data.Version)
|
.detectRepertoryMounts(dataDirectory, data.Version)
|
||||||
@@ -213,204 +252,225 @@ ipcMain.on('detect_mounts', (event, data) => {
|
|||||||
siaLocation = siaLocation.toUpperCase();
|
siaLocation = siaLocation.toUpperCase();
|
||||||
grabDriveLetters(hsLocation, siaLocation);
|
grabDriveLetters(hsLocation, siaLocation);
|
||||||
}
|
}
|
||||||
event.sender.send('detect_mounts_reply', {
|
setImage(hsLocation, siaLocation);
|
||||||
data: {
|
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
|
||||||
DriveLetters: driveLetters,
|
DriveLetters: driveLetters,
|
||||||
Locations: {
|
Locations: {
|
||||||
Hyperspace: hsLocation,
|
Hyperspace: hsLocation,
|
||||||
Sia: siaLocation,
|
Sia: siaLocation,
|
||||||
},
|
},
|
||||||
Success: true,
|
PIDS: {
|
||||||
PIDS: {
|
Hyperspace: results.Hyperspace.PID,
|
||||||
Hyperspace: results.Hyperspace.PID,
|
Sia: results.Sia.PID,
|
||||||
Sia: results.Sia.PID,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(error => {
|
||||||
grabDriveLetters('', '');
|
grabDriveLetters('', '');
|
||||||
|
setImage('', '');
|
||||||
event.sender.send('detect_mounts_reply', {
|
standardIPCReply(event, Constants.IPC_Detect_Mounts_Reply, {
|
||||||
data: {
|
DriveLetters: driveLetters,
|
||||||
DriveLetters: driveLetters,
|
}, error);
|
||||||
Error: err,
|
|
||||||
Success: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('download_file', (event, data) => {
|
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
|
||||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
const destination = path.join(dataDirectory, data.Filename);
|
const destination = path.join(dataDirectory, data.Filename);
|
||||||
helpers.downloadFile(data.URL, destination, (progress) => {
|
helpers.downloadFile(data.URL, destination, (progress) => {
|
||||||
event.sender.send('download_file_progress', {
|
standardIPCReply(event, Constants.IPC_Download_File_Progress, {
|
||||||
data: {
|
Destination: destination,
|
||||||
Destination: destination,
|
Progress: progress,
|
||||||
Progress: progress,
|
URL: data.URL,
|
||||||
URL: data.URL,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, (success, err) => {
|
|
||||||
event.sender.send('download_file_complete', {
|
|
||||||
data: {
|
|
||||||
Destination: destination,
|
|
||||||
Error: err,
|
|
||||||
Success: success,
|
|
||||||
URL: data.URL,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}, error => {
|
||||||
|
standardIPCReply(event, Constants.IPC_Download_File_Complete, {
|
||||||
|
Destination: destination,
|
||||||
|
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 dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
const destination = path.join(dataDirectory, data.Version);
|
const destination = path.join(dataDirectory, data.Version);
|
||||||
helpers.mkDirByPathSync(destination);
|
helpers.mkDirByPathSync(destination);
|
||||||
|
|
||||||
const stream = fs.createReadStream(data.Source);
|
const stream = fs.createReadStream(data.Source);
|
||||||
stream.pipe(unzip.Extract({ path: destination }))
|
stream
|
||||||
.on('error', (e) => {
|
.pipe(unzip.Extract({ path: destination }))
|
||||||
|
.on('error', error => {
|
||||||
try {
|
try {
|
||||||
helpers.removeDirectoryRecursively(destination);
|
helpers.removeDirectoryRecursively(destination);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
stream.close();
|
stream.close();
|
||||||
event.sender.send('extract_release_complete', {
|
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
|
||||||
data: {
|
Source: data.Source,
|
||||||
Error: e,
|
}, error);
|
||||||
Source: data.Source,
|
|
||||||
Success: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.on('finish', () => {
|
.on('finish', () => {
|
||||||
stream.close();
|
stream.close();
|
||||||
event.sender.send('extract_release_complete', {
|
standardIPCReply(event, Constants.IPC_Extract_Release_Complete, {
|
||||||
data: {
|
Source: data.Source,
|
||||||
Source: data.Source,
|
|
||||||
Success: true,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('get_platform', (event) => {
|
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
|
||||||
event.sender.send('get_platform_reply', {
|
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()
|
data: os.platform()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('get_state', (event, data) => {
|
ipcMain.on(Constants.IPC_Get_State, (event, data) => {
|
||||||
const dataDirectory = helpers.resolvePath(data);
|
const dataDirectory = helpers.resolvePath(data);
|
||||||
helpers.mkDirByPathSync(dataDirectory);
|
helpers.mkDirByPathSync(dataDirectory);
|
||||||
const configFile = path.join(dataDirectory, 'settings.json');
|
const configFile = path.join(dataDirectory, 'settings.json');
|
||||||
if (fs.existsSync(configFile)) {
|
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')),
|
data: JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
event.sender.send('get_state_reply', {
|
event.sender.send(Constants.IPC_Get_State_Reply, {
|
||||||
data: null,
|
data: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('grab_releases', (event) => {
|
ipcMain.on(Constants.IPC_Grab_Releases, (event) => {
|
||||||
event.sender.send('grab_releases_reply');
|
standardIPCReply(event, Constants.IPC_Grab_Releases_Reply);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('grab_ui_releases', (event) => {
|
ipcMain.on(Constants.IPC_Grab_UI_Releases, (event) => {
|
||||||
event.sender.send('grab_ui_releases_reply');
|
standardIPCReply(event, Constants.IPC_Grab_UI_Releases_Reply);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('install_dependency', (event, data) => {
|
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
|
||||||
helpers
|
helpers
|
||||||
.executeAndWait(data.Source)
|
.executeAndWait(data.Source)
|
||||||
.then(()=> {
|
.then(()=> {
|
||||||
event.sender.send('install_dependency_reply', {
|
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
|
||||||
data: {
|
Source: data.Source,
|
||||||
Source: data.Source,
|
|
||||||
Success: true,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((e)=> {
|
.catch(error => {
|
||||||
event.sender.send('install_dependency_reply', {
|
standardIPCReply(event, Constants.IPC_Install_Dependency_Reply, {
|
||||||
data: {
|
Source: data.Source,
|
||||||
Error: e,
|
}, error);
|
||||||
Source: data.Source,
|
|
||||||
Success: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('install_upgrade', (event, data) => {
|
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
|
||||||
helpers
|
helpers
|
||||||
.executeAsync(data.Source)
|
.executeAsync(data.Source)
|
||||||
.then(()=> {
|
.then(()=> {
|
||||||
mainWindow.close();
|
mainWindow.close();
|
||||||
})
|
})
|
||||||
.catch((e)=> {
|
.catch(error => {
|
||||||
event.sender.send('install_upgrade_reply', {
|
standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, {
|
||||||
data: {
|
Source: data.Source,
|
||||||
Error: e,
|
}, error);
|
||||||
Source: data.Source,
|
|
||||||
Success: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('mount_drive', (event, data) => {
|
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
|
||||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
const errorHandler = (pid) => {
|
const errorHandler = (pid, error) => {
|
||||||
mountedPIDs.splice(mountedPIDs.indexOf(pid), 1);
|
mountedPIDs.splice(mountedPIDs.indexOf(pid), 1);
|
||||||
event.sender.send('unmount_drive_reply', {
|
standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, {
|
||||||
data: {
|
PID: -1,
|
||||||
PID: -1,
|
StorageType: data.StorageType,
|
||||||
StorageType: data.StorageType,
|
}, error || Error(data.StorageType + ' Unmounted'));
|
||||||
Success: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
helpers.executeMount(dataDirectory, data.Version, data.StorageType, data.Location, (_, pid)=> {
|
helpers.executeMount(dataDirectory, data.Version, data.StorageType, data.Location, (error, pid) => {
|
||||||
errorHandler(pid);
|
errorHandler(pid, error);
|
||||||
})
|
})
|
||||||
.then(pid=> {
|
.then(pid => {
|
||||||
if (pid !== -1) {
|
if (pid !== -1) {
|
||||||
mountedPIDs.push(pid);
|
mountedPIDs.push(pid);
|
||||||
}
|
}
|
||||||
event.sender.send('mount_drive_reply', {
|
standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
|
||||||
data: {
|
PID: pid,
|
||||||
PID: pid,
|
StorageType: data.StorageType,
|
||||||
StorageType: data.StorageType,
|
|
||||||
Success: true,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((_, pid) => {
|
.catch(error => {
|
||||||
errorHandler(pid);
|
errorHandler(-1, error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('save_state', (event, data) => {
|
ipcMain.on(Constants.IPC_Save_State, (event, data) => {
|
||||||
const dataDirectory = helpers.resolvePath(data.Directory);
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
helpers.mkDirByPathSync(dataDirectory);
|
helpers.mkDirByPathSync(dataDirectory);
|
||||||
const configFile = path.join(dataDirectory, 'settings.json');
|
const configFile = path.join(dataDirectory, 'settings.json');
|
||||||
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('unmount_drive', (event, data) => {
|
ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => {
|
||||||
helpers.stopProcessByPID(data.PID)
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
||||||
.then((pid)=> {
|
const setConfigValue = (i) => {
|
||||||
if (mountedPIDs.indexOf(pid) === -1) {
|
if (i < data.Items.length) {
|
||||||
event.sender.send('unmount_drive_reply');
|
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, {});
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
.catch((e) => {
|
setConfigValue(0);
|
||||||
console.log(e);
|
});
|
||||||
});
|
|
||||||
|
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(Constants.IPC_Unmount_Drive_Reply);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
116
helpers.js
@@ -80,18 +80,18 @@ module.exports.downloadFile = (url, destination, progressCallback, completeCallb
|
|||||||
|
|
||||||
response.data.on('end', () => {
|
response.data.on('end', () => {
|
||||||
stream.end(() => {
|
stream.end(() => {
|
||||||
completeCallback(true);
|
completeCallback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
response.data.on('error', (e) => {
|
response.data.on('error', (e) => {
|
||||||
stream.end(() => {
|
stream.end(() => {
|
||||||
completeCallback(false, e);
|
completeCallback(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((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 => {
|
module.exports.getMissingDependencies = dependencies => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!dependencies || (dependencies.length === 0)) {
|
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 => {
|
module.exports.stopProcessByPID = pid => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "repertory-ui",
|
"name": "repertory-ui",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.1",
|
"@fortawesome/fontawesome-svg-core": "^1.2.1",
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
"react-dev-utils": "^5.0.1",
|
"react-dev-utils": "^5.0.1",
|
||||||
"react-dom": "^16.4.1",
|
"react-dom": "^16.4.1",
|
||||||
"react-loader-spinner": "^2.0.6",
|
"react-loader-spinner": "^2.0.6",
|
||||||
|
"react-tooltip": "^3.8.4",
|
||||||
"resolve": "1.6.0",
|
"resolve": "1.6.0",
|
||||||
"style-loader": "0.19.0",
|
"style-loader": "0.19.0",
|
||||||
"sw-precache-webpack-plugin": "0.11.4",
|
"sw-precache-webpack-plugin": "0.11.4",
|
||||||
@@ -68,7 +69,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"electron": "3.0.0",
|
"electron": "^3.0.2",
|
||||||
"electron-builder": "^20.28.4",
|
"electron-builder": "^20.28.4",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"webpack-browser-plugin": "^1.0.20"
|
"webpack-browser-plugin": "^1.0.20"
|
||||||
@@ -121,6 +122,7 @@
|
|||||||
"appId": "repertory-ui",
|
"appId": "repertory-ui",
|
||||||
"files": [
|
"files": [
|
||||||
"./electron.js",
|
"./electron.js",
|
||||||
|
"./src/constants.js",
|
||||||
"build/**/*",
|
"build/**/*",
|
||||||
"node_modules/**/*",
|
"node_modules/**/*",
|
||||||
"./helpers.js"
|
"./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": {
|
"Locations": {
|
||||||
"win32": {
|
"win32": {
|
||||||
|
"1.0.1": {
|
||||||
|
"hash": "",
|
||||||
|
"urls": [
|
||||||
|
"https://sia.pixeldrain.com/api/file/Alo1IF1u/download"
|
||||||
|
]
|
||||||
|
},
|
||||||
"1.0.0": {
|
"1.0.0": {
|
||||||
"hash": "",
|
"hash": "",
|
||||||
"urls": [
|
"urls": [
|
||||||
@@ -11,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"Versions": {
|
"Versions": {
|
||||||
"win32": [
|
"win32": [
|
||||||
|
"1.0.1",
|
||||||
"1.0.0"
|
"1.0.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/App.css
@@ -9,3 +9,22 @@
|
|||||||
background-image: url('./assets/images/background.jpg');
|
background-image: url('./assets/images/background.jpg');
|
||||||
background-size: cover;
|
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;
|
||||||
|
}
|
||||||
764
src/App.js
@@ -1,18 +1,22 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import CSSModules from 'react-css-modules';
|
import axios from 'axios';
|
||||||
import styles from './App.css';
|
import styles from './App.css';
|
||||||
import Box from './components/UI/Box/Box';
|
import Box from './components/UI/Box/Box';
|
||||||
import DropDown from './components/UI/DropDown/DropDown';
|
import Configuration from './containers/Configuration/Configuration';
|
||||||
import * as Constants from './constants';
|
import CSSModules from 'react-css-modules';
|
||||||
import axios from 'axios';
|
|
||||||
import MountItems from './containers/MountItems/MountItems';
|
|
||||||
import DependencyList from './components/DependencyList/DependencyList';
|
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 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 UpgradeIcon from './components/UpgradeIcon/UpgradeIcon';
|
||||||
|
import UpgradeUI from './components/UpgradeUI/UpgradeUI';
|
||||||
|
|
||||||
|
const Constants = require('./constants');
|
||||||
const Scheduler = require('node-schedule');
|
const Scheduler = require('node-schedule');
|
||||||
|
|
||||||
let ipcRenderer = null;
|
let ipcRenderer = null;
|
||||||
@@ -25,180 +29,30 @@ class App extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.on('get_platform_reply', (event, arg) => {
|
ipcRenderer.on(Constants.IPC_Check_Installed_Reply, this.onCheckInstalledReply);
|
||||||
this.setState({
|
ipcRenderer.on(Constants.IPC_Download_File_Complete, this.onDownloadFileComplete);
|
||||||
Platform: arg.data,
|
ipcRenderer.on(Constants.IPC_Download_File_Progress, this.onDownloadFileProgress);
|
||||||
});
|
ipcRenderer.on(Constants.IPC_Extract_Release_Complete, this.onExtractReleaseComplete);
|
||||||
ipcRenderer.send('get_state', Constants.DATA_LOCATIONS[arg.data]);
|
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) => {
|
ipcRenderer.send(Constants.IPC_Get_State, Constants.DATA_LOCATIONS[this.props.platform]);
|
||||||
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');
|
|
||||||
Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob);
|
Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
AllowOptions: false,
|
|
||||||
AllowDownload: false,
|
AllowDownload: false,
|
||||||
AutoMountChecked: false,
|
AutoMountProcessed: false,
|
||||||
|
ConfigStorageType: null,
|
||||||
|
DisplayError: false,
|
||||||
|
DisplayMainContent: false,
|
||||||
|
Error: null,
|
||||||
|
ErrorAction: null,
|
||||||
|
ErrorCritical: false,
|
||||||
DownloadActive: false,
|
DownloadActive: false,
|
||||||
DownloadProgress: 0.0,
|
DownloadProgress: 0.0,
|
||||||
DownloadingDependency: false,
|
DownloadingDependency: false,
|
||||||
@@ -212,6 +66,7 @@ class App extends Component {
|
|||||||
},
|
},
|
||||||
LocationsLookup: {},
|
LocationsLookup: {},
|
||||||
MissingDependencies: [],
|
MissingDependencies: [],
|
||||||
|
MountsBusy: false,
|
||||||
Platform: 'unknown',
|
Platform: 'unknown',
|
||||||
Release: 3,
|
Release: 3,
|
||||||
ReleaseTypes: [
|
ReleaseTypes: [
|
||||||
@@ -220,7 +75,7 @@ class App extends Component {
|
|||||||
'Beta',
|
'Beta',
|
||||||
'Alpha',
|
'Alpha',
|
||||||
],
|
],
|
||||||
RepertoryVersion: 'none',
|
InstalledVersion: 'none',
|
||||||
Sia: {
|
Sia: {
|
||||||
AutoMount: false,
|
AutoMount: false,
|
||||||
MountLocation: '',
|
MountLocation: '',
|
||||||
@@ -228,7 +83,8 @@ class App extends Component {
|
|||||||
UpgradeAvailable: false,
|
UpgradeAvailable: false,
|
||||||
UpgradeData: {},
|
UpgradeData: {},
|
||||||
UpgradeDismissed: false,
|
UpgradeDismissed: false,
|
||||||
Version: 0,
|
Version: -1,
|
||||||
|
VersionAvailable: false,
|
||||||
VersionLookup: {
|
VersionLookup: {
|
||||||
Alpha: [
|
Alpha: [
|
||||||
'unavailable'
|
'unavailable'
|
||||||
@@ -255,25 +111,58 @@ class App extends Component {
|
|||||||
AllowDownload: false,
|
AllowDownload: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ipcRenderer) {
|
if (selectedVersion !== 'unavailable') {
|
||||||
let dependencies = [];
|
if (ipcRenderer) {
|
||||||
if (this.state.LocationsLookup[selectedVersion] && this.state.LocationsLookup[selectedVersion].dependencies) {
|
let dependencies = [];
|
||||||
dependencies = this.state.LocationsLookup[selectedVersion].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,
|
Dependencies: dependencies,
|
||||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||||
Version: selectedVersion,
|
Version: selectedVersion,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
closeErrorDisplay = () => {
|
||||||
|
if (this.state.ErrorAction) {
|
||||||
|
this.state.ErrorAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.ErrorCritical) {
|
||||||
|
if (ipcRenderer) {
|
||||||
|
ipcRenderer.send(Constants.IPC_Shutdown);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
DisplayError: false,
|
||||||
|
Error: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
if (ipcRenderer) {
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Check_Installed_Reply, this.onCheckInstalledReply);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Download_File_Complete, this.onDownloadFileComplete);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Download_File_Progress, this.onDownloadFileProgress);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Extract_Release_Complete, this.onExtractReleaseComplete);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Get_State_Reply, this.onGetStateReply);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Grab_Releases_Reply, this.onGrabReleasesReply);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Grab_UI_Releases_Reply, this.onGrabUiReleasesReply);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Install_Dependency_Reply, this.onInstallDependencyReply);
|
||||||
|
ipcRenderer.removeListener(Constants.IPC_Install_Upgrade_Reply, this.onInstallUpgradeReply);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
grabReleases = () => {
|
grabReleases = () => {
|
||||||
if (this.state.Platform !== 'unknown') {
|
if (this.props.platform !== 'unknown') {
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.send('grab_releases');
|
ipcRenderer.send(Constants.IPC_Grab_Releases);
|
||||||
ipcRenderer.send('grab_ui_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);
|
this.saveState(this.state.Release, this.state.Version, sia, hyperspace);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleConfigClicked = (storageType) => {
|
||||||
|
this.setState({
|
||||||
|
ConfigStorageType: storageType,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
handleConfigClosed = () => {
|
||||||
|
this.setState({
|
||||||
|
ConfigStorageType: null,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
handleDependencyDownload = (url) => {
|
handleDependencyDownload = (url) => {
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
const items = url.split('/');
|
const items = url.split('/');
|
||||||
@@ -313,8 +214,8 @@ class App extends Component {
|
|||||||
DownloadName: fileName,
|
DownloadName: fileName,
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.send('download_file', {
|
ipcRenderer.send(Constants.IPC_Download_File, {
|
||||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||||
Filename: fileName,
|
Filename: fileName,
|
||||||
URL: url,
|
URL: url,
|
||||||
});
|
});
|
||||||
@@ -341,12 +242,13 @@ class App extends Component {
|
|||||||
|
|
||||||
handleReleaseChanged = (e) => {
|
handleReleaseChanged = (e) => {
|
||||||
const val = parseInt(e.target.value, 10);
|
const val = parseInt(e.target.value, 10);
|
||||||
|
const versionIndex = this.state.VersionLookup[this.state.ReleaseTypes[val]].length - 1;
|
||||||
this.setState({
|
this.setState({
|
||||||
Release: val,
|
Release: val,
|
||||||
Version: 0
|
Version: versionIndex
|
||||||
});
|
});
|
||||||
this.saveState(val, 0, this.state.Sia, this.state.Hyperspace);
|
this.saveState(val, versionIndex, this.state.Sia, this.state.Hyperspace);
|
||||||
this.checkVersionInstalled(val, 0);
|
this.checkVersionInstalled(val, versionIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleReleaseDownload = () => {
|
handleReleaseDownload = () => {
|
||||||
@@ -359,8 +261,8 @@ class App extends Component {
|
|||||||
DownloadName: fileName,
|
DownloadName: fileName,
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.send('download_file', {
|
ipcRenderer.send(Constants.IPC_Download_File, {
|
||||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||||
Filename: fileName,
|
Filename: fileName,
|
||||||
URL: this.state.LocationsLookup[selectedVersion].urls[0],
|
URL: this.state.LocationsLookup[selectedVersion].urls[0],
|
||||||
});
|
});
|
||||||
@@ -375,9 +277,9 @@ class App extends Component {
|
|||||||
DownloadName: 'UI Upgrade',
|
DownloadName: 'UI Upgrade',
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.send('download_file', {
|
ipcRenderer.send(Constants.IPC_Download_File, {
|
||||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||||
Filename: this.state.Platform === 'win32' ? 'upgrade.exe' : 'upgrade',
|
Filename: this.props.platform === 'win32' ? 'upgrade.exe' : 'upgrade',
|
||||||
URL: this.state.UpgradeData.urls[0],
|
URL: this.state.UpgradeData.urls[0],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -395,13 +297,228 @@ class App extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
notifyAutoMountProcessed = () => {
|
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)=> {
|
saveState = (release, version, sia, hyperspace)=> {
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.send('save_state', {
|
ipcRenderer.send(Constants.IPC_Save_State, {
|
||||||
Directory: Constants.DATA_LOCATIONS[this.state.Platform],
|
Directory: Constants.DATA_LOCATIONS[this.props.platform],
|
||||||
State: {
|
State: {
|
||||||
Hyperspace: hyperspace,
|
Hyperspace: hyperspace,
|
||||||
Release: release,
|
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 = () => {
|
updateCheckScheduledJob = () => {
|
||||||
if (this.state.Platform !== 'unknown') {
|
if (this.props.platform !== 'unknown') {
|
||||||
if (ipcRenderer) {
|
this.grabReleases();
|
||||||
ipcRenderer.send('grab_ui_releases');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const selectedVersion = this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
const selectedVersion = (this.state.Version === -1) ?
|
||||||
const downloadEnabled = this.state.AllowDownload &&
|
'unavailable' :
|
||||||
!this.state.DownloadActive &&
|
this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]][this.state.Version];
|
||||||
(((selectedVersion !== 'unavailable') && (selectedVersion !== this.state.RepertoryVersion)));
|
|
||||||
const allowMount = this.state.RepertoryVersion !== 'none';
|
|
||||||
const missingDependencies = (this.state.MissingDependencies.length > 0);
|
|
||||||
|
|
||||||
let mountDisplay = null;
|
const downloadEnabled = this.state.AllowDownload &&
|
||||||
if (allowMount) {
|
!this.state.MountsBusy &&
|
||||||
mountDisplay = <MountItems platform={this.state.Platform}
|
!this.state.DownloadActive &&
|
||||||
sia={this.state.Sia}
|
(selectedVersion !== 'unavailable') &&
|
||||||
hyperspace={this.state.Hyperspace}
|
(selectedVersion !== this.state.InstalledVersion);
|
||||||
changed={this.handleMountLocationChanged}
|
|
||||||
processAutoMount={!this.state.AutoMountChecked}
|
const allowMount = this.state.InstalledVersion !== 'none';
|
||||||
autoMountProcessed={this.notifyAutoMountProcessed}
|
const missingDependencies = (this.state.MissingDependencies.length > 0);
|
||||||
autoMountChanged={this.handleAutoMountChanged}
|
const allowConfig = this.state.LocationsLookup[selectedVersion] &&
|
||||||
version={this.state.RepertoryVersion}
|
this.state.LocationsLookup[selectedVersion].config_support;
|
||||||
directory={Constants.DATA_LOCATIONS[this.state.Platform]}
|
|
||||||
disabled={!allowMount}/>;
|
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;
|
let dependencyDisplay = null;
|
||||||
if (missingDependencies && !this.state.DownloadActive) {
|
if (showDependencies) {
|
||||||
dependencyDisplay = (
|
dependencyDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<DependencyList allowDownload={!this.state.DownloadingDependency}
|
<DependencyList allowDownload={!this.state.DownloadingDependency}
|
||||||
@@ -458,104 +614,100 @@ class App extends Component {
|
|||||||
if (this.state.DownloadActive) {
|
if (this.state.DownloadActive) {
|
||||||
downloadDisplay = (
|
downloadDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<DownloadProgress progress={this.state.DownloadProgress}
|
<DownloadProgress display={this.state.DownloadName}
|
||||||
display={this.state.DownloadName}/>
|
progress={this.state.DownloadProgress}/>
|
||||||
</Modal>);
|
</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;
|
let upgradeDisplay = null;
|
||||||
if (!missingDependencies &&
|
if (showUpgrade) {
|
||||||
!this.state.DownloadActive &&
|
|
||||||
this.state.UpgradeAvailable &&
|
|
||||||
!this.state.UpgradeDismissed) {
|
|
||||||
upgradeDisplay = (
|
upgradeDisplay = (
|
||||||
<Modal>
|
<Modal>
|
||||||
<UpgradeUI upgrade={this.handleUIDownload}
|
<UpgradeUI cancel={()=>this.setState({UpgradeDismissed: true})}
|
||||||
cancel={()=>this.setState({UpgradeDismissed: true})}/>
|
upgrade={this.handleUIDownload}/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = null;
|
let mainContent = [];
|
||||||
if (this.state.AllowOptions) {
|
if (this.state.DisplayMainContent) {
|
||||||
options = (
|
let key = 0;
|
||||||
<table width='100%' cellPadding='2'>
|
mainContent.push((
|
||||||
<tbody>
|
<div key={'rvd_' + key++}
|
||||||
<tr>
|
style={{height: '44%'}}>
|
||||||
<td width='33%'>
|
<ReleaseVersionDisplay disabled={this.state.DownloadActive || this.state.ExtractActive || this.state.MountsBusy}
|
||||||
<h2>Release</h2>
|
downloadClicked={this.handleReleaseDownload}
|
||||||
</td>
|
downloadDisabled={!downloadEnabled}
|
||||||
<td width='33%'>
|
installedVersion={this.state.InstalledVersion}
|
||||||
<h2>Version</h2>
|
release={this.state.Release}
|
||||||
</td>
|
releaseChanged={this.handleReleaseChanged}
|
||||||
<td width='33%'>
|
releaseExtracting={this.state.ExtractActive}
|
||||||
<h2>Installed</h2>
|
releaseTypes={this.state.ReleaseTypes}
|
||||||
</td>
|
version={this.state.Version}
|
||||||
</tr>
|
versionAvailable={this.state.VersionAvailable}
|
||||||
<tr>
|
versionChanged={this.handleVersionChanged}
|
||||||
<td>
|
versions={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}/>
|
||||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive}
|
</div>
|
||||||
items={this.state.ReleaseTypes}
|
));
|
||||||
selected={this.state.Release}
|
|
||||||
changed={this.handleReleaseChanged}/>
|
if (allowMount) {
|
||||||
</td>
|
mainContent.push((
|
||||||
<td>
|
<div key={'md_' + key++}
|
||||||
<DropDown disabled={this.state.DownloadActive || this.state.ExtractActive}
|
style={{height: '56%'}}>
|
||||||
items={this.state.VersionLookup[this.state.ReleaseTypes[this.state.Release]]}
|
<MountItems allowConfig={allowConfig}
|
||||||
selected={this.state.Version}
|
autoMountChanged={this.handleAutoMountChanged}
|
||||||
changed={this.handleVersionChanged}/>
|
autoMountProcessed={this.notifyAutoMountProcessed}
|
||||||
</td>
|
changed={this.handleMountLocationChanged}
|
||||||
<td>
|
configClicked={this.handleConfigClicked}
|
||||||
{this.state.RepertoryVersion}
|
directory={Constants.DATA_LOCATIONS[this.props.platform]}
|
||||||
</td>
|
errorHandler={this.setErrorState}
|
||||||
</tr>
|
hyperspace={this.state.Hyperspace}
|
||||||
<tr>
|
mountsBusy={this.notifyMountsBusy}
|
||||||
<td colSpan={3}>
|
platform={this.props.platform}
|
||||||
{releaseDisplay}
|
processAutoMount={!this.state.AutoMountProcessed}
|
||||||
</td>
|
sia={this.state.Sia}
|
||||||
</tr>
|
version={this.state.InstalledVersion}/>
|
||||||
<tr>
|
</div>
|
||||||
<td colSpan={3}>
|
));
|
||||||
{mountDisplay}
|
}
|
||||||
</td>
|
} else {
|
||||||
</tr>
|
mainContent = <Loading/>
|
||||||
</tbody>
|
|
||||||
</table>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div styleName='App'>
|
<div styleName='App'>
|
||||||
|
{errorDisplay}
|
||||||
{dependencyDisplay}
|
{dependencyDisplay}
|
||||||
{upgradeDisplay}
|
{upgradeDisplay}
|
||||||
{downloadDisplay}
|
{downloadDisplay}
|
||||||
<Box dxDark dxStyle={{'height': 'auto', 'padding': '2px'}}>
|
{configDisplay}
|
||||||
<table cellPadding={0} cellSpacing={0} style={{margin: 0, padding: 0}}>
|
<div styleName='Container'>
|
||||||
<tbody style={{margin: 0, padding: 0}}>
|
<div styleName='Header'>
|
||||||
<tr style={{margin: 0, padding: 0}}>
|
<Box>
|
||||||
<td width='33%' style={{margin: 0, padding: 0}}/>
|
<Grid>
|
||||||
<td width='33%' style={{margin: 0, padding: 0}}>
|
<Text col={0}
|
||||||
<h1 style={{'textAlign': 'center'}}>{'Repertory UI v' + this.props.version}</h1>
|
colSpan={'remain'}
|
||||||
</td>
|
row={0}
|
||||||
<td width='33%' style={{margin: 0, padding: 0}} align='right' valign='middle'>
|
rowSpan={'remain'}
|
||||||
<UpgradeIcon
|
text={'Repertory UI v' + this.props.version}
|
||||||
available={this.state.UpgradeAvailable}
|
textAlign={'center'}
|
||||||
clicked={()=>this.setState({UpgradeDismissed: false})}/>
|
type={'Heading1'}/>
|
||||||
</td>
|
<UpgradeIcon
|
||||||
</tr>
|
available={this.state.UpgradeAvailable}
|
||||||
</tbody>
|
clicked={()=>this.setState({UpgradeDismissed: false})}
|
||||||
</table>
|
col={dimensions => dimensions.columns - 6}
|
||||||
</Box>
|
colSpan={5}
|
||||||
<Box dxStyle={{'padding': '4px', 'marginTop': '10px'}}>
|
row={1}
|
||||||
{options}
|
rowSpan={remain=>remain - 2}/>
|
||||||
</Box>
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
<div styleName='Content'>
|
||||||
|
<Box dxStyle={{padding: '8px'}}>
|
||||||
|
{mainContent}
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
</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)=> {
|
const items = props.dependencies.map((k, i)=> {
|
||||||
return (
|
return (
|
||||||
<Dependency allowDownload={props.allowDownload}
|
<Dependency allowDownload={props.allowDownload}
|
||||||
|
download={k.download}
|
||||||
key={i}
|
key={i}
|
||||||
name={k.display}
|
name={k.display}
|
||||||
download={k.download}
|
|
||||||
onDownload={props.onDownload}/>
|
onDownload={props.onDownload}/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
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'}}>
|
<div style={{width: '100%', height: 'auto', paddingBottom: '5px', boxSizing: 'border-box'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>Missing Dependencies</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>Missing Dependencies</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import styles from './DownloadProgress.css';
|
|||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box dxDark dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
<Box dxStyle={{width: '380px', height: 'auto', padding: '5px'}}>
|
||||||
<div style={{width: '100%', height: 'auto'}}>
|
<div style={{width: '100%', height: 'auto'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.display}</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>{'Downloading ' + props.display}</h1>
|
||||||
</div>
|
</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 DropDown from '../UI/DropDown/DropDown';
|
||||||
import Button from '../UI/Button/Button';
|
import Button from '../UI/Button/Button';
|
||||||
import Loader from 'react-loader-spinner';
|
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) => {
|
export default CSSModules((props) => {
|
||||||
let inputControl = null;
|
let configButton = null;
|
||||||
let mountWidth = '70%';
|
let secondRow = 6;
|
||||||
if (props.platform === 'win32') {
|
if (props.allowConfig) {
|
||||||
inputControl = <DropDown disabled={!props.allowMount || props.mounted}
|
configButton = (
|
||||||
items={props.items}
|
<RootElem colSpan={4}
|
||||||
selected={props.items.indexOf(props.location)}
|
rowSpan={6}>
|
||||||
changed={props.changed}/>;
|
<img alt=''
|
||||||
mountWidth = '18%';
|
height={'16px'}
|
||||||
} else {
|
onClick={props.configClicked}
|
||||||
inputControl = <input disabled={!props.allowMount || props.mounted}
|
src={configureImage}
|
||||||
type={'text'}
|
style={{padding: 0, border: 0, margin: 0, cursor: 'pointer'}}
|
||||||
value={props.location}
|
width={'16px'}/>
|
||||||
onChange={props.changed}/>;
|
</RootElem>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let actionDisplay = null;
|
let inputColumnSpan;
|
||||||
if (props.allowMount) {
|
let inputControl = null;
|
||||||
actionDisplay = <Button buttonStyles={{width: '100%'}}
|
if (props.platform === 'win32') {
|
||||||
clicked={()=>props.clicked(props.title, !props.mounted, props.location, props.pid)}>{props.mounted ? 'Unmount' : 'Mount'}</Button>;
|
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 {
|
} else {
|
||||||
actionDisplay = <Loader color={'var(--heading_text_color)'}
|
inputColumnSpan = 60;
|
||||||
height='24px'
|
inputControl = (
|
||||||
width='24px'
|
<RootElem colSpan={inputColumnSpan}
|
||||||
type='Circles'/>;
|
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 (
|
return (
|
||||||
<div styleName='MountItem'>
|
<Grid>
|
||||||
<h2>{props.title}</h2>
|
{configButton}
|
||||||
<table width='100%' cellPadding='2'>
|
<Text
|
||||||
<tbody>
|
col={configButton ? 6 : 0}
|
||||||
<tr>
|
rowSpan={5}
|
||||||
<td width={mountWidth} height='30px'>{inputControl}</td>
|
text={props.title}
|
||||||
<td width='25%' align='center' valign='middle'>
|
type={'Heading1'}/>
|
||||||
{actionDisplay}
|
{inputControl}
|
||||||
</td>
|
{actionsDisplay}
|
||||||
<td>
|
{autoMountControl}
|
||||||
<input type='checkbox' checked={props.autoMount} onChange={props.autoMountChanged}/>Auto-mount
|
</Grid>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}, styles, {allowMultiple: true});
|
}, 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';
|
import styles from './Box.css';
|
||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
const styleList = ['Box'];
|
const styleList = [];
|
||||||
|
styleList.push('Box');
|
||||||
if (props.dxDark) {
|
if (props.dxDark) {
|
||||||
styleList.push('Darker');
|
styleList.push('Darker');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
.Button {
|
.Button {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
color: var(--text_color);
|
color: var(--text_color);
|
||||||
@@ -10,7 +9,10 @@
|
|||||||
border: none;
|
border: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-outline: none;
|
text-outline: none;
|
||||||
width: 70px;
|
vertical-align: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Button:hover:enabled {
|
.Button:hover:enabled {
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.Button:hover:disabled {
|
.Button:hover:disabled {
|
||||||
cursor: default;
|
cursor: no-drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Button:active,
|
.Button:active,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
.DropDown {
|
.DropDown {
|
||||||
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@@ -22,3 +24,16 @@
|
|||||||
border-color: rgba(10, 10, 20, 0.9);
|
border-color: rgba(10, 10, 20, 0.9);
|
||||||
color: var(--text_color);
|
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 React from 'react';
|
||||||
import CSSModules from 'react-css-modules';
|
|
||||||
import styles from './DropDown.css';
|
import styles from './DropDown.css';
|
||||||
|
import CSSModules from 'react-css-modules';
|
||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
const options = props.items.map((s, i) => {
|
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%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 2001;
|
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'
|
import styles from './Modal.css'
|
||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
|
let modalStyles = [];
|
||||||
|
let contentStyles = [];
|
||||||
|
modalStyles.push('Modal');
|
||||||
|
contentStyles.push('Content');
|
||||||
|
if (props.critical) {
|
||||||
|
modalStyles.push('Critical');
|
||||||
|
contentStyles.push('Critical');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
styleName='Modal'
|
styleName={modalStyles.join(' ')}
|
||||||
onClick={props.clicked}>
|
onClick={props.clicked}>
|
||||||
<div styleName='Content'>
|
<div styleName={contentStyles.join(' ')}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
</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 {
|
.Owner {
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 20px;
|
margin: 0;
|
||||||
height: 20px;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.UpgradeIcon {
|
.UpgradeIcon {
|
||||||
cursor: default;
|
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 React from 'react';
|
||||||
import CSSModules from 'react-css-modules';
|
import CSSModules from 'react-css-modules';
|
||||||
import styles from './UpgradeIcon.css';
|
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) => {
|
export default CSSModules((props) => {
|
||||||
return props.available ?
|
let placement = 'left';
|
||||||
<img alt='' styleName='UpgradeIcon' src={availableImage} onClick={props.clicked}/> :
|
let toolTipText = 'UI Upgrade Available';
|
||||||
<div styleName='UpgradeIcon'/> ;
|
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});
|
}, styles, {allowMultiple: true});
|
||||||
@@ -6,22 +6,22 @@ import styles from './UpgradeUI.css';
|
|||||||
|
|
||||||
export default CSSModules((props) => {
|
export default CSSModules((props) => {
|
||||||
return (
|
return (
|
||||||
<Box dxDark dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
<Box dxStyle={{width: '180px', height: 'auto', padding: '5px'}}>
|
||||||
<div style={{width: '100%', height: 'auto'}}>
|
<div style={{width: '100%', height: 'auto'}}>
|
||||||
<h1 style={{width: '100%', textAlign: 'center'}}>UI Upgrade Available</h1>
|
<h1 style={{width: '100%', textAlign: 'center'}}>UI Upgrade Available</h1>
|
||||||
</div>
|
</div>
|
||||||
<table cellSpacing={5} width="100%">
|
<table cellSpacing={5} width="100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="50%">
|
<td width="50%">
|
||||||
<Button buttonStyles={{width: '100%'}}
|
<Button buttonStyles={{width: '100%'}}
|
||||||
clicked={props.upgrade}>Install</Button>
|
clicked={props.upgrade}>Install</Button>
|
||||||
</td>
|
</td>
|
||||||
<td width="50%">
|
<td width="50%">
|
||||||
<Button buttonStyles={{width: '100%'}}
|
<Button buttonStyles={{width: '100%'}}
|
||||||
clicked={props.cancel}>Cancel</Button>
|
clicked={props.cancel}>Cancel</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Box>);
|
</Box>);
|
||||||
|
|||||||
@@ -1,7 +1,62 @@
|
|||||||
export const RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/master/releases.json';
|
Object.defineProperty(exports, "__esModule", {
|
||||||
export const DATA_LOCATIONS = {
|
value: true
|
||||||
|
});
|
||||||
|
exports.RELEASES_URL = 'https://bitbucket.org/blockstorage/repertory/raw/master/releases.json';
|
||||||
|
exports.DATA_LOCATIONS = {
|
||||||
linux: '~/.local/repertory/ui',
|
linux: '~/.local/repertory/ui',
|
||||||
darwin: '~/Library/Application Support/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 {
|
.MountItems {
|
||||||
margin-top: 10px;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import CSSModules from 'react-css-modules';
|
|||||||
import styles from './MountItems.css';
|
import styles from './MountItems.css';
|
||||||
import MountItem from '../../components/MountItem/MountItem';
|
import MountItem from '../../components/MountItem/MountItem';
|
||||||
|
|
||||||
|
const Constants = require('../../constants');
|
||||||
|
|
||||||
let ipcRenderer = null;
|
let ipcRenderer = null;
|
||||||
if (!process.versions.hasOwnProperty('electron')) {
|
if (!process.versions.hasOwnProperty('electron')) {
|
||||||
ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
|
ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null);
|
||||||
@@ -13,64 +15,9 @@ class MountItems extends Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.on('detect_mounts_reply', (event, arg) => {
|
ipcRenderer.on(Constants.IPC_Detect_Mounts_Reply, this.onDetectMountsReply);
|
||||||
if (arg.data.Success) {
|
ipcRenderer.on(Constants.IPC_Mount_Drive_Reply, this.onMountDriveReply);
|
||||||
const sia = {
|
ipcRenderer.on(Constants.IPC_Unmount_Drive_Reply, this.onUnmountDriveReply);
|
||||||
...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();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.detectMounts();
|
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 = ()=> {
|
detectMounts = ()=> {
|
||||||
ipcRenderer.send('detect_mounts', {
|
this.props.mountsBusy(true);
|
||||||
|
ipcRenderer.send(Constants.IPC_Detect_Mounts, {
|
||||||
Directory: this.props.directory,
|
Directory: this.props.directory,
|
||||||
Version: this.props.version,
|
Version: this.props.version,
|
||||||
});
|
});
|
||||||
@@ -117,15 +73,17 @@ class MountItems extends Component {
|
|||||||
[storageType]: state,
|
[storageType]: state,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.props.mountsBusy(true);
|
||||||
|
|
||||||
if (mount) {
|
if (mount) {
|
||||||
ipcRenderer.send('mount_drive', {
|
ipcRenderer.send(Constants.IPC_Mount_Drive, {
|
||||||
Directory: this.props.directory,
|
Directory: this.props.directory,
|
||||||
Location: location,
|
Location: location,
|
||||||
StorageType: storageType,
|
StorageType: storageType,
|
||||||
Version: this.props.version,
|
Version: this.props.version,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ipcRenderer.send('unmount_drive', {
|
ipcRenderer.send(Constants.IPC_Unmount_Drive, {
|
||||||
Directory: this.props.directory,
|
Directory: this.props.directory,
|
||||||
Location: location,
|
Location: location,
|
||||||
PID: pid,
|
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 = ()=> {
|
performAutoMount = ()=> {
|
||||||
if (this.props.processAutoMount) {
|
if (this.props.processAutoMount) {
|
||||||
this.props.autoMountProcessed();
|
this.props.autoMountProcessed();
|
||||||
@@ -155,28 +176,33 @@ class MountItems extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div styleName='MountItems'>
|
<div styleName='MountItems'>
|
||||||
<MountItem allowMount={this.state.Hyperspace.AllowMount}
|
<MountItem allowConfig={this.props.allowConfig}
|
||||||
|
allowMount={this.state.Hyperspace.AllowMount}
|
||||||
autoMount={this.props.hyperspace.AutoMount}
|
autoMount={this.props.hyperspace.AutoMount}
|
||||||
autoMountChanged={(e)=>this.props.autoMountChanged('Hyperspace', e)}
|
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)}
|
changed={(e) => this.handleMountLocationChanged('Hyperspace', e.target.value)}
|
||||||
clicked={this.handleMountUnMount}
|
clicked={this.handleMountUnMount}
|
||||||
pid={this.state.Hyperspace.PID}/>
|
configClicked={()=>this.props.configClicked('Hyperspace')}
|
||||||
<MountItem allowMount={this.state.Sia.AllowMount}
|
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}
|
autoMount={this.props.sia.AutoMount}
|
||||||
autoMountChanged={(e)=>this.props.autoMountChanged('Sia', e)}
|
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)}
|
changed={(e) => this.handleMountLocationChanged('Sia', e.target.value)}
|
||||||
clicked={this.handleMountUnMount}
|
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>);
|
</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;
|
--border_radius: 4px;
|
||||||
|
|
||||||
--control_background: rgba(150, 150, 190, .15);
|
--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_border: 1px solid rgba(70, 70, 70, 0.9);
|
||||||
--control_box_shadow: 1px 1px 1px black;
|
--control_box_shadow: 1px 1px 1px black;
|
||||||
--control_transparent_background: rgba(60, 60, 70, 0.4);
|
--control_transparent_background: rgba(40, 40, 55, 0.45);
|
||||||
--control_dark_transparent_background: rgba(60, 60, 70, 0.4);
|
--control_dark_transparent_background: rgba(15, 15, 15, 0.8);
|
||||||
|
|
||||||
--text_color: rgba(200, 205, 220, 0.7);
|
--text_color: rgba(200, 205, 220, 0.7);
|
||||||
--text_color_hover: rgba(200, 205, 220, 0.7);
|
--text_color_hover: rgba(200, 205, 220, 0.7);
|
||||||
--heading_text_color: rgba(194, 217, 255, 0.6);
|
--text_color_error: rgba(203, 120, 120, 0.8);
|
||||||
--heading_other_text_color: rgba(200, 205, 220, 0.7);
|
--heading_text_color: rgba(161, 190, 219, 0.7);
|
||||||
|
--heading_other_text_color: var(--heading_text_color);
|
||||||
--text_color_transition: color 0.3s;
|
--text_color_transition: color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: 'Nunito', sans-serif;
|
||||||
font-size: 15px;
|
font-size: 5vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-moz-focus-inner {
|
*::-moz-focus-inner {
|
||||||
@@ -61,3 +62,27 @@ h1 {
|
|||||||
h2, h3 {
|
h2, h3 {
|
||||||
color: var(--heading_other_text_color);
|
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 React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import registerServiceWorker from './registerServiceWorker';
|
|
||||||
import packageJson from '../package.json';
|
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();
|
|
||||||
|
|||||||