diff --git a/.gitignore b/.gitignore
index 023767f02..91e5403cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,30 +1,9 @@
/node_modules
-/LBRY-darwin-x64
/dist
-
-/src/main/dist
-/src/main/locales
-/src/main/node_modules
-/src/renderer/dist
-/build/venv
/build/daemon.ver
-/lbry-app-venv
-/lbry-app
-/lbry-venv
+/build/venv
+*.pyc
/static/daemon/lbrynet*
/static/locales
-/daemon/build
-/daemon/venv
-/daemon/requirements.txt
-/.idea
-*.pyc
-*.iml
-.#*
-build/daemon.zip
-.vimrc
-
-package-lock.json
-
-.DS_Store
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 578abc5f7..c1a3914e7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,11 +38,11 @@ To make contributing as easy and rewarding of possible, we have instituted the f
| 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 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 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 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 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 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%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%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%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%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
give contributors a general idea about the type of issues they are looking at. It could very well be
diff --git a/README.md b/README.md
index be41950c6..7eabde706 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,6 @@ development and testing purposes.
* [Git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/)
* [Yarn](https://yarnpkg.com/en/docs/install)
-* `yarn --add-python-to-path install --global --production windows-build-tools` (Windows only)
### One-time Setup
@@ -61,40 +60,10 @@ The app can be run from the sources using the following command:
### On Windows
-#### Windows Dependency
-
-1. Download and install `git` from github.io
- (configure to use command prompt integration)
-2. Download and install `npm` and `node` from
- nodejs.org
-3. Download and install `python 2.7` from
- python.org
-4. Download and Install `Microsoft Visual C++ Compiler for Python 2.7` from
- Microsoft
-5. Download and install `.NET Framework 2.0 Software Development Kit (SDK) (x64)` from
- Microsoft (may need
- to extract setup.exe and install manually by running install.exe as Administrator)
-
#### One-time Setup
-1. Open a command prompt as administrator and run the following:
-
-```
-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`.
+Download the lbry daemon and CLI [binaries](https://github.com/lbryio/lbry/releases) and place them
+in `static\daemon`.
### Build
diff --git a/build/build.sh b/build/build.sh
index 2619fb5b0..1ace2ad48 100755
--- a/build/build.sh
+++ b/build/build.sh
@@ -73,9 +73,9 @@ fi
# Build the app #
###################
if [ "$FULL_BUILD" == "true" ]; then
- # if $OSX; then
- # security unlock-keychain -p ${KEYCHAIN_PASSWORD} osx-build.keychain
- # fi
+ if $OSX; then
+ security unlock-keychain -p ${KEYCHAIN_PASSWORD} osx-build.keychain
+ fi
yarn build
diff --git a/build/install_deps.sh b/build/install_deps.sh
index 29b7dd723..c31b010f0 100755
--- a/build/install_deps.sh
+++ b/build/install_deps.sh
@@ -40,8 +40,12 @@ set -eu
if $LINUX; then
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
-elif $OSX && ! cmd_exists brew ; then
- /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+elif $OSX; then
+ if ! cmd_exists brew; then
+ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+ else
+ brew update
+ fi
fi
diff --git a/package.json b/package.json
index 8469c3907..0cad777b0 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,9 @@
"compile": "electron-webpack && yarn extract-langs",
"build": "yarn compile && electron-builder build",
"postinstall": "electron-builder install-app-deps",
+ "postmerge": "yarnhook",
+ "postcheckout": "yarnhook",
+ "postrewrite": "yarnhook",
"precommit": "lint-staged",
"lint": "eslint 'src/**/*.{js,jsx}' --fix",
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
@@ -41,7 +44,7 @@
"install": "^0.10.2",
"jayson": "^2.0.2",
"jshashes": "^1.0.7",
- "keytar": "^4.0.3",
+ "keytar-prebuild": "^4.0.4",
"localforage": "^1.5.0",
"npm": "^5.5.1",
"qrcode.react": "^0.7.2",
@@ -102,7 +105,8 @@
"prettier": "^1.4.2",
"sass-loader": "^6.0.6",
"webpack": "^3.10.0",
- "webpack-build-notifier": "^0.1.18"
+ "webpack-build-notifier": "^0.1.18",
+ "yarnhook": "^0.1.1"
},
"resolutions": {
"webpack/webpack-sources": "1.0.1"
diff --git a/src/main/Daemon.js b/src/main/Daemon.js
new file mode 100644
index 000000000..b97d4714f
--- /dev/null
+++ b/src/main/Daemon.js
@@ -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);
+ });
+ }
+}
diff --git a/src/main/Tray.js b/src/main/Tray.js
new file mode 100644
index 000000000..6937dda42
--- /dev/null
+++ b/src/main/Tray.js
@@ -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);
+ }
+}
diff --git a/src/main/createWindow.js b/src/main/createWindow.js
new file mode 100644
index 000000000..6809761f3
--- /dev/null
+++ b/src/main/createWindow.js
@@ -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;
+};
diff --git a/src/main/index.js b/src/main/index.js
index b3330c129..22de0ce76 100644
--- a/src/main/index.js
+++ b/src/main/index.js
@@ -1,54 +1,20 @@
/* eslint-disable no-console */
// Module imports
-import Path from 'path';
-import Url from 'url';
-import Jayson from 'jayson';
-import Semver from 'semver';
-import Https from 'https';
-import Keytar from 'keytar';
-import ChildProcess from 'child_process';
-import Assert from 'assert';
-import { app, dialog, BrowserWindow, globalShortcut, ipcMain, Menu, Tray } from 'electron';
+import keytar from 'keytar-prebuild';
+import SemVer from 'semver';
+import url from 'url';
+import https from 'https';
+import { shell, app, ipcMain, dialog } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
-import mainMenu from './menu/mainMenu';
-import contextMenu from './menu/contextMenu';
+import Daemon from './Daemon';
+import Tray from './Tray';
+import createWindow from './createWindow';
-const localVersion = app.getVersion();
-
-// 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';
+// For now, log info messages in production for easier debugging
+log.transports.file.level = '';
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
// auto-update system and is ready to install. If the user declined an update earlier,
// 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.
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
-// this is set to true and app.quit() is called again to quit for real.
-let readyToQuit = false;
+let isQuitting;
-// If we receive a URI to open from an external app but there's no window to
-// sendCredits it to, it's cached in this variable.
-let openUri = null;
+const updateRendererWindow = window => {
+ rendererWindow = window;
+};
-// Set this to true to minimize on clicking close
-// false for normal action
-let minimize = true;
+const installExtensions = async () => {
+ // eslint-disable-next-line import/no-extraneous-dependencies,global-require
+ 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()
-let tray = null;
-
-function processRequestedUri(uri) {
- // Windows normalizes URIs when they're passed in from other apps. On Windows,
- // this function 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.
- // 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);
- });
-}
+ return Promise.all(
+ extensions.map(
+ name => installer.default(installer[name], forceDownload),
+ devtronExtension.install()
+ )
+ ).catch(console.log);
+};
app.setAsDefaultProtocolClient('lbry');
+app.setName('LBRY');
-app.on('ready', () => {
- launchDaemonIfNotRunning();
- if (process.platform === 'linux') {
- checkLinuxTraySupport(err => {
- if (!err) createTray();
- else minimize = false;
- });
- } else {
- createTray();
+app.on('ready', async () => {
+ daemon = new Daemon();
+ daemon.on('exit', () => {
+ daemon = null;
+ if (!isQuitting) {
+ dialog.showErrorBox(
+ 'Daemon has Exited',
+ 'The daemon may have encountered an unexpected error, or another daemon instance is already running.'
+ );
+ app.quit();
+ }
+ });
+ daemon.launch();
+ if (process.env.NODE_ENV === 'development') {
+ await installExtensions();
}
rendererWindow = createWindow();
-});
-
-// 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();
- });
- }
- }
+ tray = new Tray(rendererWindow, updateRendererWindow);
+ tray.create();
});
app.on('activate', () => {
// 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.
- if (rendererWindow === null) {
- createWindow();
- }
+ if (!rendererWindow) rendererWindow = createWindow();
});
-if (process.platform === 'darwin') {
- app.on('open-url', (event, uri) => {
- handleOpenUriRequested(uri);
+app.on('will-quit', (e) => {
+ if (process.platform === 'win32' && autoUpdateDownloaded && !autoUpdateAccepted && !showingAutoUpdateCloseAlert) {
+ // 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) => {
app.on('quit', () => {
console.log('Launching upgrade installer at', installerPath);
// 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.
- 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?
console.log('Update downloaded to', installerPath);
console.log(
'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.');
+ app.quit();
+});
+
+autoUpdater.on('update-downloaded', () => {
+ autoUpdateDownloaded = true;
+})
+
+ipcMain.on('autoUpdateAccepted', () => {
+ autoUpdateAccepted = true;
+ autoUpdater.quitAndInstall();
});
ipcMain.on('version-info-requested', () => {
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');
}
- let result = '';
+ const localVersion = app.getVersion();
+ const latestReleaseAPIURL = 'https://api.github.com/repos/lbryio/lbry-app/releases/latest';
const opts = {
headers: {
'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 => {
result += data;
});
@@ -536,7 +181,7 @@ ipcMain.on('version-info-requested', () => {
rendererWindow.webContents.send('version-info-received', null);
}
} else {
- const upgradeAvailable = Semver.gt(formatRc(remoteVersion), formatRc(localVersion));
+ const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
if (rendererWindow) {
rendererWindow.webContents.send('version-info-received', {
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 => {
- 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);
});
});
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 instance’s (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();
+}
\ No newline at end of file
diff --git a/src/main/menu/contextMenu.js b/src/main/menu/contextMenu.js
deleted file mode 100644
index e24885ec2..000000000
--- a/src/main/menu/contextMenu.js
+++ /dev/null
@@ -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);
-};
diff --git a/src/main/menu/mainMenu.js b/src/main/menu/mainMenu.js
deleted file mode 100644
index 879ce8206..000000000
--- a/src/main/menu/mainMenu.js
+++ /dev/null
@@ -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));
-};
diff --git a/src/main/menu/setupBarMenu.js b/src/main/menu/setupBarMenu.js
new file mode 100644
index 000000000..14c4e74db
--- /dev/null
+++ b/src/main/menu/setupBarMenu.js
@@ -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);
+};
diff --git a/src/main/menu/setupContextMenu.js b/src/main/menu/setupContextMenu.js
new file mode 100644
index 000000000..01f540d81
--- /dev/null
+++ b/src/main/menu/setupContextMenu.js
@@ -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();
+ });
+};
diff --git a/src/renderer/component/video/index.js b/src/renderer/component/video/index.js
index 8c0a91289..573dba1a9 100644
--- a/src/renderer/component/video/index.js
+++ b/src/renderer/component/video/index.js
@@ -1,27 +1,21 @@
-import React from "react";
-import { connect } from "react-redux";
-import { doChangeVolume } from "redux/actions/app";
-import { selectVolume } from "redux/selectors/app";
-import { doPlayUri, doSetPlayingUri } from "redux/actions/content";
-import { doPlay, doPause, savePosition } from "redux/actions/media";
-import {
- makeSelectMetadataForUri,
- makeSelectContentTypeForUri,
-} from "redux/selectors/claims";
+import React from 'react';
+import { connect } from 'react-redux';
+import { doChangeVolume } from 'redux/actions/app';
+import { selectVolume } from 'redux/selectors/app';
+import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
+import { doPlay, doPause, savePosition } from 'redux/actions/media';
+import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
import {
makeSelectFileInfoForUri,
makeSelectLoadingForUri,
makeSelectDownloadingForUri,
-} from "redux/selectors/file_info";
-import { makeSelectCostInfoForUri } from "redux/selectors/cost_info";
-import { selectShowNsfw } from "redux/selectors/settings";
-import {
- selectMediaPaused,
- makeSelectMediaPositionForUri,
-} from "redux/selectors/media";
-import Video from "./view";
-import { selectPlayingUri } from "redux/selectors/content";
-import { makeSelectClaimForUri } from "redux/selectors/claims";
+} from 'redux/selectors/file_info';
+import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
+import { selectShowNsfw } from 'redux/selectors/settings';
+import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
+import Video from './view';
+import { selectPlayingUri } from 'redux/selectors/content';
+import { makeSelectClaimForUri } from 'redux/selectors/claims';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
@@ -44,8 +38,7 @@ const perform = dispatch => ({
changeVolume: volume => dispatch(doChangeVolume(volume)),
doPlay: () => dispatch(doPlay()),
doPause: () => dispatch(doPause()),
- savePosition: (claimId, position) =>
- dispatch(savePosition(claimId, position)),
+ savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
});
export default connect(select, perform)(Video);
diff --git a/src/renderer/index.js b/src/renderer/index.js
index cafd41c35..5ee4a84bf 100644
--- a/src/renderer/index.js
+++ b/src/renderer/index.js
@@ -1,3 +1,4 @@
+/* eslint-disable react/jsx-filename-extension */
import amplitude from 'amplitude-js';
import App from 'component/app';
import SnackBar from 'component/snackBar';
@@ -5,7 +6,6 @@ import SplashScreen from 'component/splash';
import * as ACTIONS from 'constants/action_types';
import { ipcRenderer, remote, shell } from 'electron';
import lbry from 'lbry';
-/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
@@ -18,7 +18,6 @@ import store from 'store';
import app from './app';
const { autoUpdater } = remote.require('electron-updater');
-const { contextMenu } = remote.require('./main.js');
autoUpdater.logger = remote.require("electron-log");
diff --git a/src/renderer/modal/modalAutoUpdateConfirm/view.jsx b/src/renderer/modal/modalAutoUpdateConfirm/view.jsx
index 88218c4da..ba9a32d4a 100644
--- a/src/renderer/modal/modalAutoUpdateConfirm/view.jsx
+++ b/src/renderer/modal/modalAutoUpdateConfirm/view.jsx
@@ -15,7 +15,7 @@ class ModalAutoUpdateConfirm extends React.PureComponent {
type="confirm"
contentLabel={__("Update Downloaded")}
confirmButtonLabel={__("Upgrade")}
- abortButtonLabel={__("Now now")}
+ abortButtonLabel={__("Not now")}
onConfirmed={() => {
ipcRenderer.send("autoUpdateAccepted");
}}
diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx
index 924444687..10ff018ed 100644
--- a/src/renderer/modal/modalRouter/view.jsx
+++ b/src/renderer/modal/modalRouter/view.jsx
@@ -2,8 +2,8 @@ import React from 'react';
import ModalError from 'modal/modalError';
import ModalAuthFailure from 'modal/modalAuthFailure';
import ModalDownloading from 'modal/modalDownloading';
-import ModalAutoUpdateDownloaded from "modal/modalAutoUpdateDownloaded";
-import ModalAutoUpdateConfirm from "modal/modalAutoUpdateDownloaded";
+import ModalAutoUpdateDownloaded from 'modal/modalAutoUpdateDownloaded';
+import ModalAutoUpdateConfirm from 'modal/modalAutoUpdateConfirm';
import ModalUpgrade from 'modal/modalUpgrade';
import ModalWelcome from 'modal/modalWelcome';
import ModalFirstReward from 'modal/modalFirstReward';
diff --git a/src/renderer/redux/actions/app.js b/src/renderer/redux/actions/app.js
index 955516749..ef79a0f6a 100644
--- a/src/renderer/redux/actions/app.js
+++ b/src/renderer/redux/actions/app.js
@@ -100,7 +100,7 @@ export function doDownloadUpgradeRequested() {
});
}
} else { // Old behavior for Linux
- doDownloadUpgrade();
+ dispatch(doDownloadUpgrade());
}
};
}
diff --git a/src/renderer/redux/actions/media.js b/src/renderer/redux/actions/media.js
index d3076ed75..f554d054d 100644
--- a/src/renderer/redux/actions/media.js
+++ b/src/renderer/redux/actions/media.js
@@ -1,8 +1,8 @@
// @flow
-import * as actions from "constants/action_types";
-import type { Action, Dispatch } from "redux/reducers/media";
-import lbry from "lbry";
-import { makeSelectClaimForUri } from "redux/selectors/claims";
+import * as actions from 'constants/action_types';
+import type { Action, Dispatch } from 'redux/reducers/media';
+import lbry from 'lbry';
+import { makeSelectClaimForUri } from 'redux/selectors/claims';
export const doPlay = () => (dispatch: Dispatch) =>
dispatch({
diff --git a/src/renderer/redux/reducers/app.js b/src/renderer/redux/reducers/app.js
index f1899a021..0b9ccee07 100644
--- a/src/renderer/redux/reducers/app.js
+++ b/src/renderer/redux/reducers/app.js
@@ -90,7 +90,6 @@ reducers[ACTIONS.AUTO_UPDATE_DOWNLOADED] = state =>
});
reducers[ACTIONS.AUTO_UPDATE_DECLINED] = state => {
- console.log('in AUTO_UPDATE_DECLINED reducer')
return Object.assign({}, state, {
autoUpdateDeclined: true,
});
diff --git a/src/renderer/redux/reducers/media.js b/src/renderer/redux/reducers/media.js
index 7e3bb9591..44447fe4f 100644
--- a/src/renderer/redux/reducers/media.js
+++ b/src/renderer/redux/reducers/media.js
@@ -1,6 +1,6 @@
// @flow
-import * as actions from "constants/action_types";
-import { handleActions } from "util/redux-utils";
+import * as actions from 'constants/action_types';
+import { handleActions } from 'util/redux-utils';
export type MediaState = {
paused: Boolean,
diff --git a/src/renderer/redux/selectors/media.js b/src/renderer/redux/selectors/media.js
index 9ef16abaf..e6cc54a71 100644
--- a/src/renderer/redux/selectors/media.js
+++ b/src/renderer/redux/selectors/media.js
@@ -1,14 +1,11 @@
-import * as settings from "constants/settings";
-import { createSelector } from "reselect";
-import lbryuri from "lbryuri";
-import { makeSelectClaimForUri } from "redux/selectors/claims";
+import * as settings from 'constants/settings';
+import { createSelector } from 'reselect';
+import lbryuri from 'lbryuri';
+import { makeSelectClaimForUri } from 'redux/selectors/claims';
const _selectState = state => state.media || {};
-export const selectMediaPaused = createSelector(
- _selectState,
- state => state.paused
-);
+export const selectMediaPaused = createSelector(_selectState, state => state.paused);
export const makeSelectMediaPositionForUri = uri =>
createSelector(_selectState, makeSelectClaimForUri(uri), (state, claim) => {
diff --git a/static/img/tray/default/tray.png b/static/img/tray/default/tray.png
new file mode 100644
index 000000000..5fe694194
Binary files /dev/null and b/static/img/tray/default/tray.png differ
diff --git a/static/img/tray/mac/trayTemplate.png b/static/img/tray/mac/trayTemplate.png
new file mode 100644
index 000000000..f1e30e1ca
Binary files /dev/null and b/static/img/tray/mac/trayTemplate.png differ
diff --git a/static/img/tray/mac/trayTemplate@2x.png b/static/img/tray/mac/trayTemplate@2x.png
new file mode 100644
index 000000000..d709f995c
Binary files /dev/null and b/static/img/tray/mac/trayTemplate@2x.png differ
diff --git a/static/img/tray/windows/tray.ico b/static/img/tray/windows/tray.ico
new file mode 100644
index 000000000..1e1ac47df
Binary files /dev/null and b/static/img/tray/windows/tray.ico differ
diff --git a/static/img/untitled folder/trayTemplate.png b/static/img/untitled folder/trayTemplate.png
new file mode 100644
index 000000000..57b671483
Binary files /dev/null and b/static/img/untitled folder/trayTemplate.png differ
diff --git a/static/img/untitled folder/trayTemplate@2x.png b/static/img/untitled folder/trayTemplate@2x.png
new file mode 100644
index 000000000..8f10cf35b
Binary files /dev/null and b/static/img/untitled folder/trayTemplate@2x.png differ
diff --git a/webpack.renderer.additions.js b/webpack.renderer.additions.js
index b18039ad1..aebfe1576 100644
--- a/webpack.renderer.additions.js
+++ b/webpack.renderer.additions.js
@@ -1,7 +1,7 @@
-const Path = require('path');
+const path = require('path');
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 = {
// This rule is temporarily necessary until https://github.com/electron-userland/electron-webpack/issues/60 is fixed.
diff --git a/yarn.lock b/yarn.lock
index 35e76177e..0c540f5bc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -95,8 +95,8 @@
component-url "^0.2.1"
"@types/node@^7.0.18":
- version "7.0.43"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
+ version "7.0.52"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.52.tgz#8990d3350375542b0c21a83cd0331e6a8fc86716"
"@types/webpack-env@^1.13.2":
version "1.13.2"
@@ -182,7 +182,7 @@ ajv@^4.9.1:
co "^4.6.0"
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"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9"
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"
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:
version "5.5.1"
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"
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:
version "0.1.4"
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"
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"
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:
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"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
-debug@2.2.0:
- 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:
+debug@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
@@ -2911,8 +2905,8 @@ electron-webpack@^1.11.0:
yargs "^10.0.3"
electron@^1.7.9:
- version "1.7.9"
- resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.9.tgz#add54e9f8f83ed02f6519ec10135f698b19336cf"
+ version "1.7.10"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.10.tgz#3a3e83d965fd7fafe473be8ddf8f472561b6253d"
dependencies:
"@types/node" "^7.0.18"
electron-download "^3.0.1"
@@ -3062,10 +3056,14 @@ es6-promise@^3.0.2:
version "3.3.1"
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"
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:
version "3.0.0"
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"
extract-zip@^1.0.3:
- version "1.6.5"
- resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.5.tgz#99a06735b6ea20ea9b705d779acffcc87cff0440"
+ version "1.6.6"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c"
dependencies:
concat-stream "1.6.0"
- debug "2.2.0"
+ debug "2.6.9"
mkdirp "0.5.0"
yauzl "2.4.1"
-extsprintf@1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+extsprintf@1.3.0:
+ version "1.3.0"
+ 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:
version "0.1.8"
@@ -5049,13 +5051,13 @@ jsonpointer@^4.0.0:
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
jsprim@^1.2.2:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
- extsprintf "1.0.2"
+ extsprintf "1.3.0"
json-schema "0.2.3"
- verror "1.3.6"
+ verror "1.10.0"
jstransform@^11.0.3:
version "11.0.3"
@@ -5085,11 +5087,12 @@ keypress@0.1.x:
version "0.1.0"
resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.1.0.tgz#4a3188d4291b66b4f65edb99f806aa9ae293592a"
-keytar@^4.0.3:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.1.0.tgz#9e3933e489d656de1a868e1293709313044989d7"
+keytar-prebuild@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/keytar-prebuild/-/keytar-prebuild-4.0.4.tgz#eb6354c68f2b3609dc325ef8709844632652d602"
dependencies:
- nan "2.5.1"
+ nan "2.7.0"
+ prebuild-install "^2.2.2"
killable@^1.0.0:
version "1.0.0"
@@ -5792,10 +5795,6 @@ mp4-stream@^2.0.0:
next-event "^1.0.0"
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:
version "2.0.0"
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"
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"
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"
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:
version "2.3.0"
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"
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"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
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"
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:
version "15.6.2"
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"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
-verror@1.3.6:
- version "1.3.6"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+verror@1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
- extsprintf "1.0.2"
+ assert-plus "^1.0.0"
+ core-util-is "1.0.2"
+ extsprintf "^1.2.0"
videostream@^2.3.0:
version "2.4.2"
@@ -9415,6 +9448,13 @@ yargs@~3.10.0:
decamelize "^1.0.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:
version "2.4.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"