504 lines
13 KiB
JavaScript
504 lines
13 KiB
JavaScript
// Modules to control application life and create native browser window
|
|
|
|
const {app, BrowserWindow, Tray, nativeImage, Menu} = require('electron');
|
|
const {ipcMain} = require('electron');
|
|
const Constants = require('./src/constants');
|
|
const path = require('path');
|
|
const url = require('url');
|
|
require('electron-debug')();
|
|
const os = require('os');
|
|
const helpers = require('./helpers');
|
|
const fs = require('fs');
|
|
const unzip = require('unzipper');
|
|
const AutoLaunch = require('auto-launch');
|
|
|
|
// Keep a global reference of the window object, if you don't, the window will
|
|
// be closed automatically when the JavaScript object is garbage collected.
|
|
let mainContextWindow;
|
|
let mainWindow;
|
|
let mainWindowTray;
|
|
let mountedPIDs = [];
|
|
|
|
function createWindow() {
|
|
// Create the browser window.
|
|
const height = process.env.ELECTRON_START_URL ? 340 : 320;
|
|
mainWindow = new BrowserWindow({
|
|
width: 425,
|
|
height: height,
|
|
resizable: false,
|
|
webPreferences: {
|
|
webSecurity: !process.env.ELECTRON_START_URL
|
|
}
|
|
});
|
|
|
|
// and load the index.html of the app.
|
|
const startUrl = process.env.ELECTRON_START_URL || url.format({
|
|
pathname: path.join(__dirname, '/build/index.html'),
|
|
protocol: 'file:',
|
|
slashes: true
|
|
});
|
|
mainWindow.loadURL(startUrl);
|
|
|
|
// Emitted when the window is closed.
|
|
mainWindow.on('closed', function () {
|
|
// Dereference the window object, usually you would store windows
|
|
// in an array if your app supports multi windows, this is the time
|
|
// when you should delete the corresponding element.
|
|
mainWindow = null
|
|
});
|
|
|
|
if (os.platform() === 'win32') {
|
|
const autoLauncher = new AutoLaunch({
|
|
name: 'Repertory UI',
|
|
path: path.resolve(path.join(app.getAppPath(), '..\\..\\repertory-ui.exe')),
|
|
});
|
|
|
|
const image = nativeImage.createFromPath(path.join(__dirname, '/build/icon.ico'));
|
|
mainContextWindow = Menu.buildFromTemplate([
|
|
{
|
|
label: 'Visible', type: 'checkbox', click(item) {
|
|
if (item.checked) {
|
|
mainWindow.show();
|
|
if (mainWindow.isMinimized()) {
|
|
mainWindow.restore();
|
|
}
|
|
mainWindow.focus()
|
|
} else {
|
|
mainWindow.hide();
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: 'Auto-start', type: 'checkbox', click(item) {
|
|
if (item.checked) {
|
|
autoLauncher.enable();
|
|
} else {
|
|
autoLauncher.disable();
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: 'Exit', click(item) {
|
|
app.quit();
|
|
}
|
|
}
|
|
]);
|
|
|
|
mainContextWindow.items[0].checked = true;
|
|
autoLauncher.isEnabled()
|
|
.then((enabled) => {
|
|
mainContextWindow.items[1].checked = enabled;
|
|
|
|
mainWindowTray = new Tray(image);
|
|
mainWindowTray.setToolTip('Repertory UI');
|
|
mainWindowTray.setContextMenu(mainContextWindow)
|
|
})
|
|
.catch(() => {
|
|
app.quit();
|
|
});
|
|
}
|
|
}
|
|
|
|
const instanceLock = app.requestSingleInstanceLock();
|
|
if (!instanceLock) {
|
|
app.quit()
|
|
} else {
|
|
app.on('second-instance', () => {
|
|
if (mainWindow) {
|
|
mainWindow.show();
|
|
if (mainContextWindow) {
|
|
mainContextWindow.items[0].checked = true;
|
|
}
|
|
if (mainWindow.isMinimized()) {
|
|
mainWindow.restore();
|
|
}
|
|
mainWindow.focus()
|
|
}
|
|
});
|
|
|
|
app.on('ready', createWindow);
|
|
|
|
app.on('window-all-closed', () => {
|
|
// On OS X it is common for applications and their menu bar
|
|
// to stay active until the user quits explicitly with Cmd + Q
|
|
if (process.platform !== 'darwin') {
|
|
app.quit()
|
|
}
|
|
});
|
|
|
|
app.on('activate', () => {
|
|
// On OS X it's common to re-create a window in the app when the
|
|
// dock icon is clicked and there are no other windows open.
|
|
if (mainWindow === null) {
|
|
createWindow()
|
|
}
|
|
});
|
|
}
|
|
|
|
ipcMain.on(Constants.IPC_Check_Installed, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
const destination = path.join(dataDirectory, data.Version);
|
|
helpers
|
|
.getMissingDependencies(data.Dependencies)
|
|
.then((dependencies) => {
|
|
let exists = false;
|
|
try {
|
|
exists = fs.existsSync(destination) && fs.lstatSync(destination).isDirectory();
|
|
} catch (e) {
|
|
}
|
|
event.sender.send(Constants.IPC_Check_Installed_Reply, {
|
|
data: {
|
|
Dependencies: dependencies,
|
|
Exists: exists,
|
|
Success: true,
|
|
Version: data.Version,
|
|
}
|
|
});
|
|
}).catch((e) => {
|
|
event.sender.send(Constants.IPC_Check_Installed_Reply, {
|
|
data: {
|
|
Dependencies: [],
|
|
Error: e,
|
|
Success: false,
|
|
Version: data.Version,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Delete_File, (event, data) => {
|
|
try {
|
|
if (fs.existsSync(data.FilePath)) {
|
|
fs.unlinkSync(data.FilePath);
|
|
}
|
|
} catch (e) {
|
|
}
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Detect_Mounts, (event, data) => {
|
|
let driveLetters = {
|
|
Hyperspace: [],
|
|
Sia: [],
|
|
};
|
|
|
|
const grabDriveLetters = (hsLocation, siaLocation) => {
|
|
for (let i = 'c'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) {
|
|
const drive = (String.fromCharCode(i) + ':').toUpperCase();
|
|
if (!(hsLocation.startsWith(drive) || siaLocation.startsWith(drive))) {
|
|
try {
|
|
if (!fs.existsSync(drive)) {
|
|
driveLetters.Hyperspace.push(drive);
|
|
driveLetters.Sia.push(drive);
|
|
}
|
|
} catch (e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hsLocation.length > 0) {
|
|
if (!driveLetters.Hyperspace.find((driveLetter) => {
|
|
return driveLetter === hsLocation;
|
|
})) {
|
|
driveLetters.Hyperspace.push(hsLocation);
|
|
}
|
|
}
|
|
|
|
if (siaLocation.length > 0) {
|
|
if (!driveLetters.Sia.find((driveLetter) => {
|
|
return driveLetter === siaLocation;
|
|
})) {
|
|
driveLetters.Sia.push(siaLocation);
|
|
}
|
|
}
|
|
};
|
|
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
helpers
|
|
.detectRepertoryMounts(dataDirectory, data.Version)
|
|
.then((results) => {
|
|
let hsLocation = results.Hyperspace.Location;
|
|
let siaLocation = results.Sia.Location;
|
|
if (os.platform() === 'win32') {
|
|
hsLocation = hsLocation.toUpperCase();
|
|
siaLocation = siaLocation.toUpperCase();
|
|
grabDriveLetters(hsLocation, siaLocation);
|
|
}
|
|
event.sender.send(Constants.IPC_Detect_Mounts_Reply, {
|
|
data: {
|
|
DriveLetters: driveLetters,
|
|
Locations: {
|
|
Hyperspace: hsLocation,
|
|
Sia: siaLocation,
|
|
},
|
|
Success: true,
|
|
PIDS: {
|
|
Hyperspace: results.Hyperspace.PID,
|
|
Sia: results.Sia.PID,
|
|
}
|
|
}
|
|
});
|
|
})
|
|
.catch((err) => {
|
|
grabDriveLetters('', '');
|
|
|
|
event.sender.send(Constants.IPC_Detect_Mounts_Reply, {
|
|
data: {
|
|
DriveLetters: driveLetters,
|
|
Error: err,
|
|
Success: false,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Download_File, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
const destination = path.join(dataDirectory, data.Filename);
|
|
helpers.downloadFile(data.URL, destination, (progress) => {
|
|
event.sender.send(Constants.IPC_Download_File_Progress, {
|
|
data: {
|
|
Destination: destination,
|
|
Progress: progress,
|
|
URL: data.URL,
|
|
}
|
|
});
|
|
}, (success, err) => {
|
|
event.sender.send(Constants.IPC_Download_File_Complete, {
|
|
data: {
|
|
Destination: destination,
|
|
Error: err,
|
|
Success: success,
|
|
URL: data.URL,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Extract_Release, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
const destination = path.join(dataDirectory, data.Version);
|
|
helpers.mkDirByPathSync(destination);
|
|
|
|
const stream = fs.createReadStream(data.Source);
|
|
stream
|
|
.pipe(unzip.Extract({ path: destination }))
|
|
.on('error', (e) => {
|
|
try {
|
|
helpers.removeDirectoryRecursively(destination);
|
|
} catch (e) {
|
|
}
|
|
stream.close();
|
|
event.sender.send(Constants.IPC_Extract_Release_Complete, {
|
|
data: {
|
|
Error: e,
|
|
Source: data.Source,
|
|
Success: false,
|
|
}
|
|
});
|
|
})
|
|
.on('finish', () => {
|
|
stream.close();
|
|
event.sender.send(Constants.IPC_Extract_Release_Complete, {
|
|
data: {
|
|
Source: data.Source,
|
|
Success: true,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Get_Config, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
helpers
|
|
.getConfig(dataDirectory, data.Version, data.StorageType)
|
|
.then((data) => {
|
|
if (data.Code === 0) {
|
|
event.sender.send(Constants.IPC_Get_Config_Reply, {
|
|
data: {
|
|
Success: true,
|
|
Config: data.Data,
|
|
}
|
|
});
|
|
} else {
|
|
event.sender.send(Constants.IPC_Get_Config_Reply, {
|
|
data: {
|
|
Error: data.Code,
|
|
Success: false,
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.catch((e)=> {
|
|
event.sender.send(Constants.IPC_Get_Config_Reply, {
|
|
data: {
|
|
Error: e,
|
|
Success: false,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Get_Config_Template, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
helpers
|
|
.getConfigTemplate(dataDirectory, data.Version, data.StorageType)
|
|
.then((data) => {
|
|
event.sender.send(Constants.IPC_Get_Config_Template_Reply, {
|
|
data: {
|
|
Success: true,
|
|
Template: data,
|
|
}
|
|
});
|
|
})
|
|
.catch((e)=> {
|
|
event.sender.send(Constants.IPC_Get_Config_Template_Reply, {
|
|
data: {
|
|
Error: e,
|
|
Success: false,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Get_Platform, (event) => {
|
|
event.sender.send(Constants.IPC_Get_Platform_Reply, {
|
|
data: os.platform()
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Get_State, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data);
|
|
helpers.mkDirByPathSync(dataDirectory);
|
|
const configFile = path.join(dataDirectory, 'settings.json');
|
|
if (fs.existsSync(configFile)) {
|
|
event.sender.send(Constants.IPC_Get_State_Reply, {
|
|
data: JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
|
});
|
|
} else {
|
|
event.sender.send(Constants.IPC_Get_State_Reply, {
|
|
data: null,
|
|
});
|
|
}
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Grab_Releases, (event) => {
|
|
event.sender.send(Constants.IPC_Grab_Releases_Reply);
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Grab_UI_Releases, (event) => {
|
|
event.sender.send(Constants.IPC_Grab_UI_Releases_Reply);
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Install_Dependency, (event, data) => {
|
|
helpers
|
|
.executeAndWait(data.Source)
|
|
.then(()=> {
|
|
event.sender.send(Constants.IPC_Install_Dependency_Reply, {
|
|
data: {
|
|
Source: data.Source,
|
|
Success: true,
|
|
}
|
|
});
|
|
})
|
|
.catch((e)=> {
|
|
event.sender.send(Constants.IPC_Install_Dependency_Reply, {
|
|
data: {
|
|
Error: e,
|
|
Source: data.Source,
|
|
Success: false,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => {
|
|
helpers
|
|
.executeAsync(data.Source)
|
|
.then(()=> {
|
|
mainWindow.close();
|
|
})
|
|
.catch((e)=> {
|
|
event.sender.send(Constants.IPC_Install_Upgrade_Reply, {
|
|
data: {
|
|
Error: e,
|
|
Source: data.Source,
|
|
Success: false,
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
const errorHandler = (pid) => {
|
|
mountedPIDs.splice(mountedPIDs.indexOf(pid), 1);
|
|
event.sender.send(Constants.IPC_Unmount_Drive_Reply, {
|
|
data: {
|
|
PID: -1,
|
|
StorageType: data.StorageType,
|
|
Success: false,
|
|
}
|
|
});
|
|
};
|
|
helpers.executeMount(dataDirectory, data.Version, data.StorageType, data.Location, (_, pid)=> {
|
|
errorHandler(pid);
|
|
})
|
|
.then(pid=> {
|
|
if (pid !== -1) {
|
|
mountedPIDs.push(pid);
|
|
}
|
|
event.sender.send(Constants.IPC_Mount_Drive_Reply, {
|
|
data: {
|
|
PID: pid,
|
|
StorageType: data.StorageType,
|
|
Success: true,
|
|
}
|
|
});
|
|
})
|
|
.catch((_, pid) => {
|
|
errorHandler(pid);
|
|
});
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Save_State, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
helpers.mkDirByPathSync(dataDirectory);
|
|
const configFile = path.join(dataDirectory, 'settings.json');
|
|
fs.writeFileSync(configFile, JSON.stringify(data.State), 'utf8');
|
|
});
|
|
|
|
ipcMain.on(Constants.IPC_Set_Config_Values, (event, data) => {
|
|
const dataDirectory = helpers.resolvePath(data.Directory);
|
|
const setConfigValue = (i) => {
|
|
if (i < data.Items.length) {
|
|
helpers
|
|
.setConfigValue(data.Items[i].Name, data.Items[i].Value, dataDirectory, data.StorageType, data.Version)
|
|
.then(() => {
|
|
setConfigValue(++i);
|
|
})
|
|
.catch(() => {
|
|
setConfigValue(++i);
|
|
});
|
|
} else {
|
|
event.sender.send(Constants.IPC_Set_Config_Values_Reply, {});
|
|
}
|
|
};
|
|
setConfigValue(0);
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|