lbry-desktop/src/platforms/electron/index.js

332 lines
9.8 KiB
JavaScript
Raw Normal View History

// Module imports
2019-03-05 05:46:57 +01:00
import '@babel/polyfill';
2018-03-29 23:16:37 +02:00
import keytar from 'keytar';
2018-01-08 04:46:22 +01:00
import SemVer from 'semver';
2018-01-18 03:13:08 +01:00
import url from 'url';
2018-01-08 04:46:22 +01:00
import https from 'https';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
2019-02-18 18:33:02 +01:00
import { Lbry } from 'lbry-redux';
2018-01-18 03:13:08 +01:00
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
2018-02-24 00:20:12 +01:00
import createTray from './createTray';
2018-01-18 03:13:08 +01:00
import createWindow from './createWindow';
2019-03-05 05:46:57 +01:00
import pjson from '../../../package.json';
2019-03-15 02:54:17 +01:00
import startSandbox from './startSandbox';
import installDevtools from './installDevtools';
2017-12-13 22:36:30 +01:00
2017-12-22 07:42:04 +01:00
autoUpdater.autoDownload = true;
// This is set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;
// This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false;
2018-01-18 03:13:08 +01:00
// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;
2019-03-05 05:46:57 +01:00
2018-01-18 03:13:08 +01:00
let tray;
let daemon;
2018-02-24 00:20:12 +01:00
const appState = {};
2018-01-18 03:13:08 +01:00
app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.setAppUserModelId('io.lbry.LBRY');
2018-01-18 03:13:08 +01:00
2018-07-19 08:45:32 +02:00
if (isDev) {
// Enable WEBGL
app.commandLine.appendSwitch('ignore-gpu-blacklist');
app.commandLine.appendSwitch('--disable-gpu-process-crash-limit');
app.disableDomainBlockingFor3DAPIs();
// Disable security warnings in dev mode - https://github.com/electron/electron/blob/master/docs/tutorial/security.md#electron-security-warnings
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
2018-07-19 08:45:32 +02:00
}
const startDaemon = async() => {
2019-02-18 18:33:02 +01:00
let isDaemonRunning = false;
2019-02-18 18:33:02 +01:00
await Lbry.status()
.then(() => {
isDaemonRunning = true;
console.log('SDK already running');
})
.catch(() => {
console.log('Starting SDK');
});
2018-10-26 06:20:18 +02:00
2018-01-29 15:14:31 +01:00
if (!isDaemonRunning) {
daemon = new Daemon();
daemon.on('exit', () => {
2018-07-11 07:01:54 +02:00
if (!isDev) {
daemon = null;
if (!appState.isQuitting) {
dialog.showErrorBox(
'Daemon has Exited',
'The daemon may have encountered an unexpected error, or another daemon instance is already running. \n\n' +
'For more information please visit: \n' +
'https://lbry.io/faq/startup-troubleshooting'
);
}
app.quit();
2018-01-29 15:14:31 +01:00
}
});
daemon.launch();
}
};
// When we are starting the app, ensure there are no other apps already running
const gotSingleInstanceLock = app.requestSingleInstanceLock();
2019-03-19 04:33:46 +01:00
if (!gotSingleInstanceLock) {
// Another instance already has a lock, abort
app.quit();
} else {
app.on('second-instance', (event, argv) => {
// Send the url to the app to navigate first, then focus
if (rendererWindow) {
if (
(process.platform === 'win32' || process.platform === 'linux') &&
String(argv[1]).startsWith('lbry')
) {
let URI = argv[1];
// Keep only command line / deep linked arguments
// Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
// restore the original URI that was typed.
// - If the URI has no path, Windows adds a trailing slash. LBRY URIs can't have a slash with no
// path, so we just strip it off.
// - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
// an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
// - ? also interpreted as an anchor, remove slash also.
if (process.platform === 'win32') {
URI = URI.replace(/\/$/, '')
.replace('/#', '#')
.replace('/?', '?');
}
2019-02-22 06:01:59 +01:00
rendererWindow.webContents.send('open-uri-requested', URI);
}
2019-03-20 18:57:31 +01:00
rendererWindow.show();
}
});
2019-02-22 06:01:59 +01:00
app.on('ready', async() => {
startDaemon();
startSandbox();
if (isDev) {
await installDevtools();
2018-10-26 06:20:18 +02:00
} else {
rendererWindow.webContents.on('devtools-opened', () => {
// Send a message to the renderer process so we can log a security warning
rendererWindow.webContents.send('devtools-is-opened');
});
2018-10-26 06:20:18 +02:00
}
rendererWindow = createWindow(appState);
tray = createTray(rendererWindow);
// HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
// See https://github.com/electron/electron/issues/13008#issuecomment-400261941
session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
if (details.url.indexOf('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33') !== -1) {
callback({
redirectURL: details.url.replace(
'7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
'57c9d07b416b5a2ea23d28247300e4af36329bdc'
),
});
} else {
callback({ cancel: false });
}
});
});
}
2018-01-18 03:13:08 +01:00
app.on('activate', () => {
if (rendererWindow) {
rendererWindow.show();
}
});
2018-01-29 13:04:41 +01:00
app.on('will-quit', event => {
if (
process.platform === 'win32' &&
autoUpdateDownloaded &&
!appState.autoUpdateAccepted &&
!showingAutoUpdateCloseAlert
2018-01-29 13:04:41 +01:00
) {
// We're on Win and have an update downloaded, but the user declined it (or closed
// the app without accepting it). Now the user is closing the app, so the new update
// will install. On Mac this is silent, but on Windows they get a confusing permission
// escalation dialog, so we show Windows users a warning dialog first.
showingAutoUpdateCloseAlert = true;
2018-01-29 13:04:41 +01:00
dialog.showMessageBox(
{
type: 'info',
title: 'LBRY Will Upgrade',
message:
'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
},
() => {
app.quit();
}
);
2017-12-04 21:46:51 +01:00
event.preventDefault();
return;
2017-12-04 21:46:51 +01:00
}
2017-01-16 20:06:53 +01:00
2018-02-24 00:20:12 +01:00
appState.isQuitting = true;
if (daemon) {
daemon.quit();
event.preventDefault();
}
if (rendererWindow) {
rendererWindow = null;
}
2017-12-13 22:36:30 +01:00
});
2017-02-23 19:46:25 +01:00
2018-01-18 03:13:08 +01:00
// https://electronjs.org/docs/api/app#event-will-finish-launching
app.on('will-finish-launching', () => {
// Protocol handler for macOS
app.on('open-url', (event, URL) => {
2017-03-24 00:07:08 +01:00
event.preventDefault();
if (rendererWindow) {
rendererWindow.webContents.send('open-uri-requested', URL);
rendererWindow.show();
} else {
appState.macDeepLinkingURI = URL;
}
2017-12-13 22:36:30 +01:00
});
2017-03-24 00:07:08 +01:00
});
2017-01-18 17:32:01 +01:00
2018-02-24 00:20:12 +01:00
app.on('before-quit', () => {
appState.isQuitting = true;
2017-03-24 00:07:08 +01:00
});
ipcMain.on('upgrade', (event, installerPath) => {
app.on('quit', () => {
console.log('Launching upgrade installer at', installerPath);
2017-03-24 00:07:08 +01:00
// This gets triggered called after *all* other quit-related events, so
// we'll only get here if we're fully prepared and quitting for real.
2018-01-18 03:13:08 +01:00
shell.openItem(installerPath);
});
2017-03-17 23:05:25 +01:00
// what to do if no shutdown in a long time?
console.log('Update downloaded to', installerPath);
2017-12-13 22:36:30 +01:00
console.log(
'The app will close, and you will be prompted to install the latest version of LBRY.'
2017-12-13 22:36:30 +01:00
);
console.log('After the install is complete, please reopen the app.');
2018-01-18 03:13:08 +01:00
app.quit();
2017-12-04 21:46:51 +01:00
});
autoUpdater.on('update-downloaded', () => {
autoUpdateDownloaded = true;
2018-01-29 13:04:41 +01:00
});
ipcMain.on('autoUpdateAccepted', () => {
appState.autoUpdateAccepted = true;
autoUpdater.quitAndInstall();
2017-12-04 21:46:51 +01:00
});
ipcMain.on('version-info-requested', () => {
2017-12-04 21:46:51 +01:00
function formatRc(ver) {
2018-01-18 03:13:08 +01:00
// Adds dash if needed to make RC suffix SemVer friendly
return ver.replace(/([^-])rc/, '$1-rc');
2017-12-04 21:46:51 +01:00
}
const localVersion = pjson.version;
2018-01-18 03:13:08 +01:00
let result = '';
const onSuccess = res => {
res.on('data', data => {
result += data;
});
res.on('end', () => {
2019-03-19 04:33:46 +01:00
let json;
try {
json = JSON.parse(result);
2019-03-19 02:44:49 +01:00
} catch (e) {
2019-03-19 04:33:46 +01:00
return;
}
const tagName = json.tag_name;
2019-02-18 18:33:02 +01:00
if (tagName) {
const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
if (!remoteVersion) {
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', localVersion);
}
} else {
const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', {
remoteVersion,
localVersion,
upgradeAvailable,
});
}
}
2019-02-18 18:33:02 +01:00
} else if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', localVersion);
}
});
};
2019-03-19 04:33:46 +01:00
const requestLatestRelease = (alreadyRedirected = false) => {
2019-03-19 02:44:49 +01:00
const req = https.get(
{
hostname: 'api.github.com',
path: '/repos/lbryio/lbry-desktop/releases/latest',
headers: { 'user-agent': `LBRY/${localVersion}` },
},
res => {
if (res.statusCode === 301 || res.statusCode === 302) {
requestLatestRelease(res.headers.location, true);
} else {
onSuccess(res);
}
}
2019-03-19 02:44:49 +01:00
);
if (alreadyRedirected) return;
req.on('error', err => {
console.log('Failed to get current version from GitHub. Error:', err);
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', null);
}
});
};
2019-03-19 04:33:46 +01:00
requestLatestRelease();
2017-12-04 21:46:51 +01:00
});
ipcMain.on('get-auth-token', event => {
2018-01-08 04:46:22 +01:00
keytar.getPassword('LBRY', 'auth_token').then(token => {
event.sender.send('auth-token-response', token ? token.toString().trim() : null);
});
});
ipcMain.on('set-auth-token', (event, token) => {
2018-01-08 04:46:22 +01:00
keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});
process.on('uncaughtException', error => {
2019-03-19 04:33:46 +01:00
console.log(error);
2018-01-18 03:13:08 +01:00
dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
2018-02-24 00:20:12 +01:00
appState.isQuitting = true;
2018-01-18 03:13:08 +01:00
if (daemon) daemon.quit();
app.exit(1);
});