Merge remote-tracking branch 'origin/master' into auto-update

This commit is contained in:
Alex Liebowitz 2018-01-23 02:44:15 -05:00
commit 483809be1b
31 changed files with 631 additions and 800 deletions

25
.gitignore vendored
View file

@ -1,30 +1,9 @@
/node_modules /node_modules
/LBRY-darwin-x64
/dist /dist
/src/main/dist
/src/main/locales
/src/main/node_modules
/src/renderer/dist
/build/venv
/build/daemon.ver /build/daemon.ver
/lbry-app-venv /build/venv
/lbry-app *.pyc
/lbry-venv
/static/daemon/lbrynet* /static/daemon/lbrynet*
/static/locales /static/locales
/daemon/build
/daemon/venv
/daemon/requirements.txt
/.idea
*.pyc
*.iml
.#*
build/daemon.zip
.vimrc
package-lock.json
.DS_Store

View file

@ -38,11 +38,11 @@ To make contributing as easy and rewarding of possible, we have instituted the f
| Level | Description | | Level | Description |
| --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [**level 0**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+0%22+no%3Aassignee) | Typos and text edits -- a tech-savvy non-programmer can fix these | | [**level 0**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+0%22+no%3Aassignee) | Typos and text edits -- a tech-savvy non-programmer can fix these |
| [**level 1**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+1%22+no%3Aassignee) | Programming issues that require little knowledge of how the LBRY app works | | [**level 1**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+1%22+no%3Aassignee) | Programming issues that require little knowledge of how the LBRY app works |
| [**level 2**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+2%22+no%3Aassignee) | Issues of average difficulty that require the developer to dig into how the app works a little bit | | [**level 2**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+2%22+no%3Aassignee) | Issues of average difficulty that require the developer to dig into how the app works a little bit |
| [**level 3**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+3%22+no%3Aassignee) | Issues that are likely too tricky to be level 2 or require more thinking outside of the box | | [**level 3**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+3%22+no%3Aassignee) | Issues that are likely too tricky to be level 2 or require more thinking outside of the box |
| [**level 4**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+4%22+no%3Aassignee) | Big features or really hard issues | | [**level 4**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+4%22+no%3Aassignee) | Big features or really hard issues |
The process of ranking issues is highly subjective. The purpose of sorting issues like this is to The process of ranking issues is highly subjective. The purpose of sorting issues like this is to
give contributors a general idea about the type of issues they are looking at. It could very well be give contributors a general idea about the type of issues they are looking at. It could very well be

View file

@ -31,7 +31,6 @@ development and testing purposes.
* [Git](https://git-scm.com/downloads) * [Git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/) * [Node.js](https://nodejs.org/en/download/)
* [Yarn](https://yarnpkg.com/en/docs/install) * [Yarn](https://yarnpkg.com/en/docs/install)
* `yarn --add-python-to-path install --global --production windows-build-tools` (Windows only)
### One-time Setup ### One-time Setup
@ -61,40 +60,10 @@ The app can be run from the sources using the following command:
### On Windows ### On Windows
#### Windows Dependency
1. Download and install `git` from <a href="https://git-for-windows.github.io/">github.io<a>
(configure to use command prompt integration)
2. Download and install `npm` and `node` from
<a href="https://nodejs.org/en/download/current/">nodejs.org<a>
3. Download and install `python 2.7` from
<a href="https://www.python.org/downloads/windows/">python.org</a>
4. Download and Install `Microsoft Visual C++ Compiler for Python 2.7` from
<a href="https://www.microsoft.com/en-us/download/confirmation.aspx?id=44266">Microsoft<a>
5. Download and install `.NET Framework 2.0 Software Development Kit (SDK) (x64)` from
<a href="https://www.microsoft.com/en-gb/download/details.aspx?id=15354">Microsoft<a> (may need
to extract setup.exe and install manually by running install.exe as Administrator)
#### One-time Setup #### One-time Setup
1. Open a command prompt as administrator and run the following: Download the lbry daemon and CLI [binaries](https://github.com/lbryio/lbry/releases) and place them
in `static\daemon`.
```
npm install --global --production windows-build-tools
exit
```
2. Open a command prompt in the root of the project and run the following:
```
python -m pip install -r build\requirements.txt
npm install -g yarn
yarn install
yarn build
```
3. Download the lbry daemon and CLI [binaries](https://github.com/lbryio/lbry/releases) and place
them in `static\daemon`.
### Build ### Build

View file

@ -73,9 +73,9 @@ fi
# Build the app # # Build the app #
################### ###################
if [ "$FULL_BUILD" == "true" ]; then if [ "$FULL_BUILD" == "true" ]; then
# if $OSX; then if $OSX; then
# security unlock-keychain -p ${KEYCHAIN_PASSWORD} osx-build.keychain security unlock-keychain -p ${KEYCHAIN_PASSWORD} osx-build.keychain
# fi fi
yarn build yarn build

View file

@ -40,8 +40,12 @@ set -eu
if $LINUX; then if $LINUX; then
INSTALL="$SUDO apt-get install --no-install-recommends -y" INSTALL="$SUDO apt-get install --no-install-recommends -y"
$INSTALL build-essential libssl-dev libffi-dev libgmp3-dev python2.7-dev libsecret-1-dev curl $INSTALL build-essential libssl-dev libffi-dev libgmp3-dev python2.7-dev libsecret-1-dev curl
elif $OSX && ! cmd_exists brew ; then elif $OSX; then
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" if ! cmd_exists brew; then
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
else
brew update
fi
fi fi

View file

@ -21,6 +21,9 @@
"compile": "electron-webpack && yarn extract-langs", "compile": "electron-webpack && yarn extract-langs",
"build": "yarn compile && electron-builder build", "build": "yarn compile && electron-builder build",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"postmerge": "yarnhook",
"postcheckout": "yarnhook",
"postrewrite": "yarnhook",
"precommit": "lint-staged", "precommit": "lint-staged",
"lint": "eslint 'src/**/*.{js,jsx}' --fix", "lint": "eslint 'src/**/*.{js,jsx}' --fix",
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write" "format": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
@ -41,7 +44,7 @@
"install": "^0.10.2", "install": "^0.10.2",
"jayson": "^2.0.2", "jayson": "^2.0.2",
"jshashes": "^1.0.7", "jshashes": "^1.0.7",
"keytar": "^4.0.3", "keytar-prebuild": "^4.0.4",
"localforage": "^1.5.0", "localforage": "^1.5.0",
"npm": "^5.5.1", "npm": "^5.5.1",
"qrcode.react": "^0.7.2", "qrcode.react": "^0.7.2",
@ -102,7 +105,8 @@
"prettier": "^1.4.2", "prettier": "^1.4.2",
"sass-loader": "^6.0.6", "sass-loader": "^6.0.6",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-build-notifier": "^0.1.18" "webpack-build-notifier": "^0.1.18",
"yarnhook": "^0.1.1"
}, },
"resolutions": { "resolutions": {
"webpack/webpack-sources": "1.0.1" "webpack/webpack-sources": "1.0.1"

64
src/main/Daemon.js Normal file
View file

@ -0,0 +1,64 @@
/* eslint-disable no-console */
import path from 'path';
import { spawn, execSync } from 'child_process';
export default class Daemon {
static path = process.env.LBRY_DAEMON || path.join(__static, 'daemon/lbrynet-daemon');
subprocess;
handlers;
constructor() {
this.handlers = [];
}
launch() {
// Kill any running daemon
if (process.platform === 'win32') {
try {
execSync('taskkill /im lbrynet-daemon.exe /t /f');
} catch (error) {
console.warn(error.message);
}
} else {
try {
execSync('pkill lbrynet-daemon');
} catch (error) {
console.warn(error.message);
}
}
console.log('Launching daemon:', Daemon.path);
this.subprocess = spawn(Daemon.path);
this.subprocess.stdout.on('data', data => console.log(`Daemon: ${data}`));
this.subprocess.stderr.on('data', data => console.error(`Daemon: ${data}`));
this.subprocess.on('exit', () => this.fire('exit'));
this.subprocess.on('error', error => console.error(`Daemon: ${error}`));
}
quit() {
if (process.platform === 'win32') {
try {
execSync(`taskkill /pid ${this.subprocess.pid} /t /f`);
} catch (error) {
console.error(error.message);
}
} else {
this.subprocess.kill();
}
}
// Follows the publish/subscribe pattern
// Subscribe method
on(event, handler, context = handler) {
this.handlers.push({ event, handler: handler.bind(context) });
}
// Publish method
fire(event, args) {
this.handlers.forEach(topic => {
if (topic.event === event) topic.handler(args);
});
}
}

63
src/main/Tray.js Normal file
View file

@ -0,0 +1,63 @@
import { app, Menu, Tray as ElectronTray } from 'electron';
import path from 'path';
import createWindow from './createWindow';
export default class Tray {
window;
updateAttachedWindow;
tray;
constructor(window, updateAttachedWindow) {
this.window = window;
this.updateAttachedWindow = updateAttachedWindow;
}
create() {
let iconPath;
switch (process.platform) {
case 'darwin': {
iconPath = path.join(__static, '/img/tray/mac/trayTemplate.png');
break;
}
case 'win32': {
iconPath = path.join(__static, '/img/tray/windows/tray.ico');
break;
}
default: {
iconPath = path.join(__static, '/img/tray/default/tray.png');
}
}
this.tray = new ElectronTray(iconPath);
this.tray.on('double-click', () => {
if (!this.window || this.window.isDestroyed()) {
this.window = createWindow();
this.updateAttachedWindow(this.window);
} else {
this.window.show();
this.window.focus();
}
});
this.tray.setToolTip('LBRY App');
const template = [
{
label: `Open ${app.getName()}`,
click: () => {
if (!this.window || this.window.isDestroyed()) {
this.window = createWindow();
this.updateAttachedWindow(this.window);
} else {
this.window.show();
this.window.focus();
}
},
},
{ role: 'quit' },
];
const contextMenu = Menu.buildFromTemplate(template);
this.tray.setContextMenu(contextMenu);
}
}

100
src/main/createWindow.js Normal file
View file

@ -0,0 +1,100 @@
import { app, BrowserWindow, dialog } from 'electron';
import setupBarMenu from './menu/setupBarMenu';
import setupContextMenu from './menu/setupContextMenu';
export default deepLinkingURIArg => {
let windowConfiguration = {
backgroundColor: '#155B4A',
minWidth: 800,
minHeight: 600,
autoHideMenuBar: true,
show: false,
};
// Disable renderer process's webSecurity on development to enable CORS.
windowConfiguration =
process.env.NODE_ENV === 'development'
? {
...windowConfiguration,
webPreferences: {
webSecurity: false,
},
}
: windowConfiguration;
const rendererURL =
process.env.NODE_ENV === 'development'
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`;
let window = new BrowserWindow(windowConfiguration);
window.maximize();
window.loadURL(rendererURL);
let deepLinkingURI;
// Protocol handler for win32
if (
!deepLinkingURIArg &&
process.platform === 'win32' &&
String(process.argv[1]).startsWith('lbry')
) {
// 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.
deepLinkingURI = process.argv[1].replace(/\/$/, '').replace('/#', '#');
} else {
deepLinkingURI = deepLinkingURIArg;
}
setupBarMenu();
setupContextMenu(window);
window.on('closed', () => {
window = null;
});
window.on('focus', () => {
window.webContents.send('window-is-focused', null);
});
window.on('unresponsive', () => {
dialog.showMessageBox(
window,
{
type: 'warning',
buttons: ['Wait', 'Quit'],
title: 'LBRY Unresponsive',
defaultId: 1,
message: 'LBRY is not responding. Would you like to quit?',
cancelId: 0,
},
buttonIndex => {
if (buttonIndex === 1) app.quit();
}
);
});
window.once('ready-to-show', () => {
window.show();
});
window.webContents.on('did-finish-load', () => {
window.webContents.send('open-uri-requested', deepLinkingURI, true);
window.webContents.session.setUserAgent(`LBRY/${app.getVersion()}`);
if (process.env.NODE_ENV === 'development') {
window.webContents.openDevTools();
}
});
window.webContents.on('crashed', () => {
window = null;
});
return window;
};

View file

@ -1,54 +1,20 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
// Module imports // Module imports
import Path from 'path'; import keytar from 'keytar-prebuild';
import Url from 'url'; import SemVer from 'semver';
import Jayson from 'jayson'; import url from 'url';
import Semver from 'semver'; import https from 'https';
import Https from 'https'; import { shell, app, ipcMain, dialog } from 'electron';
import Keytar from 'keytar';
import ChildProcess from 'child_process';
import Assert from 'assert';
import { app, dialog, BrowserWindow, globalShortcut, ipcMain, Menu, Tray } from 'electron';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import log from 'electron-log'; import log from 'electron-log';
import mainMenu from './menu/mainMenu'; import Daemon from './Daemon';
import contextMenu from './menu/contextMenu'; import Tray from './Tray';
import createWindow from './createWindow';
const localVersion = app.getVersion(); // For now, log info messages in production for easier debugging
log.transports.file.level = '';
// Debug configs
const isDevelopment = process.env.NODE_ENV === 'development';
// For now, log info messages in production for easier debugging of built apps
log.transports.file.level = 'info';
autoUpdater.autoDownload = true; autoUpdater.autoDownload = true;
// Misc constants
const LATEST_RELEASE_API_URL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
const DAEMON_PATH = process.env.LBRY_DAEMON || Path.join(__static, 'daemon/lbrynet-daemon');
const rendererUrl = isDevelopment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`;
const client = Jayson.client.http({
host: 'localhost',
port: 5279,
path: '/',
timeout: 1000,
});
// 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 rendererWindow;
// Also keep the daemon subprocess alive
let daemonSubprocess;
// This is set to true right before we try to shut the daemon subprocess --
// if it dies when we didn't ask it to shut down, we want to alert the user.
let daemonStopRequested = false;
// This is set to true if an auto update has been downloaded through the Electron // 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, // auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown. // it will still install on shutdown.
@ -61,470 +27,149 @@ let autoUpdateAccepted = false;
// that we show on Windows after you decline an upgrade and close the app later. // that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false; let showingAutoUpdateCloseAlert = false;
// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;
// eslint-disable-next-line no-unused-vars
let tray;
let daemon;
// When a quit is attempted, we cancel the quit, do some preparations, then let isQuitting;
// this is set to true and app.quit() is called again to quit for real.
let readyToQuit = false;
// If we receive a URI to open from an external app but there's no window to const updateRendererWindow = window => {
// sendCredits it to, it's cached in this variable. rendererWindow = window;
let openUri = null; };
// Set this to true to minimize on clicking close const installExtensions = async () => {
// false for normal action // eslint-disable-next-line import/no-extraneous-dependencies,global-require
let minimize = true; const installer = require('electron-devtools-installer');
// eslint-disable-next-line import/no-extraneous-dependencies,global-require
const devtronExtension = require('devtron');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
// Keep the tray also, it is getting GC'd if put in createTray() return Promise.all(
let tray = null; extensions.map(
name => installer.default(installer[name], forceDownload),
function processRequestedUri(uri) { devtronExtension.install()
// Windows normalizes URIs when they're passed in from other apps. On Windows, )
// this function tries to restore the original URI that was typed. ).catch(console.log);
// - 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.
// On Linux and Mac, we just return the URI as given.
if (process.platform === 'win32') {
return uri.replace(/\/$/, '').replace('/#', '#');
}
return uri;
}
/*
* Replacement for Electron's shell.openItem. The Electron version doesn't
* reliably work from the main process, and we need to be able to run it
* when no windows are open.
*/
function openItem(fullPath) {
const subprocOptions = {
detached: true,
stdio: 'ignore',
};
let child;
if (process.platform === 'darwin') {
child = ChildProcess.spawn('open', [fullPath], subprocOptions);
} else if (process.platform === 'linux') {
child = ChildProcess.spawn('xdg-open', [fullPath], subprocOptions);
} else if (process.platform === 'win32') {
child = ChildProcess.spawn(fullPath, Object.assign({}, subprocOptions, { shell: true }));
}
// Causes child process reference to be garbage collected, allowing main process to exit
child.unref();
}
/*
* Quits by first killing the daemon, the calling quitting for real.
*/
export function safeQuit() {
minimize = false;
app.quit();
}
function getMenuTemplate() {
function getToggleItem() {
if (rendererWindow.isVisible() && rendererWindow.isFocused()) {
return {
label: 'Hide LBRY App',
click: () => rendererWindow.hide(),
};
}
return {
label: 'Show LBRY App',
click: () => rendererWindow.show(),
};
}
return [
getToggleItem(),
{
label: 'Quit',
click: () => safeQuit(),
},
];
}
// This needs to be done as for linux the context menu doesn't update automatically(docs)
function updateTray() {
const trayContextMenu = Menu.buildFromTemplate(getMenuTemplate());
if (tray) {
tray.setContextMenu(trayContextMenu);
} else {
console.log('How did update tray get called without a tray?');
}
}
function createWindow() {
// Disable renderer process's webSecurity on development to enable CORS.
let windowConfiguration = {
backgroundColor: '#155B4A',
minWidth: 800,
minHeight: 600,
autoHideMenuBar: true,
};
windowConfiguration = isDevelopment
? {
...windowConfiguration,
webPreferences: {
webSecurity: false,
},
}
: windowConfiguration;
let window = new BrowserWindow(windowConfiguration);
window.webContents.session.setUserAgent(`LBRY/${localVersion}`);
window.maximize();
if (isDevelopment) {
window.webContents.openDevTools();
}
window.loadURL(rendererUrl);
if (openUri) {
// We stored and received a URI that an external app requested before we had a window object
window.webContents.on('did-finish-load', () => {
window.webContents.send('open-uri-requested', openUri, true);
});
}
window.removeAllListeners();
window.on('close', event => {
if (minimize) {
event.preventDefault();
window.hide();
}
});
window.on('closed', () => {
window = null;
});
window.on('hide', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
window.on('show', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
});
window.on('blur', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Unregisters Alt+F4 shortcut
globalShortcut.unregister('Alt+F4');
});
window.on('focus', () => {
// Checks what to show in the tray icon menu
if (minimize) updateTray();
// Registers shortcut for closing(quitting) the app
globalShortcut.register('Alt+F4', () => safeQuit());
window.webContents.send('window-is-focused', null);
});
mainMenu();
return window;
}
function createTray() {
// Minimize to tray logic follows:
// Set the tray icon
let iconPath;
if (process.platform === 'darwin') {
// Using @2x for mac retina screens so the icon isn't blurry
// file name needs to include "Template" at the end for dark menu bar
iconPath = Path.join(__static, '/img/fav/macTemplate@2x.png');
} else {
iconPath = Path.join(__static, '/img/fav/32x32.png');
}
tray = new Tray(iconPath);
tray.setToolTip('LBRY App');
tray.setTitle('LBRY');
tray.on('double-click', () => {
rendererWindow.show();
});
}
function handleOpenUriRequested(uri) {
if (!rendererWindow) {
// Window not created yet, so store up requested URI for when it is
openUri = processRequestedUri(uri);
} else {
if (rendererWindow.isMinimized()) {
rendererWindow.restore();
} else if (!rendererWindow.isVisible()) {
rendererWindow.show();
}
rendererWindow.focus();
rendererWindow.webContents.send('open-uri-requested', processRequestedUri(uri));
}
}
/*
* Quits without any preparation. When a quit is requested (either through the
* interface or through app.quit()), we abort the quit, try to shut down the daemon,
* and then call this to quit for real.
*/
function quitNow() {
readyToQuit = true;
safeQuit();
}
function handleDaemonSubprocessExited() {
console.log('The daemon has exited.');
daemonSubprocess = null;
if (!daemonStopRequested) {
// We didn't request to stop the daemon, so display a
// warning and schedule a quit.
//
// TODO: maybe it would be better to restart the daemon?
if (rendererWindow) {
console.log('Did not request daemon stop, so quitting in 5 seconds.');
rendererWindow.loadURL(`file://${__static}/warning.html`);
setTimeout(quitNow, 5000);
} else {
console.log('Did not request daemon stop, so quitting.');
quitNow();
}
}
}
function launchDaemon() {
Assert(!daemonSubprocess, 'Tried to launch daemon twice');
console.log('Launching daemon:', DAEMON_PATH);
daemonSubprocess = ChildProcess.spawn(DAEMON_PATH);
// Need to handle the data event instead of attaching to
// process.stdout because the latter doesn't work. I believe on
// windows it buffers stdout and we don't get any meaningful output
daemonSubprocess.stdout.on('data', buf => {
console.log(String(buf).trim());
});
daemonSubprocess.stderr.on('data', buf => {
console.log(String(buf).trim());
});
daemonSubprocess.on('exit', handleDaemonSubprocessExited);
}
const isSecondaryInstance = app.makeSingleInstance(argv => {
if (argv.length >= 2) {
handleOpenUriRequested(argv[1]); // This will handle restoring and focusing the window
} else if (rendererWindow) {
if (rendererWindow.isMinimized()) {
rendererWindow.restore();
} else if (!rendererWindow.isVisible()) {
rendererWindow.show();
}
rendererWindow.focus();
}
});
if (isSecondaryInstance) {
// We're not in the original process, so quit
quitNow();
}
function launchDaemonIfNotRunning() {
// Check if the daemon is already running. If we get
// an error its because its not running
console.log('Checking for lbrynet daemon');
client.request('status', [], err => {
if (err) {
console.log('lbrynet daemon needs to be launched');
launchDaemon();
} else {
console.log('lbrynet daemon is already running');
}
});
}
// Taken from webtorrent-desktop
function checkLinuxTraySupport(cb) {
// Check that we're on Ubuntu (or another debian system) and that we have
// libappindicator1.
ChildProcess.exec('dpkg --get-selections libappindicator1', (err, stdout) => {
if (err) return cb(err);
// Unfortunately there's no cleaner way, as far as I can tell, to check
// whether a debian package is installed:
if (stdout.endsWith('\tinstall\n')) {
return cb(null);
}
return cb(new Error('debian package not installed'));
});
}
// When a quit is attempted, this is called. It attempts to shutdown the daemon,
// then calls quitNow() to quit for real.
function shutdownDaemonAndQuit(evenIfNotStartedByApp = false) {
function doShutdown() {
console.log('Shutting down daemon');
daemonStopRequested = true;
client.request('daemon_stop', [], err => {
if (err) {
console.log(`received error when stopping lbrynet-daemon. Error message: ${err.message}\n`);
console.log('You will need to manually kill the daemon.');
} else {
console.log('Successfully stopped daemon via RPC call.');
quitNow();
}
});
}
if (daemonSubprocess) {
doShutdown();
} else if (!evenIfNotStartedByApp) {
console.log('Not killing lbrynet-daemon because app did not start it');
quitNow();
} else {
doShutdown();
}
// Is it safe to start the installer before the daemon finishes running?
// If not, we should wait until the daemon is closed before we start the install.
}
if (isDevelopment) {
import('devtron')
.then(({ install }) => {
install();
console.log('Added Extension: Devtron');
})
.catch(error => {
console.error(error);
});
import('electron-devtools-installer')
.then(({ default: installExtension, REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS }) => {
app.on('ready', () => {
[REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS].forEach(extension => {
installExtension(extension)
.then(name => console.log(`Added Extension: ${name}`))
.catch(err => console.log('An error occurred: ', err));
});
});
})
.catch(error => {
console.error(error);
});
}
app.setAsDefaultProtocolClient('lbry'); app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.on('ready', () => { app.on('ready', async () => {
launchDaemonIfNotRunning(); daemon = new Daemon();
if (process.platform === 'linux') { daemon.on('exit', () => {
checkLinuxTraySupport(err => { daemon = null;
if (!err) createTray(); if (!isQuitting) {
else minimize = false; dialog.showErrorBox(
}); 'Daemon has Exited',
} else { 'The daemon may have encountered an unexpected error, or another daemon instance is already running.'
createTray(); );
app.quit();
}
});
daemon.launch();
if (process.env.NODE_ENV === 'development') {
await installExtensions();
} }
rendererWindow = createWindow(); rendererWindow = createWindow();
}); tray = new Tray(rendererWindow, updateRendererWindow);
tray.create();
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS 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('before-quit', event => {
if (!readyToQuit) {
// We need to shutdown the daemon before we're ready to actually quit. This
// event will be triggered re-entrantly once preparation is done.
event.preventDefault();
shutdownDaemonAndQuit();
} else if (autoUpdateDownloaded) {
if (autoUpdateAccepted) {
// User accepted the update, so install the update and restart.
autoUpdater.quitAndInstall();
} else if (process.platform == 'win32' && !showingAutoUpdateCloseAlert) {
// We 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.
event.preventDefault();
showingAutoUpdateCloseAlert = true;
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.',
}, () => {
// After the user approves the dialog, we can quit once and for all.
quitNow();
});
}
}
}); });
app.on('activate', () => { app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.
if (rendererWindow === null) { if (!rendererWindow) rendererWindow = createWindow();
createWindow();
}
}); });
if (process.platform === 'darwin') { app.on('will-quit', (e) => {
app.on('open-url', (event, uri) => { if (process.platform === 'win32' && autoUpdateDownloaded && !autoUpdateAccepted && !showingAutoUpdateCloseAlert) {
handleOpenUriRequested(uri); // 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;
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();
});
e.preventDefault();
return;
}
isQuitting = true;
if (daemon) daemon.quit();
});
// 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) => {
event.preventDefault();
if (rendererWindow && !rendererWindow.isDestroyed()) {
rendererWindow.webContents.send('open-uri-requested', URL);
rendererWindow.show();
rendererWindow.focus();
} else {
rendererWindow = createWindow(URL);
}
}); });
} else if (process.argv.length >= 2) { });
handleOpenUriRequested(process.argv[1]);
} app.on('window-all-closed', () => {
// Subscribe to event so the app doesn't quit when closing the window.
});
ipcMain.on('upgrade', (event, installerPath) => { ipcMain.on('upgrade', (event, installerPath) => {
app.on('quit', () => { app.on('quit', () => {
console.log('Launching upgrade installer at', installerPath); console.log('Launching upgrade installer at', installerPath);
// This gets triggered called after *all* other quit-related events, so // 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. // we'll only get here if we're fully prepared and quitting for real.
openItem(installerPath); shell.openItem(installerPath);
}); });
if (rendererWindow) {
rendererWindow.loadURL(`file://${__static}/upgrade.html`);
}
shutdownDaemonAndQuit(true);
// wait for daemon to shut down before upgrading
// what to do if no shutdown in a long time? // what to do if no shutdown in a long time?
console.log('Update downloaded to', installerPath); console.log('Update downloaded to', installerPath);
console.log( console.log(
'The app will close, and you will be prompted to install the latest version of LBRY.' 'The app will close, and you will be prompted to install the latest version of LBRY.'
); );
console.log('After the install is complete, please reopen the app.'); console.log('After the install is complete, please reopen the app.');
app.quit();
});
autoUpdater.on('update-downloaded', () => {
autoUpdateDownloaded = true;
})
ipcMain.on('autoUpdateAccepted', () => {
autoUpdateAccepted = true;
autoUpdater.quitAndInstall();
}); });
ipcMain.on('version-info-requested', () => { ipcMain.on('version-info-requested', () => {
function formatRc(ver) { function formatRc(ver) {
// Adds dash if needed to make RC suffix semver friendly // Adds dash if needed to make RC suffix SemVer friendly
return ver.replace(/([^-])rc/, '$1-rc'); return ver.replace(/([^-])rc/, '$1-rc');
} }
let result = ''; const localVersion = app.getVersion();
const latestReleaseAPIURL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
const opts = { const opts = {
headers: { headers: {
'User-Agent': `LBRY/${localVersion}`, 'User-Agent': `LBRY/${localVersion}`,
}, },
}; };
let result = '';
const req = Https.get(Object.assign(opts, Url.parse(LATEST_RELEASE_API_URL)), res => { const req = https.get(Object.assign(opts, url.parse(latestReleaseAPIURL)), res => {
res.on('data', data => { res.on('data', data => {
result += data; result += data;
}); });
@ -536,7 +181,7 @@ ipcMain.on('version-info-requested', () => {
rendererWindow.webContents.send('version-info-received', null); rendererWindow.webContents.send('version-info-received', null);
} }
} else { } else {
const upgradeAvailable = Semver.gt(formatRc(remoteVersion), formatRc(localVersion)); const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
if (rendererWindow) { if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', { rendererWindow.webContents.send('version-info-received', {
remoteVersion, remoteVersion,
@ -556,26 +201,44 @@ ipcMain.on('version-info-requested', () => {
}); });
}); });
autoUpdater.on('update-downloaded', () => {
autoUpdateDownloaded = true;
});
ipcMain.on('autoUpdateAccepted', () => {
autoUpdateAccepted = true;
minimize = false;
app.quit();
});
ipcMain.on('get-auth-token', event => { ipcMain.on('get-auth-token', event => {
Keytar.getPassword('LBRY', 'auth_token').then(token => { keytar.getPassword('LBRY', 'auth_token').then(token => {
event.sender.send('auth-token-response', token ? token.toString().trim() : null); event.sender.send('auth-token-response', token ? token.toString().trim() : null);
}); });
}); });
ipcMain.on('set-auth-token', (event, token) => { ipcMain.on('set-auth-token', (event, token) => {
Keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null); keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
}); });
export { contextMenu }; process.on('uncaughtException', error => {
dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
isQuitting = true;
if (daemon) daemon.quit();
app.exit(1);
});
// Force single instance application
const isSecondInstance = app.makeSingleInstance(argv => {
// Protocol handler for win32
// argv: An array of the second instances (command line / deep linked) arguments
let URI;
if (process.platform === 'win32' && String(argv[1]).startsWith('lbry')) {
// Keep only command line / deep linked arguments
URI = argv[1].replace(/\/$/, '').replace('/#', '#');
}
if (rendererWindow && !rendererWindow.isDestroyed()) {
rendererWindow.webContents.send('open-uri-requested', URI);
rendererWindow.show();
rendererWindow.focus();
} else {
rendererWindow = createWindow(URI);
}
});
if (isSecondInstance) {
app.exit();
}

View file

@ -1,20 +0,0 @@
import { Menu } from 'electron';
const contextMenuTemplate = [{ role: 'cut' }, { role: 'copy' }, { role: 'paste' }];
export default (win, posX, posY, showDevItems) => {
const template = contextMenuTemplate.slice();
if (showDevItems) {
template.push({
type: 'separator',
});
template.push({
label: 'Inspect Element',
click() {
win.inspectElement(posX, posY);
},
});
}
Menu.buildFromTemplate(template).popup(win);
};

View file

@ -1,138 +0,0 @@
import { app, Menu, shell } from 'electron';
import { safeQuit } from '../index';
const baseTemplate = [
{
label: 'File',
submenu: [
{
label: 'Quit',
accelerator: 'CommandOrControl+Q',
click: () => safeQuit(),
},
],
},
{
label: 'Edit',
submenu: [
{
role: 'undo',
},
{
role: 'redo',
},
{
type: 'separator',
},
{
role: 'cut',
},
{
role: 'copy',
},
{
role: 'paste',
},
{
role: 'selectall',
},
],
},
{
label: 'View',
submenu: [
{
role: 'reload',
},
{
label: 'Developer',
submenu: [
{
role: 'forcereload',
},
{
role: 'toggledevtools',
},
],
},
{
type: 'separator',
},
{
role: 'togglefullscreen',
},
],
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send('open-menu', '/help');
}
},
},
{
label: 'Frequently Asked Questions',
click() {
shell.openExternal('https://lbry.io/faq');
},
},
{
type: 'separator',
},
{
label: 'Report Issue',
click() {
shell.openExternal('https://lbry.io/faq/contributing#report-a-bug');
},
},
{
type: 'separator',
},
{
label: 'Developer API Guide',
click() {
shell.openExternal('https://lbry.io/quickstart');
},
},
],
},
];
const macOSAppMenuTemplate = {
label: app.getName(),
submenu: [
{
role: 'about',
},
{
type: 'separator',
},
{
role: 'hide',
},
{
role: 'hideothers',
},
{
role: 'unhide',
},
{
type: 'separator',
},
{
role: 'quit',
},
],
};
export default () => {
const template = baseTemplate.slice();
if (process.platform === 'darwin') {
template.unshift(macOSAppMenuTemplate);
}
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
};

View file

@ -0,0 +1,89 @@
import { app, Menu, shell } from 'electron';
export default () => {
const template = [
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{
label: 'Developer',
submenu: [{ role: 'forcereload' }, { role: 'toggledevtools' }],
},
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
{
role: 'window',
submenu: [{ role: 'minimize' }, { role: 'close' }],
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: (menuItem, browserWindow) => {
if (browserWindow) {
browserWindow.webContents.send('open-menu', '/help');
} else {
shell.openExternal('https://lbry.io/faq');
}
},
},
{
label: 'Frequently Asked Questions',
click: () => {
shell.openExternal('https://lbry.io/faq');
},
},
{ type: 'separator' },
{
label: 'Report Issue',
click: () => {
shell.openExternal('https://github.com/lbryio/lbry-app/issues/new');
},
},
{ type: 'separator' },
{
label: 'Developer API Guide',
click: () => {
shell.openExternal('https://lbry.io/quickstart');
},
},
],
},
];
const darwinTemplateAddition = {
label: app.getName(),
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ type: 'separator' },
{ role: 'quit' },
],
};
if (process.platform === 'darwin') {
template.unshift(darwinTemplateAddition);
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
};

View file

@ -0,0 +1,26 @@
// @flow
import { Menu, BrowserWindow } from 'electron';
export default (rendererWindow: BrowserWindow) => {
rendererWindow.webContents.on('context-menu', (e, params) => {
const { x, y } = params;
const template = [{ role: 'cut' }, { role: 'copy' }, { role: 'paste' }];
const developmentTemplateAddition = [
{ type: 'separator' },
{
label: 'Inspect element',
click: () => {
rendererWindow.inspectElement(x, y);
},
},
];
if (process.env.NODE_ENV === 'development') {
template.push(...developmentTemplateAddition);
}
Menu.buildFromTemplate(template).popup();
});
};

View file

@ -1,27 +1,21 @@
import React from "react"; import React from 'react';
import { connect } from "react-redux"; import { connect } from 'react-redux';
import { doChangeVolume } from "redux/actions/app"; import { doChangeVolume } from 'redux/actions/app';
import { selectVolume } from "redux/selectors/app"; import { selectVolume } from 'redux/selectors/app';
import { doPlayUri, doSetPlayingUri } from "redux/actions/content"; import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
import { doPlay, doPause, savePosition } from "redux/actions/media"; import { doPlay, doPause, savePosition } from 'redux/actions/media';
import { import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
makeSelectMetadataForUri,
makeSelectContentTypeForUri,
} from "redux/selectors/claims";
import { import {
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
makeSelectLoadingForUri, makeSelectLoadingForUri,
makeSelectDownloadingForUri, makeSelectDownloadingForUri,
} from "redux/selectors/file_info"; } from 'redux/selectors/file_info';
import { makeSelectCostInfoForUri } from "redux/selectors/cost_info"; import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { selectShowNsfw } from "redux/selectors/settings"; import { selectShowNsfw } from 'redux/selectors/settings';
import { import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
selectMediaPaused, import Video from './view';
makeSelectMediaPositionForUri, import { selectPlayingUri } from 'redux/selectors/content';
} from "redux/selectors/media"; import { makeSelectClaimForUri } from 'redux/selectors/claims';
import Video from "./view";
import { selectPlayingUri } from "redux/selectors/content";
import { makeSelectClaimForUri } from "redux/selectors/claims";
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
@ -44,8 +38,7 @@ const perform = dispatch => ({
changeVolume: volume => dispatch(doChangeVolume(volume)), changeVolume: volume => dispatch(doChangeVolume(volume)),
doPlay: () => dispatch(doPlay()), doPlay: () => dispatch(doPlay()),
doPause: () => dispatch(doPause()), doPause: () => dispatch(doPause()),
savePosition: (claimId, position) => savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
dispatch(savePosition(claimId, position)),
}); });
export default connect(select, perform)(Video); export default connect(select, perform)(Video);

View file

@ -1,3 +1,4 @@
/* eslint-disable react/jsx-filename-extension */
import amplitude from 'amplitude-js'; import amplitude from 'amplitude-js';
import App from 'component/app'; import App from 'component/app';
import SnackBar from 'component/snackBar'; import SnackBar from 'component/snackBar';
@ -5,7 +6,6 @@ import SplashScreen from 'component/splash';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { ipcRenderer, remote, shell } from 'electron'; import { ipcRenderer, remote, shell } from 'electron';
import lbry from 'lbry'; import lbry from 'lbry';
/* eslint-disable react/jsx-filename-extension */
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -18,7 +18,6 @@ import store from 'store';
import app from './app'; import app from './app';
const { autoUpdater } = remote.require('electron-updater'); const { autoUpdater } = remote.require('electron-updater');
const { contextMenu } = remote.require('./main.js');
autoUpdater.logger = remote.require("electron-log"); autoUpdater.logger = remote.require("electron-log");

View file

@ -15,7 +15,7 @@ class ModalAutoUpdateConfirm extends React.PureComponent {
type="confirm" type="confirm"
contentLabel={__("Update Downloaded")} contentLabel={__("Update Downloaded")}
confirmButtonLabel={__("Upgrade")} confirmButtonLabel={__("Upgrade")}
abortButtonLabel={__("Now now")} abortButtonLabel={__("Not now")}
onConfirmed={() => { onConfirmed={() => {
ipcRenderer.send("autoUpdateAccepted"); ipcRenderer.send("autoUpdateAccepted");
}} }}

View file

@ -2,8 +2,8 @@ import React from 'react';
import ModalError from 'modal/modalError'; import ModalError from 'modal/modalError';
import ModalAuthFailure from 'modal/modalAuthFailure'; import ModalAuthFailure from 'modal/modalAuthFailure';
import ModalDownloading from 'modal/modalDownloading'; import ModalDownloading from 'modal/modalDownloading';
import ModalAutoUpdateDownloaded from "modal/modalAutoUpdateDownloaded"; import ModalAutoUpdateDownloaded from 'modal/modalAutoUpdateDownloaded';
import ModalAutoUpdateConfirm from "modal/modalAutoUpdateDownloaded"; import ModalAutoUpdateConfirm from 'modal/modalAutoUpdateConfirm';
import ModalUpgrade from 'modal/modalUpgrade'; import ModalUpgrade from 'modal/modalUpgrade';
import ModalWelcome from 'modal/modalWelcome'; import ModalWelcome from 'modal/modalWelcome';
import ModalFirstReward from 'modal/modalFirstReward'; import ModalFirstReward from 'modal/modalFirstReward';

View file

@ -100,7 +100,7 @@ export function doDownloadUpgradeRequested() {
}); });
} }
} else { // Old behavior for Linux } else { // Old behavior for Linux
doDownloadUpgrade(); dispatch(doDownloadUpgrade());
} }
}; };
} }

View file

@ -1,8 +1,8 @@
// @flow // @flow
import * as actions from "constants/action_types"; import * as actions from 'constants/action_types';
import type { Action, Dispatch } from "redux/reducers/media"; import type { Action, Dispatch } from 'redux/reducers/media';
import lbry from "lbry"; import lbry from 'lbry';
import { makeSelectClaimForUri } from "redux/selectors/claims"; import { makeSelectClaimForUri } from 'redux/selectors/claims';
export const doPlay = () => (dispatch: Dispatch) => export const doPlay = () => (dispatch: Dispatch) =>
dispatch({ dispatch({

View file

@ -90,7 +90,6 @@ reducers[ACTIONS.AUTO_UPDATE_DOWNLOADED] = state =>
}); });
reducers[ACTIONS.AUTO_UPDATE_DECLINED] = state => { reducers[ACTIONS.AUTO_UPDATE_DECLINED] = state => {
console.log('in AUTO_UPDATE_DECLINED reducer')
return Object.assign({}, state, { return Object.assign({}, state, {
autoUpdateDeclined: true, autoUpdateDeclined: true,
}); });

View file

@ -1,6 +1,6 @@
// @flow // @flow
import * as actions from "constants/action_types"; import * as actions from 'constants/action_types';
import { handleActions } from "util/redux-utils"; import { handleActions } from 'util/redux-utils';
export type MediaState = { export type MediaState = {
paused: Boolean, paused: Boolean,

View file

@ -1,14 +1,11 @@
import * as settings from "constants/settings"; import * as settings from 'constants/settings';
import { createSelector } from "reselect"; import { createSelector } from 'reselect';
import lbryuri from "lbryuri"; import lbryuri from 'lbryuri';
import { makeSelectClaimForUri } from "redux/selectors/claims"; import { makeSelectClaimForUri } from 'redux/selectors/claims';
const _selectState = state => state.media || {}; const _selectState = state => state.media || {};
export const selectMediaPaused = createSelector( export const selectMediaPaused = createSelector(_selectState, state => state.paused);
_selectState,
state => state.paused
);
export const makeSelectMediaPositionForUri = uri => export const makeSelectMediaPositionForUri = uri =>
createSelector(_selectState, makeSelectClaimForUri(uri), (state, claim) => { createSelector(_selectState, makeSelectClaimForUri(uri), (state, claim) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,7 +1,7 @@
const Path = require('path'); const path = require('path');
const FlowFlowPlugin = require('./flowtype-plugin'); const FlowFlowPlugin = require('./flowtype-plugin');
const ELECTRON_RENDERER_PROCESS_ROOT = Path.resolve(__dirname, 'src/renderer/'); const ELECTRON_RENDERER_PROCESS_ROOT = path.resolve(__dirname, 'src/renderer/');
module.exports = { module.exports = {
// This rule is temporarily necessary until https://github.com/electron-userland/electron-webpack/issues/60 is fixed. // This rule is temporarily necessary until https://github.com/electron-userland/electron-webpack/issues/60 is fixed.

136
yarn.lock
View file

@ -95,8 +95,8 @@
component-url "^0.2.1" component-url "^0.2.1"
"@types/node@^7.0.18": "@types/node@^7.0.18":
version "7.0.43" version "7.0.52"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716"
"@types/webpack-env@^1.13.2": "@types/webpack-env@^1.13.2":
version "1.13.2" version "1.13.2"
@ -182,7 +182,7 @@ ajv@^4.9.1:
co "^4.6.0" co "^4.6.0"
json-stable-stringify "^1.0.1" json-stable-stringify "^1.0.1"
ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5, ajv@^5.2.3: ajv@^5.0.0, ajv@^5.1.5, ajv@^5.2.3:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9"
dependencies: dependencies:
@ -191,6 +191,15 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5, ajv@^5.2.3:
fast-json-stable-stringify "^2.0.0" fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0" json-schema-traverse "^0.3.0"
ajv@^5.1.0, ajv@^5.5.1:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^5.3.0: ajv@^5.3.0:
version "5.5.1" version "5.5.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.1.tgz#b38bb8876d9e86bee994956a04e721e88b248eb2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.1.tgz#b38bb8876d9e86bee994956a04e721e88b248eb2"
@ -200,15 +209,6 @@ ajv@^5.3.0:
fast-json-stable-stringify "^2.0.0" fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0" json-schema-traverse "^0.3.0"
ajv@^5.5.1:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
align-text@^0.1.1, align-text@^0.1.3: align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@ -2200,7 +2200,7 @@ core-js@^2.4.0, core-js@^2.5.0:
version "2.5.1" version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
core-util-is@~1.0.0: core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -2446,19 +2446,13 @@ debug@*, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.6.6: debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.6.6:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@2.2.0: debug@^2.6.8:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
dependencies:
ms "0.7.1"
debug@^2.2.0, debug@^2.6.8:
version "2.6.8" version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies: dependencies:
@ -2911,8 +2905,8 @@ electron-webpack@^1.11.0:
yargs "^10.0.3" yargs "^10.0.3"
electron@^1.7.9: electron@^1.7.9:
version "1.7.9" version "1.7.10"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.9.tgz#add54e9f8f83ed02f6519ec10135f698b19336cf" resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.10.tgz#3a3e83d965fd7fafe473be8ddf8f472561b6253d"
dependencies: dependencies:
"@types/node" "^7.0.18" "@types/node" "^7.0.18"
electron-download "^3.0.1" electron-download "^3.0.1"
@ -3062,10 +3056,14 @@ es6-promise@^3.0.2:
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
es6-promise@^4.0.3, es6-promise@^4.0.5: es6-promise@^4.0.3:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.0.tgz#dda03ca8f9f89bc597e689842929de7ba8cebdf0" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.0.tgz#dda03ca8f9f89bc597e689842929de7ba8cebdf0"
es6-promise@^4.0.5:
version "4.2.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.2.tgz#f722d7769af88bd33bc13ec6605e1f92966b82d9"
es6-promisify@3.0.0: es6-promisify@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-3.0.0.tgz#22226b92957317f965247edfde9295f83efebe86" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-3.0.0.tgz#22226b92957317f965247edfde9295f83efebe86"
@ -3490,17 +3488,21 @@ extract-text-webpack-plugin@^3.0.2:
webpack-sources "^1.0.1" webpack-sources "^1.0.1"
extract-zip@^1.0.3: extract-zip@^1.0.3:
version "1.6.5" version "1.6.6"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.5.tgz#99a06735b6ea20ea9b705d779acffcc87cff0440" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c"
dependencies: dependencies:
concat-stream "1.6.0" concat-stream "1.6.0"
debug "2.2.0" debug "2.6.9"
mkdirp "0.5.0" mkdirp "0.5.0"
yauzl "2.4.1" yauzl "2.4.1"
extsprintf@1.0.2: extsprintf@1.3.0:
version "1.0.2" version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
eyes@0.1.8: eyes@0.1.8:
version "0.1.8" version "0.1.8"
@ -5049,13 +5051,13 @@ jsonpointer@^4.0.0:
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
jsprim@^1.2.2: jsprim@^1.2.2:
version "1.4.0" version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies: dependencies:
assert-plus "1.0.0" assert-plus "1.0.0"
extsprintf "1.0.2" extsprintf "1.3.0"
json-schema "0.2.3" json-schema "0.2.3"
verror "1.3.6" verror "1.10.0"
jstransform@^11.0.3: jstransform@^11.0.3:
version "11.0.3" version "11.0.3"
@ -5085,11 +5087,12 @@ keypress@0.1.x:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.1.0.tgz#4a3188d4291b66b4f65edb99f806aa9ae293592a" resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.1.0.tgz#4a3188d4291b66b4f65edb99f806aa9ae293592a"
keytar@^4.0.3: keytar-prebuild@^4.0.4:
version "4.1.0" version "4.0.4"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.1.0.tgz#9e3933e489d656de1a868e1293709313044989d7" resolved "https://registry.yarnpkg.com/keytar-prebuild/-/keytar-prebuild-4.0.4.tgz#eb6354c68f2b3609dc325ef8709844632652d602"
dependencies: dependencies:
nan "2.5.1" nan "2.7.0"
prebuild-install "^2.2.2"
killable@^1.0.0: killable@^1.0.0:
version "1.0.0" version "1.0.0"
@ -5792,10 +5795,6 @@ mp4-stream@^2.0.0:
next-event "^1.0.0" next-event "^1.0.0"
readable-stream "^2.0.3" readable-stream "^2.0.3"
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
ms@2.0.0, ms@^2.0.0: ms@2.0.0, ms@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -5822,7 +5821,11 @@ mute-stream@0.0.7, mute-stream@~0.0.4:
version "0.0.7" version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
nan@2.5.1, nan@^2.3.0, nan@^2.3.2: nan@2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
nan@^2.3.0, nan@^2.3.2:
version "2.5.1" version "2.5.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"
@ -6937,6 +6940,25 @@ postcss@^6.0.1:
source-map "^0.6.1" source-map "^0.6.1"
supports-color "^4.4.0" supports-color "^4.4.0"
prebuild-install@^2.2.2:
version "2.4.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.4.1.tgz#c28ba1d1eedc17fbd6b3229a657ffc0fba479b49"
dependencies:
expand-template "^1.0.2"
github-from-package "0.0.0"
minimist "^1.2.0"
mkdirp "^0.5.1"
node-abi "^2.1.1"
noop-logger "^0.1.1"
npmlog "^4.0.1"
os-homedir "^1.0.1"
pump "^1.0.1"
rc "^1.1.6"
simple-get "^1.4.2"
tar-fs "^1.13.0"
tunnel-agent "^0.6.0"
xtend "4.0.1"
prebuild-install@^2.3.0: prebuild-install@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.3.0.tgz#19481247df728b854ab57b187ce234211311b485" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.3.0.tgz#19481247df728b854ab57b187ce234211311b485"
@ -7243,7 +7265,7 @@ rc-progress@^2.0.6:
babel-runtime "6.x" babel-runtime "6.x"
prop-types "^15.5.8" prop-types "^15.5.8"
rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1: rc@^1.0.1, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
dependencies: dependencies:
@ -7252,6 +7274,15 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
rc@^1.1.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-addons-create-fragment@^15.0.0: react-addons-create-fragment@^15.0.0:
version "15.6.2" version "15.6.2"
resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.2.tgz#a394de7c2c7becd6b5475ba1b97ac472ce7c74f8" resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.2.tgz#a394de7c2c7becd6b5475ba1b97ac472ce7c74f8"
@ -8984,11 +9015,13 @@ vendors@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
verror@1.3.6: verror@1.10.0:
version "1.3.6" version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies: dependencies:
extsprintf "1.0.2" assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
videostream@^2.3.0: videostream@^2.3.0:
version "2.4.2" version "2.4.2"
@ -9415,6 +9448,13 @@ yargs@~3.10.0:
decamelize "^1.0.0" decamelize "^1.0.0"
window-size "0.1.0" window-size "0.1.0"
yarnhook@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/yarnhook/-/yarnhook-0.1.1.tgz#6e67757327e6390cb313f371bdc44ded7c5e047b"
dependencies:
execa "^0.8.0"
find-parent-dir "^0.3.0"
yauzl@2.4.1: yauzl@2.4.1:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"